diff --git a/runtime/src/cdrom.c b/runtime/src/cdrom.c index 68a40c30..59529a42 100644 --- a/runtime/src/cdrom.c +++ b/runtime/src/cdrom.c @@ -578,11 +578,12 @@ static int apply_speed(int delay) { static int apply_read_speed(int delay) { /* A route is an explicit DATA-read allowlist, never a blanket drive-speed - * change. Keep FMV/STR authentic: XA-ADPCM (0x40), filter (0x08), and - * Form2/0x924 (0x20) — MotK sets mode 0xa2 (Form2+2×) before the XA bit, - * and disc_speed=4x was compressing that preamble into a one-sector race - * (soak §93 P1 sim 184: rd≈112896 vs delivered). */ - if (xa_stream_active || (mode_reg & 0x68u)) return delay; + * change. Keep FMV/STR authentic once the drive is explicitly in XA/filter + * mode (0x40/0x08) or an XA stream is already active. Do not treat the + * whole-sector/Form2 bit (0x20) alone as realtime media: Tomba's normal + * asset loads use mode 0xA0, so including 0x20 here made the CD Speed mod + * configure divisor=32 while every actual data-read deadline stayed 1x/2x. */ + if (xa_stream_active || (mode_reg & 0x48u)) return delay; if (s_warm_route_active) return warm_route_period(); return apply_speed(delay); } diff --git a/runtime/src/fntrace.c b/runtime/src/fntrace.c index ee92645a..b767ee6e 100644 --- a/runtime/src/fntrace.c +++ b/runtime/src/fntrace.c @@ -71,7 +71,19 @@ void fntrace_mark_game_started(CPUState* cpu) { void fntrace_maybe_mark_game_started(CPUState* cpu, uint32_t addr) { if (s_game_started) return; if (s_game_entry_phys == 0) return; /* no game range armed */ - if ((addr & 0x1FFFFFFFu) == s_game_entry_phys) + /* The dirty-RAM interpreter path may miss the exact PS-X EXE entry when + * the BIOS or loader enters game text through already-interpreted flow. + * Mirror fntrace_record's safe fallback: only latch on other game-text + * addresses when a reference image exists and live RAM already matches it. + * Without that guard, relocated BIOS shell RAM can look like game RAM and + * a premature handoff corrupts boot. */ + extern int psx_game_address_in_text(uint32_t addr); + extern int psx_game_text_native_ok(uint32_t addr); + extern int dirty_ram_text_image_registered(void); + uint32_t phys = addr & 0x1FFFFFFFu; + if (phys == s_game_entry_phys || + (dirty_ram_text_image_registered() && + psx_game_address_in_text(addr) && psx_game_text_native_ok(addr))) fntrace_mark_game_started(cpu); } @@ -208,15 +220,7 @@ void fntrace_record(CPUState* cpu, uint32_t target) { * latching game-start before the EXE has loaded and clearing the dirty * baseline mid-load (the v0.0.2/v0.0.3 release-install boot crash). * Without an image, only the exact entry_pc dispatch may latch. */ - extern int psx_game_address_in_text(uint32_t addr); - extern int psx_game_text_native_ok(uint32_t addr); - extern int dirty_ram_text_image_registered(void); - uint32_t _tphys = target & 0x1FFFFFFFu; - if (_tphys == s_game_entry_phys || - (dirty_ram_text_image_registered() && - psx_game_address_in_text(target) && psx_game_text_native_ok(target))) { - fntrace_mark_game_started(cpu); - } + fntrace_maybe_mark_game_started(cpu, target); } /* Honor the one-shot capture freeze (insn_freeze): once latched, the ring * preserves the pre-divergence window instead of evicting it. */ diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 1678f6fb..0be6d739 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -10667,6 +10667,35 @@ int main(int argc, char** argv) { int cli_renderer = -1; /* 0=software 1=opengl 2=vulkan */ const char* cli_window_title = nullptr; /* label windows in a fleet */ const char* cli_memcard_dir = nullptr; /* isolate writable state in a fleet */ + std::vector cli_path_arg_storage; + cli_path_arg_storage.reserve((size_t)argc); + auto is_cli_option = [](const char* arg) { + return arg && arg[0] == '-' && arg[1] == '-'; + }; + auto consume_path_arg = [&](int& i) -> const char* { + const int first = i + 1; + std::string combined; + std::string best; + int best_index = first; + for (int j = first; j < argc; ++j) { + if (j != first && is_cli_option(argv[j])) break; + if (!combined.empty()) combined += " "; + combined += argv[j]; + + std::error_code ec; + if (std::filesystem::exists(std::filesystem::path(combined), ec)) { + best = combined; + best_index = j; + } + } + if (best.empty()) { + best = argv[first]; + best_index = first; + } + i = best_index; + cli_path_arg_storage.push_back(std::move(best)); + return cli_path_arg_storage.back().c_str(); + }; PsxNetplayConfig net_cfg; psx_netplay_config_defaults(&net_cfg); psx_netplay_apply_env(&net_cfg); /* CLI flags below win over env */ @@ -10692,17 +10721,17 @@ int main(int argc, char** argv) { * No --game-root flag: that remains config-driven. */ for (int i = 1; i < argc; i++) { if (std::strcmp(argv[i], "--bios") == 0 && i + 1 < argc) { - bios_path = argv[++i]; + bios_path = consume_path_arg(i); bios_from_cli = true; bios_explicit = true; } else if (std::strcmp(argv[i], "--game") == 0 && i + 1 < argc) { - game_config_path = argv[++i]; + game_config_path = consume_path_arg(i); } else if (std::strcmp(argv[i], "--disc") == 0 && i + 1 < argc) { - disc_override_path = argv[++i]; + disc_override_path = consume_path_arg(i); } else if (std::strcmp(argv[i], "--debug-port") == 0 && i + 1 < argc) { cli_debug_port = std::atoi(argv[++i]); } else if (std::strcmp(argv[i], "--memcard-dir") == 0 && i + 1 < argc) { - cli_memcard_dir = argv[++i]; + cli_memcard_dir = consume_path_arg(i); } else if (std::strcmp(argv[i], "--renderer") == 0 && i + 1 < argc) { const char* r = argv[++i]; if (std::strcmp(r, "software") == 0) cli_renderer = 0; diff --git a/runtime/tests/test_disc_path_resolve.cpp b/runtime/tests/test_disc_path_resolve.cpp index a3f24b59..c7a7727a 100644 --- a/runtime/tests/test_disc_path_resolve.cpp +++ b/runtime/tests/test_disc_path_resolve.cpp @@ -71,6 +71,23 @@ void test_single_file_dump(const fs::path& root) { // The whole point: both picks agree on what to read identity/CRC from. check(same(from_cue.data, from_bin.data), "single: both picks share a data track"); + + write_sectors(dir / "Tomba! (USA).bin", 4, 0x12); + write_text(dir / "Tomba! (USA).cue", + "FILE \"Tomba! (USA).bin\" BINARY\n" + " TRACK 01 MODE2/2352\n" + " INDEX 01 00:00:00\n"); + + const auto bang_from_cue = resolve_disc_path(dir / "Tomba! (USA).cue"); + check(bang_from_cue.from_cue, "single: cue path with ! mounts the cue"); + check(same(bang_from_cue.data, dir / "Tomba! (USA).bin"), + "single: cue path with ! resolves the data track"); + + const auto bang_from_bin = resolve_disc_path(dir / "Tomba! (USA).bin"); + check(bang_from_bin.upgraded_to_cue, + "single: bin path with ! upgrades to owning cue"); + check(same(bang_from_cue.data, bang_from_bin.data), + "single: ! cue/bin picks share a data track"); } // ---- 2. redump multi-track: picking any track file finds the cue ------------ diff --git a/runtime/tests/test_mod_load_acceleration.py b/runtime/tests/test_mod_load_acceleration.py index 8c7c2b01..3391b320 100644 --- a/runtime/tests/test_mod_load_acceleration.py +++ b/runtime/tests/test_mod_load_acceleration.py @@ -7,6 +7,9 @@ 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") +FNTRACE = (ROOT / "runtime/src/fntrace.c").read_text(encoding="utf-8") +DIRTY_INTERP = (ROOT / "runtime/src/dirty_ram_interp.c").read_text(encoding="utf-8") +CDROM = (ROOT / "runtime/src/cdrom.c").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" @@ -74,5 +77,10 @@ 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 (!manual_turbo_active && !turbo_load_paced && present_should_wall_pace())" in MAIN assert "if (g_mod_disc_speed_divisor >= 0)" in MAIN +assert "fntrace_maybe_mark_game_started(cpu, addr);" in DIRTY_INTERP +assert "dirty_ram_text_image_registered()" in FNTRACE +assert "psx_game_address_in_text(addr) && psx_game_text_native_ok(addr)" in FNTRACE +assert "mode_reg & 0x48u" in CDROM +assert "if (xa_stream_active || (mode_reg & 0x68u)) return delay;" not in CDROM print("mod-owned load acceleration guard passed")