Fix first-run setup loop on kits that pin a non-SCPH1001 BIOS - #27
Fix first-run setup loop on kits that pin a non-SCPH1001 BIOS#27Alexbeav wants to merge 10 commits into
Conversation
resolve_bios_for_runtime() validated an explicit --bios / bios.cfg pick before it noticed it was running on a first-run setup host, where zero BIOS backends are linked. With an empty registry bios_backend_for_file() matches nothing, so every image was rejected -- including the correct one -- and a title with openbios = false had no fallback and exited. The player could never supply the BIOS that Generate needs, so first-run setup deadlocked. 109 of 156 ports set openbios = false. The launcher-side check already had this guard in the right place; the Play path did not. Pure move, guard body unchanged.
bios_backends_missing() probed two hardcoded filenames, psxrecomp/generated/OpenBIOS_dispatch.c and SCPH1001_dispatch.c. A port that pins a different image via PSXRECOMP_BIOS_STEMS / game.toml recompiler.bios_config emits its backend under that stem instead, so the probe could never be satisfied: every Generate succeeded, the wizard reopened, and first-run setup looped forever. Every wave-3 kit pins SCPH5552, so this affected all of them. Accept any stem that has both <stem>_dispatch.c and <stem>_full.c, the same pairing runtime.cmake requires before it will link a backend.
host_loop_breaker_note() appended "the project's boot-EXE names disagree" whichever branch fired. That conclusion only holds when the game dispatch is missing; for missing BIOS backends it is wrong, and it sent two players (and the maintainer) after a naming problem that did not exist. Pick the explanation per branch, and do not name a specific BIOS image in it -- ports pin different ones, so point at the README instead.
The missing-recomp-ui error told the reader to run "git submodule add -b master https://github.com/mstan/recomp-ui.git". For anyone building a released kit that already declares recomp-ui and pins a fork commit, that either fails outright or wires up the wrong upstream at the wrong revision. Lead with clone --recurse-submodules / submodule update --init, name the GitHub source-ZIP trap that produces this state, and keep submodule add only for a repo that does not declare recomp-ui yet.
There was a problem hiding this comment.
2 issues found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="host/psxrecomp_codegen_host.c">
<violation number="1" location="host/psxrecomp_codegen_host.c:1198">
P1: When `generated/` contains a paired game output or stale non-configured BIOS, this scan treats it as a BIOS backend. The setup host can skip BIOS generation and rebuild with no linked backend; restrict the scan to configured, descriptor-bearing BIOS stems.</violation>
<violation number="2" location="host/psxrecomp_codegen_host.c:3817">
P2: When both `game_missing` and `bios_missing` are true, this branch chooses only the boot-EXE mismatch message. Add a combined branch so the wizard also gives the required BIOS remediation instead of sending the player only to the port maintainer.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| if (!d) | ||
| return 0; | ||
| while (!found && (e = readdir(d)) != NULL) { | ||
| if (dispatch_has_full(dir, e->d_name)) |
There was a problem hiding this comment.
P1: When generated/ contains a paired game output or stale non-configured BIOS, this scan treats it as a BIOS backend. The setup host can skip BIOS generation and rebuild with no linked backend; restrict the scan to configured, descriptor-bearing BIOS stems.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At host/psxrecomp_codegen_host.c, line 1198:
<comment>When `generated/` contains a paired game output or stale non-configured BIOS, this scan treats it as a BIOS backend. The setup host can skip BIOS generation and rebuild with no linked backend; restrict the scan to configured, descriptor-bearing BIOS stems.</comment>
<file context>
@@ -1140,15 +1140,74 @@ static int resolve_build_paths(void) {
+ if (!d)
+ return 0;
+ while (!found && (e = readdir(d)) != NULL) {
+ if (dispatch_has_full(dir, e->d_name))
+ found = 1;
+ }
</file context>
| if (game_missing) | ||
| cause = "please report this to the port maintainer: the project's " | ||
| "boot-EXE names disagree"; | ||
| else | ||
| cause = "Generate produced the game code but no BIOS backend, which " | ||
| "normally means it had no retail BIOS to work from. Select " | ||
| "the PlayStation BIOS dump this port requires (named in the " | ||
| "README; it must be exactly 512 KB) in the launcher, then " | ||
| "run Generate again"; | ||
| snprintf(g_loop_breaker_note, sizeof(g_loop_breaker_note), |
There was a problem hiding this comment.
P2: When both game_missing and bios_missing are true, this branch chooses only the boot-EXE mismatch message. Add a combined branch so the wizard also gives the required BIOS remediation instead of sending the player only to the port maintainer.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At host/psxrecomp_codegen_host.c, line 3817:
<comment>When both `game_missing` and `bios_missing` are true, this branch chooses only the boot-EXE mismatch message. Add a combined branch so the wizard also gives the required BIOS remediation instead of sending the player only to the port maintainer.</comment>
<file context>
@@ -3749,16 +3809,30 @@ static const char* host_loop_breaker_note(void) {
+ * BIOS backends do not: they mean Generate never emitted them, normally
+ * because no retail BIOS was available to emit them from. Blaming
+ * boot-EXE names for that sent players after the wrong thing. */
+ if (game_missing)
+ cause = "please report this to the port maintainer: the project's "
+ "boot-EXE names disagree";
</file context>
| if (game_missing) | |
| cause = "please report this to the port maintainer: the project's " | |
| "boot-EXE names disagree"; | |
| else | |
| cause = "Generate produced the game code but no BIOS backend, which " | |
| "normally means it had no retail BIOS to work from. Select " | |
| "the PlayStation BIOS dump this port requires (named in the " | |
| "README; it must be exactly 512 KB) in the launcher, then " | |
| "run Generate again"; | |
| snprintf(g_loop_breaker_note, sizeof(g_loop_breaker_note), | |
| if (game_missing && bios_missing) | |
| cause = "the generated game dispatch and BIOS backends are both " | |
| "missing. Check the boot-EXE names, select the PlayStation " | |
| "BIOS dump this port requires (named in the README; it must " | |
| "be exactly 512 KB), then run Generate again"; | |
| else if (game_missing) | |
| cause = "please report this to the port maintainer: the project's " | |
| "boot-EXE names disagree"; | |
| else | |
| cause = "Generate produced the game code but no BIOS backend, which " | |
| "normally means it had no retail BIOS to work from. Select " | |
| "the PlayStation BIOS dump this port requires (named in the " | |
| "README; it must be exactly 512 KB) in the launcher, then " | |
| "run Generate again"; |
test_cli_retail_bios_profile.py already covers recompiler.bios_config on the psxrecomp_cli.py side. The C host half of the same feature had no coverage, which is why bios_backends_missing() kept probing hardcoded OpenBIOS/SCPH1001 filenames and shipped a first-run loop on all 26 wave-3 kits. Compiles host/psxrecomp_codegen_host.c against a temporary project tree and asserts sources_missing() over four cases: a pinned non-SCPH1001 stem, the bundled stem, a dispatch with no _full.c, and no backend at all. Verified to fail on the pre-fix host and pass after. Skips cleanly when recomp-ui or a C compiler is unavailable.
Now verified end to end, not just structurallyGenerated a real SCPH5552 backend from an actual BIOS dump That is the reported bug reproduced against real generated artefacts, and the Added the missing regression test
and passes after. It skips cleanly without Why this shipped
Still outstanding: a full kit build/link and a launcher run. |
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="runtime/tests/test_codegen_host_bios_stems.py">
<violation number="1" location="runtime/tests/test_codegen_host_bios_stems.py:85">
P2: Compiling the host TU with strict `-std=c11` hides the POSIX functions it relies on (strcasecmp, opendir/readdir, realpath) behind glibc feature macros, so on glibc toolchains with GCC 14+ the probe fails to build and the test FAILs instead of running. Use `-std=gnu11` so the POSIX extensions the host needs stay declared.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| probe_c.write_text(PROBE, encoding="utf-8") | ||
| exe = tmp / ("probe.exe" if os.name == "nt" else "probe") | ||
| build = subprocess.run( | ||
| [cc, "-std=c11", "-o", str(exe), str(probe_c), str(HOST_C), |
There was a problem hiding this comment.
P2: Compiling the host TU with strict -std=c11 hides the POSIX functions it relies on (strcasecmp, opendir/readdir, realpath) behind glibc feature macros, so on glibc toolchains with GCC 14+ the probe fails to build and the test FAILs instead of running. Use -std=gnu11 so the POSIX extensions the host needs stay declared.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At runtime/tests/test_codegen_host_bios_stems.py, line 85:
<comment>Compiling the host TU with strict `-std=c11` hides the POSIX functions it relies on (strcasecmp, opendir/readdir, realpath) behind glibc feature macros, so on glibc toolchains with GCC 14+ the probe fails to build and the test FAILs instead of running. Use `-std=gnu11` so the POSIX extensions the host needs stay declared.</comment>
<file context>
@@ -0,0 +1,126 @@
+ probe_c.write_text(PROBE, encoding="utf-8")
+ exe = tmp / ("probe.exe" if os.name == "nt" else "probe")
+ build = subprocess.run(
+ [cc, "-std=c11", "-o", str(exe), str(probe_c), str(HOST_C),
+ "-I", str(ROOT / "host"),
+ "-I", str(ui / "src"), "-I", str(ui / "src" / "common")],
</file context>
| [cc, "-std=c11", "-o", str(exe), str(probe_c), str(HOST_C), | |
| [cc, "-std=gnu11", "-o", str(exe), str(probe_c), str(HOST_C), |
A built host asks its linked backends which image it accepts. A setup host has none -- that is what makes it a setup host -- so first-run discovery, bios.cfg seeding and every player-facing string hardcoded SCPH-1001. On a kit pinning anything else (all 26 wave-3 kits pin SCPH5552) that meant: a correct dump sitting beside the install was never auto-discovered, seeding refused it, and the launcher told the player their BIOS was wrong while quoting SCPH-1001's CRC at them. Add psx_bios_known_images.h as the one place those identities live, and have runtime.cmake pass the pinned stem in as PSX_EXPECTED_BIOS_STEM. Both SCPH-1001 (0x37157331) and SCPH-5552 (0xD786F0B9) are verified against the dumps and against the identity the recompiler emits into generated/<stem>_dispatch.c. Replaces the hardcodes in: - retail_bios_file_ok_c / discover_retail_bios_c (setup host) - retail_bios_file_ok / discover_retail_bios_near (runtime seeding) - the launcher BIOS row: size, CRC and "required" copy - the BIOS picker copy - PSXRT_DEFAULT_BIOS_PATH, which defaulted to bios/SCPH1001.BIN A stem absent from the table degrades safely: nothing is auto-adopted, the player is asked, and no message claims an identity it cannot back up. SCPH-1001 builds are unaffected.
Swept the remaining hardcoded SCPH-1001 assumptionsA built host asks its linked backends which image it accepts. A setup host has New Replaced in: setup-host discovery/validation ( Behaviour, from running the edited regions at three pinned stems: An unpinned or unknown stem degrades safely rather than asserting an identity VerificationThe header compiles clean under
|
Added the file without an add_test() entry, which is exactly the failure
mode runtime/check_test_registration.cmake exists to catch -- and it did,
at configure time:
Test file(s) present on disk but registered nowhere:
runtime/tests/test_codegen_host_bios_stems.py
An unregistered test cannot run and cannot fail. Register it alongside
the other runtime source-invariant guards; it needs neither a generated
BIOS nor a built runtime, and skips cleanly without recomp-ui or a C
compiler.
There was a problem hiding this comment.
3 issues found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="host/psxrecomp_codegen_host.c">
<violation number="1" location="host/psxrecomp_codegen_host.c:1326">
P2: On case-sensitive filesystems, this lookup skips common mixed-case spellings such as `SCPH5552.bin` because the filename helper emits only all-uppercase or all-lowercase forms. Generate all independent base and extension case combinations so valid dumps are still auto-discovered.</violation>
</file>
<file name="runtime/runtime.cmake">
<violation number="1" location="runtime/runtime.cmake:1310">
P2: When a reused build changes `PSXRECOMP_BIOS_STEMS`, this definition can retain the old cached stem and make setup-host BIOS discovery reject the newly pinned dump. Derive the expected stem and default path from the current `PSXRECOMP_BIOS_STEM_PRIMARY` (or refresh the cache) instead of using the stale cache entry.</violation>
</file>
<file name="runtime/src/main.cpp">
<violation number="1" location="runtime/src/main.cpp:2732">
P2: In the default `OpenBIOS;SCPH1001` build, `psx_expected_bios()` is null because the primary stem is the bundled `OpenBIOS`. This makes `discover_retail_bios_near()` skip all filename probes, so a `scph1001` session cannot find a nearby retail dump unless another path hint is supplied. Base discovery on the nonbundled linked BIOS stem (or otherwise preserve the retail filename set) instead of the bundled primary stem.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| "SCPH1001.bin", "scph1001.BIN", | ||
| }; | ||
| char names[8][32]; | ||
| int nnames = psx_known_bios_filenames(psx_expected_bios(), names, 8); |
There was a problem hiding this comment.
P2: On case-sensitive filesystems, this lookup skips common mixed-case spellings such as SCPH5552.bin because the filename helper emits only all-uppercase or all-lowercase forms. Generate all independent base and extension case combinations so valid dumps are still auto-discovered.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At host/psxrecomp_codegen_host.c, line 1326:
<comment>On case-sensitive filesystems, this lookup skips common mixed-case spellings such as `SCPH5552.bin` because the filename helper emits only all-uppercase or all-lowercase forms. Generate all independent base and extension case combinations so valid dumps are still auto-discovered.</comment>
<file context>
@@ -1307,22 +1315,22 @@ static int retail_bios_file_ok_c(const char* path) {
- "SCPH1001.bin", "scph1001.BIN",
- };
+ char names[8][32];
+ int nnames = psx_known_bios_filenames(psx_expected_bios(), names, 8);
static const char* subs[] = {
"bios", "", "system", "firmware", "psxrecomp/bios", "psxrecomp-v4/bios",
</file context>
| # The retail stem this build pins. A setup host has no linked | ||
| # backend to ask, so this is how it knows which image to look for | ||
| # and name (psx_bios_known_images.h) instead of assuming SCPH-1001. | ||
| PSX_EXPECTED_BIOS_STEM="${PSXRECOMP_BIOS_STEM}" |
There was a problem hiding this comment.
P2: When a reused build changes PSXRECOMP_BIOS_STEMS, this definition can retain the old cached stem and make setup-host BIOS discovery reject the newly pinned dump. Derive the expected stem and default path from the current PSXRECOMP_BIOS_STEM_PRIMARY (or refresh the cache) instead of using the stale cache entry.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At runtime/runtime.cmake, line 1310:
<comment>When a reused build changes `PSXRECOMP_BIOS_STEMS`, this definition can retain the old cached stem and make setup-host BIOS discovery reject the newly pinned dump. Derive the expected stem and default path from the current `PSXRECOMP_BIOS_STEM_PRIMARY` (or refresh the cache) instead of using the stale cache entry.</comment>
<file context>
@@ -1301,6 +1304,10 @@ function(psxrecomp_add_runtime_target target)
+ # The retail stem this build pins. A setup host has no linked
+ # backend to ask, so this is how it knows which image to look for
+ # and name (psx_bios_known_images.h) instead of assuming SCPH-1001.
+ PSX_EXPECTED_BIOS_STEM="${PSXRECOMP_BIOS_STEM}"
# Where the shipped redistributable image lives, relative to the exe.
# This is what a player gets when they choose no BIOS.
</file context>
| }; | ||
| char name_buf[8][32]; | ||
| const int name_count = | ||
| psx_known_bios_filenames(psx_expected_bios(), name_buf, 8); |
There was a problem hiding this comment.
P2: In the default OpenBIOS;SCPH1001 build, psx_expected_bios() is null because the primary stem is the bundled OpenBIOS. This makes discover_retail_bios_near() skip all filename probes, so a scph1001 session cannot find a nearby retail dump unless another path hint is supplied. Base discovery on the nonbundled linked BIOS stem (or otherwise preserve the retail filename set) instead of the bundled primary stem.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At runtime/src/main.cpp, line 2732:
<comment>In the default `OpenBIOS;SCPH1001` build, `psx_expected_bios()` is null because the primary stem is the bundled `OpenBIOS`. This makes `discover_retail_bios_near()` skip all filename probes, so a `scph1001` session cannot find a nearby retail dump unless another path hint is supplied. Base discovery on the nonbundled linked BIOS stem (or otherwise preserve the retail filename set) instead of the bundled primary stem.</comment>
<file context>
@@ -2708,24 +2710,27 @@ static bool retail_bios_file_ok(const std::filesystem::path& path) {
- };
+ char name_buf[8][32];
+ const int name_count =
+ psx_known_bios_filenames(psx_expected_bios(), name_buf, 8);
+ if (name_count <= 0) return {};
static const char* kSubdirs[] = {
</file context>
subprocess.run(..., text=True) decodes child output with the machine's preferred encoding. On a Windows install whose ANSI codepage is not UTF-8 the emitter's output kills the reader thread: File "encodings/cp1253.py", line 23, in decode UnicodeDecodeError: 'charmap' codec can't decode byte 0x9c Hit on a Greek-locale machine during a real generate. The generate still completed, but the captured stdout/stderr for that step is lost, so any diagnostic the CLI meant to surface silently vanishes -- and the callers that parse proc.stdout see nothing. Pin all nine output-capturing calls to utf-8 with errors="replace".
Disc selection, BIOS selection and generation were already scriptable through psxrecomp_cli.py (generate --disc --bios, rebuild, verify-disc). The one part with no headless entry point was the verdict: does the setup host consider the generated sources complete? That was reachable only by clicking through the wizard, so nothing in CI could assert it -- and a BIOS stem mismatch in exactly that check shipped a first-run loop on 26 titles. --setup-selfcheck prints that verdict as JSON and exits: 0 when setup is complete, 2 when the wizard would reopen, 1 when it cannot tell. Hooked into psxrecomp_codegen_host_forward_if_built(), which every title already calls with argc/argv at the top of main(), so existing titles gain the flag with no per-title change. Placed ahead of the PSX_HAS_GAME_DISPATCH early return so product builds answer too.
Full kit build done — the outstanding gate is closedBuilt Wipeout XL end to end from the staged kit with this branch's framework:
Three more defects found while doing it
Answering the automation questionDisc selection, BIOS selection and generation were already headless — What had no headless entry point was the verdict: does the setup host think
Negative test — hide It hooks A CI canary can now assert first-run completion in one line. |
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="recompiler/CMakeLists.txt">
<violation number="1" location="recompiler/CMakeLists.txt:630">
P3: This test is registered in the "runtime source-invariant guards" loop, which the enclosing comment guarantees only reads runtime source and "runs from a plain recompiler build" with no generated BIOS and no built runtime. `test_codegen_host_bios_stems.py` does not meet that contract: it returns 0 (reports PASS) as a SKIP whenever recomp-ui is absent or no C compiler is on PATH, and it compiles and executes `psxrecomp_codegen_host.c` rather than reading source. In a framework-only recompiler build recomp-ui is never present, so this test always skips and ctest reports it green with zero coverage, giving false confidence that the loop-breaker regression is guarded in the very tree the comment says it runs from. The host half only gets real coverage in full kit builds. Make the skip visible (e.g. return 77 so ctest treats it as SKIPPED) or relocate the registration so the loop's documented guarantee stays accurate.</violation>
</file>
<file name="host/psxrecomp_codegen_host.c">
<violation number="1" location="host/psxrecomp_codegen_host.c:4366">
P2: When a project path or display name contains a control character, `--setup-selfcheck` emits invalid JSON and headless CI parsers fail. Escape bytes below `0x20` (`\n`, `\r`, `\t`, `\b`, `\f`, or `\u00XX`) before writing them.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| for (; s && *s; ++s) { | ||
| if (*s == '\\' || *s == '"') | ||
| putchar('\\'); | ||
| putchar(*s); |
There was a problem hiding this comment.
P2: When a project path or display name contains a control character, --setup-selfcheck emits invalid JSON and headless CI parsers fail. Escape bytes below 0x20 (\n, \r, \t, \b, \f, or \u00XX) before writing them.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At host/psxrecomp_codegen_host.c, line 4366:
<comment>When a project path or display name contains a control character, `--setup-selfcheck` emits invalid JSON and headless CI parsers fail. Escape bytes below `0x20` (`\n`, `\r`, `\t`, `\b`, `\f`, or `\u00XX`) before writing them.</comment>
<file context>
@@ -4344,9 +4344,92 @@ static int host_paths_same_file(const char* a, const char* b) {
+ for (; s && *s; ++s) {
+ if (*s == '\\' || *s == '"')
+ putchar('\\');
+ putchar(*s);
+ }
+ putchar('"');
</file context>
| putchar(*s); | |
| if ((unsigned char)*s < 0x20) { | |
| switch (*s) { | |
| case '\b': fputs("\\b", stdout); break; | |
| case '\f': fputs("\\f", stdout); break; | |
| case '\n': fputs("\\n", stdout); break; | |
| case '\r': fputs("\\r", stdout); break; | |
| case '\t': fputs("\\t", stdout); break; | |
| default: fprintf(stdout, "\\u%04X", (unsigned char)*s); break; | |
| } | |
| } else { | |
| putchar(*s); | |
| } |
| bios_selection_guards | ||
| capture_history | ||
| cli_retail_bios_profile | ||
| codegen_host_bios_stems |
There was a problem hiding this comment.
P3: This test is registered in the "runtime source-invariant guards" loop, which the enclosing comment guarantees only reads runtime source and "runs from a plain recompiler build" with no generated BIOS and no built runtime. test_codegen_host_bios_stems.py does not meet that contract: it returns 0 (reports PASS) as a SKIP whenever recomp-ui is absent or no C compiler is on PATH, and it compiles and executes psxrecomp_codegen_host.c rather than reading source. In a framework-only recompiler build recomp-ui is never present, so this test always skips and ctest reports it green with zero coverage, giving false confidence that the loop-breaker regression is guarded in the very tree the comment says it runs from. The host half only gets real coverage in full kit builds. Make the skip visible (e.g. return 77 so ctest treats it as SKIPPED) or relocate the registration so the loop's documented guarantee stays accurate.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At recompiler/CMakeLists.txt, line 630:
<comment>This test is registered in the "runtime source-invariant guards" loop, which the enclosing comment guarantees only reads runtime source and "runs from a plain recompiler build" with no generated BIOS and no built runtime. `test_codegen_host_bios_stems.py` does not meet that contract: it returns 0 (reports PASS) as a SKIP whenever recomp-ui is absent or no C compiler is on PATH, and it compiles and executes `psxrecomp_codegen_host.c` rather than reading source. In a framework-only recompiler build recomp-ui is never present, so this test always skips and ctest reports it green with zero coverage, giving false confidence that the loop-breaker regression is guarded in the very tree the comment says it runs from. The host half only gets real coverage in full kit builds. Make the skip visible (e.g. return 77 so ctest treats it as SKIPPED) or relocate the registration so the loop's documented guarantee stays accurate.</comment>
<file context>
@@ -627,6 +627,7 @@ if(BUILD_TESTING)
bios_selection_guards
capture_history
cli_retail_bios_profile
+ codegen_host_bios_stems
default_renderer_guards
mod_owned_display_controls
</file context>
The message said:
git submodule update --init lib/retcomm-rbengine
That only works from inside psxrecomp/. Someone standing at the game
repo root -- which is where they cloned, and where the error is read --
gets "no submodule mapping found", because from there the path is
psxrecomp/lib/retcomm-rbengine.
retcomm-rbengine is a submodule of psxrecomp, not of the game, so a
non-recursive init at the game root populates psxrecomp/ and leaves
lib/* empty, which is exactly the state that trips this check. Name
--recursive, and name the GitHub source-ZIP trap that also produces it.
Correction: the published kits were never brokenI said earlier that "the staged kits ship submodules empty". That was wrong
So a user who clones recursively is fine. Nothing to fix in the repos. What actually was broken: the staging checkouts
— a plain clone, no recursion, and no Fixed both ways:
One real papercut fixed in this branch (
|
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="runtime/runtime.cmake">
<violation number="1" location="runtime/runtime.cmake:538">
P3: The new guidance in this block is correct (rbengine is a submodule of psxrecomp, not of the game, so non-recursive init leaves it empty), but the identical scenario elsewhere in the same file still shows the old advice: the PSX_NETPLAY FATAL_ERROR right above still suggests `git submodule update --init lib/retcomm-rbengine`, run non-recursively from the game root. Per this new message that command cannot work. Update that sibling block to match (recommend `git submodule update --init --recursive`) so the two diagnostics for the same missing-rbengine failure don't contradict each other.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| "In an existing clone, run this from the GAME repo root. Note " | ||
| "--recursive: rbengine is a submodule of psxrecomp, not of the " | ||
| "game, so a non-recursive init leaves it empty.\n" | ||
| " git submodule update --init --recursive\n" |
There was a problem hiding this comment.
P3: The new guidance in this block is correct (rbengine is a submodule of psxrecomp, not of the game, so non-recursive init leaves it empty), but the identical scenario elsewhere in the same file still shows the old advice: the PSX_NETPLAY FATAL_ERROR right above still suggests git submodule update --init lib/retcomm-rbengine, run non-recursively from the game root. Per this new message that command cannot work. Update that sibling block to match (recommend git submodule update --init --recursive) so the two diagnostics for the same missing-rbengine failure don't contradict each other.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At runtime/runtime.cmake, line 538:
<comment>The new guidance in this block is correct (rbengine is a submodule of psxrecomp, not of the game, so non-recursive init leaves it empty), but the identical scenario elsewhere in the same file still shows the old advice: the PSX_NETPLAY FATAL_ERROR right above still suggests `git submodule update --init lib/retcomm-rbengine`, run non-recursively from the game root. Per this new message that command cannot work. Update that sibling block to match (recommend `git submodule update --init --recursive`) so the two diagnostics for the same missing-rbengine failure don't contradict each other.</comment>
<file context>
@@ -529,7 +529,13 @@ if(PSX_REWIND)
+ "In an existing clone, run this from the GAME repo root. Note "
+ "--recursive: rbengine is a submodule of psxrecomp, not of the "
+ "game, so a non-recursive init leaves it empty.\n"
+ " git submodule update --init --recursive\n"
" or -DRECOMP_RBENGINE_ROOT=/path/to/retcomm-rbengine\n"
" or configure with -DPSX_REWIND=OFF to hide Rewind.")
</file context>
Two users reported batch-3 titles (Wipeout XL SCUS-94351, Threads of Fate
SLUS-01019) looping forever in first-run setup. Same root cause, and it
affects all 26 wave-3 titles.
Root cause
bios_backends_missing()probed two hardcoded filenames:The CLI honours
recompiler.bios_configand emits<stem>_*.c. Every wave-3kit pins SCPH5552 (
game.tomlbios_config,CMakeLists.txtPSXRECOMP_BIOS_STEMS, and the shipped README all agree), so those two filesnever exist. The probe was permanently unsatisfiable: Generate succeeded every
time, the wizard reopened every time.
A second, independent bug made the Play path reject a correct BIOS on a setup
host:
resolve_bios_for_runtime()validated an explicit--bios/bios.cfgpick before the
psx_bios_registry_count == 0guard. With an empty registrybios_backend_for_file()matches nothing, and a title withopenbios = false(109 of 156 ports) has no fallback. The launcher-side check already had this
guard in the right place.
Changes
runtime: check setup host before validating a chosen BIOS— pure move ofthe existing guard above the explicit-choice branch; guard body unchanged.
host: detect any recompiled BIOS stem— accept any stem with both<stem>_dispatch.cand<stem>_full.c, the same pairingruntime.cmakerequires before linking a backend.
host: give the first-run loop breaker a cause-specific explanation— itappended "the project's boot-EXE names disagree" on every branch, which is
wrong when only the BIOS backends are missing, and sent both reporters after
a naming problem that did not exist. Also stops naming a specific BIOS image,
since ports pin different ones.
runtime: point the recomp-ui preflight at submodule init— the error toldthe reader to
git submodule add -b master https://github.com/mstan/recomp-ui.git,which on a released kit that already pins a fork commit either fails or wires
up the wrong upstream at the wrong revision.
Verification
Structural: brace and paren balance unchanged, CRLF preserved, no stray NULs,
and the relocated BIOS guard is byte-identical to its previous form.
Functional: the new
dispatch_has_full()/generated_has_bios_backend()helpers were extracted and compiled standalone on both the Win32 and POSIX
branches with
-Wall -Wextra, and exercised over three cases — both halvespresent (found), dispatch only (missing), directory absent (missing). The
rewritten loop-breaker text was compiled and rendered for all three branch
combinations.
Not yet done: a real kit build. A framework-only checkout has no
recomp-ui, so this needs a full build against a kit before shipping.Follow-ups, not in this PR
Other hardcoded-SCPH1001 assumptions remain: BIOS discovery filenames and
adoption paths in
host/psxrecomp_codegen_host.c, andruntime/src/main.cpptreatscrc != 0x37157331uas "the validated dump" inthe launcher BIOS row, which warns misleadingly on a correct SCPH-5552.
Summary by cubic
Fixes the first-run setup deadlock on all 26 wave-3 kits, which pin SCPH5552 instead of SCPH1001, and adds headless setup verification. BIOS detection, discovery, seeding, and player-facing copy now follow the pinned stem via
psx_bios_known_images.hinstead of assuming SCPH1001, and the setup probe accepts any stem with both<stem>_dispatch.cand<stem>_full.c. A second bug made the Play path reject a correct BIOS on setup hosts, deadlocking titles withopenbios = false(109 of 156 ports); the setup-host guard now runs before explicit BIOS validation.Changes
recomp-uipreflight now leads withgit clone --recurse-submodules/git submodule update --initand warns that GitHub source ZIPs never contain submodules.retcomm-rbenginepreflight now recommendsgit submodule update --init --recursiveand names the GitHub source-ZIP trap, since the old path only worked from insidepsxrecomp/.--setup-selfcheck, which prints a JSON verdict of setup completeness and exits 0 (done) or 2 (wizard would reopen) so CI can assert it.subprocess.runcalls inpsxrecomp_cli.pynow decode as UTF-8 witherrors="replace"instead of the ANSI codepage, which lost captured output on non-UTF-8 Windows locales.Verification includes the regression test plus structural checks; no full kit build was run because a framework-only checkout lacks
recomp-ui.Written for commit 66f432e. Summary will update on new commits.