Files
psxrecomp/runtime/tests/test_mod_load_acceleration.py
T
Matthew Stanley f0cfa92afa config: retire the legacy generic turbo_loads switch — load acceleration is mod-only
Load acceleration moved to the Mods catalog in 125d84d
(psx.enhancement.fast-loading "Fast Loading (host pacing)" and
psx.enhancement.cd-speed, both `game_id = "*"` so they ship with every
title, both default-off), and recomp-ui deliberately stopped drawing a
generic Turbo loads row. But the legacy switch was left live here with
`offer_turbo_loads` defaulting TRUE, so any title that had not explicitly
migrated still got turbo forced on — with no UI to turn it back off.

That shipped to players. MegaManX6Recomp v1.0.4 and v1.0.5 both carry
`[runtime] turbo_loads = true` in the game.toml inside the release zip
(verified by extracting the artifacts). Two paths forced it on:

  * game.toml [runtime] turbo_loads set g_turbo_loads_enabled directly;
  * a stale [video] turbo_loads in settings.toml was restored, and the
    seed write persisted it — so one run latched the value permanently
    and no later game.toml change could undo it.

Reported as MegaManX6Recomp#14: the WARNING screen self-advances and load
spinners run fast, with Turbo confirmed off in Mods. Tomba 2 hit the same
defect (fixed per-title), and MMX5 is also unmigrated. Third occurrence of
one config default, so fix the class instead of the titles.

Both keys are still PARSED, so existing configs load without error, but
neither is honoured:

  * offer_turbo_loads defaults false and turbo_loads_offered becomes a
    constexpr false, matching how ws_offered / skip_fmv_offered /
    frame_interpolation_offered already model mod-owned features. No title
    can offer the generic switch, so forgetting to migrate is no longer
    punished.
  * game.toml turbo_loads logs a deprecation line naming the Fast Loading
    mod and enables nothing.
  * settings.toml [video] turbo_loads is neither restored nor written back
    out, so the stale row is dropped on the first save — same write-only
    latch reasoning already documented for fast_boot, which bit harder here
    because there was no UI control at all.

Also fixes a related hole found while auditing: the in-game Settings apply
path wrote both g_turbo_loads_enabled and g_auto_skip_fmv from a launcher
snapshot taken BEFORE mod_runtime_activate_plugins() ran, so the first
in-game Apply silently turned a player's enabled Fast Loading or Tweaks FMV
feature back off. Both are now gated on their offered flags.

The supported surfaces are unchanged: the Fast Loading / CD Speed mod
plugins still enable acceleration (verified: "mod selected 8x load
acceleration"), and the `turbo_loads` TCP debug command still toggles it
for development.

Verified against MMX6 built on this framework: with game.toml
`turbo_loads = true` AND `offer_turbo_loads = true` AND a stale
settings.toml `turbo_loads = true`, both deprecation lines fire and the
debug server reports enabled=0, turbo_frames=0. With the keys removed and
no mods enabled, it boots silently with enabled=0.

test_mod_load_acceleration.py is updated to pin the new contract; it also
had a stale assertion on the old 2..16 multiplier range, which is now
pinned to the symbolic PSX_MOD_LOAD_ACCEL_MAX bound. The guard-test suite
goes from 9 pre-existing failures to 8 (no new failures; this one fixed).

Refs: beads-eio.3.26, MegaManX6Recomp#14

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 18:52:16 -07:00

79 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""Guard mod-owned, bounded load acceleration and its launcher migration."""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
MAIN = (ROOT / "runtime/src/main.cpp").read_text(encoding="utf-8")
HEADER = (ROOT / "runtime/include/mod_plugins.h").read_text(encoding="utf-8")
CONFIG_H = (ROOT / "recompiler/src/config_loader.h").read_text(encoding="utf-8")
CONFIG_CPP = (ROOT / "recompiler/src/config_loader.cpp").read_text(
encoding="utf-8"
)
assert "psx_mod_set_load_acceleration" in HEADER
# The accepted range was widened from 2..16 to 1..PSX_MOD_LOAD_ACCEL_MAX (the
# bound is there to reject nonsense, not to curate "blessed" speeds), so pin the
# contract by its symbolic bound rather than the old literals.
assert "wall_clock_multiplier accepts 1..PSX_MOD_LOAD_ACCEL_MAX" in HEADER
assert "#define PSX_MOD_LOAD_ACCEL_MAX" in HEADER
assert "zero is the precise/speedrun-safe policy" in HEADER
assert "psx_mod_set_disc_speed" in HEADER
assert "this changes" in HEADER
# The legacy generic Turbo loads switch is retired: acceleration is mod-only.
# Both config keys are still PARSED (so old game.toml/settings.toml load), but
# neither is honoured, and no title can offer the generic switch any more.
assert "bool offer_turbo_loads = false;" in CONFIG_H
assert "DEPRECATED AND IGNORED" in CONFIG_H
assert 'runtime.contains("offer_turbo_loads")' in CONFIG_CPP
assert "rt.has_turbo_loads = true;" in CONFIG_CPP
assert "constexpr bool turbo_loads_offered = false;" in MAIN
assert "turbo_loads_offered = gc.runtime.offer_turbo_loads;" not in MAIN
assert "gi.has_turbo_loads = turbo_loads_offered ? 1 : 0;" in MAIN
assert "Turbo loads is mod-owned on PSX" in MAIN
# game.toml [runtime] turbo_loads must not enable anything, only warn.
assert "g_turbo_loads_enabled = 1;\n std::fprintf" not in MAIN
assert '"psxrecomp: game.toml [runtime] %s%s%s is DEPRECATED and "' in MAIN
assert '"Fast Loading (host pacing)\\" mod' in MAIN
# A stale settings.toml value is neither restored nor written back out, so it
# cannot latch: the launcher draws no row for it, which made a persisted value
# both authoritative and unreachable (MegaManX6Recomp#14).
assert "g_turbo_loads_enabled = us.turbo_loads" not in MAIN
assert "settings.toml [video] turbo_loads = true is " in MAIN
assert 'f << "turbo_loads = "' not in CONFIG_CPP
# The in-game Settings apply path reads a launcher snapshot taken BEFORE mod
# activation, so it must not write either mod-owned global.
assert "if (turbo_loads_offered) g_turbo_loads_enabled = ls.turbo_loads ? 1 : 0;" in MAIN
assert "if (skip_fmv_offered) g_auto_skip_fmv = ls.auto_skip_fmv ? 1 : 0;" in MAIN
reset = """g_mod_load_wall_multiplier = -1;
g_mod_load_release_frames = -1;"""
disc_reset = """g_mod_disc_speed_divisor = -1;
g_mod_disc_instant_rate = -1;"""
disable_mod_owned_baseline = """if (!turbo_loads_offered)
g_turbo_loads_enabled = 0;"""
activate = "mod_runtime_activate_plugins();"
apply = "if (g_mod_load_wall_multiplier >= 0) {"
assert reset in MAIN
assert disc_reset in MAIN
assert disable_mod_owned_baseline in MAIN
assert apply in MAIN
assert (
MAIN.index(reset)
< MAIN.index(disable_mod_owned_baseline)
< MAIN.index(activate)
< MAIN.index(apply)
)
assert "release_run = g_turbo_load_release_frames;" in MAIN
assert "g_frame_period_ms / (double)g_turbo_load_wall_multiplier" in MAIN
assert "if (!turbo_load_paced && g_frame_period_ms > 0.0)" in MAIN
assert "if (g_mod_disc_speed_divisor >= 0)" in MAIN
print("mod-owned load acceleration guard passed")