From 198cbb5e53c3a0c5d9fc23a0977317ec9c049d8a Mon Sep 17 00:00:00 2001 From: Alexandros Mandravillis Date: Mon, 7 Sep 2026 03:45:53 +0300 Subject: [PATCH] Support SCPH5552 in shared build preparation --- docs/BUILD_PACKS.md | 13 ++++- scripts/fixtures/psx_bios_readiness.cpp | 65 +++++++++++++++++++++++++ scripts/test_psx_bios_readiness.py | 40 +++++++++++++++ src/build/build.cpp | 10 ++-- 4 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 scripts/fixtures/psx_bios_readiness.cpp create mode 100644 scripts/test_psx_bios_readiness.py diff --git a/docs/BUILD_PACKS.md b/docs/BUILD_PACKS.md index ea2dd6c..98e3a2b 100644 --- a/docs/BUILD_PACKS.md +++ b/docs/BUILD_PACKS.md @@ -28,8 +28,8 @@ linking fails — so a failed `mklink` never leaves Install without `psxrecomp`. If linking is still impossible, RetComM falls back to a private copy from the engines cache. Titles that pin the same `psxrecomp` / `recomp-ui` commit therefore share one source tree on disk. -BIOS generated C under `psxrecomp/generated/` lives in that shared pin (OpenBIOS -/ SCPH1001 are pin-identical). Content-sync of game updates never writes through +BIOS generated C under `psxrecomp/generated/` lives in that shared pin. Readiness +checks recognize OpenBIOS, SCPH1001, and SCPH5552 output. Content-sync of game updates never writes through those links — engines are re-harvested from the staging zip instead. Developer overrides via `RETCOMM_SOURCE_DIR` skip promotion so local checkouts stay intact. @@ -41,6 +41,15 @@ requires **both** emitters (no separate tools zip). The setup host inside `src/` is never treated as a finished Play install — only `releases/` (or `current/`) counts. +The harvested PSX SDK retains the SCPH5552 profile and its matching seed file. +It does not copy the player's retail BIOS image into the shared SDK. +The selected game recipe still decides which BIOS to generate. +Run `python scripts/test_psx_bios_readiness.py` to check the production readiness +and SDK harvest functions with synthetic files. GCC or Clang with C++17 and LTO +is required. Add `--baseline-ref ` to run the same tests against an +older build implementation, or `--owned-root ` to check existing +private generated outputs. These checks do not install or play a game. + **Incremental cmake on update:** source always lives at `src/current/`. A newer release zip is **content-synced** into that tree (overwrite only when bytes change, delete paths removed upstream, leave identical files’ mtimes alone) so diff --git a/scripts/fixtures/psx_bios_readiness.cpp b/scripts/fixtures/psx_bios_readiness.cpp new file mode 100644 index 0000000..f711f44 --- /dev/null +++ b/scripts/fixtures/psx_bios_readiness.cpp @@ -0,0 +1,65 @@ +// Exercise the actual private build/cache functions without a network or retail data. +#include "../../src/build/build.cpp" + +static int failures = 0; +static void check(bool value, const char* label) { + std::cout << (value ? "PASS " : "FAIL ") << label << '\n'; + if (!value) ++failures; +} +static void put(const retcomm::fs::path& p, const std::string& content = "fixture\n") { + retcomm::fs::create_directories(p.parent_path()); + std::ofstream(p) << content; +} +int main(int argc, char** argv) { + using namespace retcomm; + if (argc < 2) return 2; + const fs::path root = argv[1]; + fs::create_directories(root); + for (const char* stem : {"OpenBIOS", "SCPH1001", "SCPH5552"}) { + const fs::path src = root / stem; + put(src / "generated" / "fixture_dispatch.c"); + put(src / "psxrecomp/generated" / (std::string(stem) + "_dispatch.c")); + check(psx_generated_ready(src), (std::string(stem) + " game ready").c_str()); + check(bios_generated_present(src / "psxrecomp"), (std::string(stem) + " engine ready").c_str()); + } + const fs::path bad = root / "negative"; + check(!psx_generated_ready(bad), "missing generated files rejected"); + put(bad / "generated/fixture_dispatch.c"); + put(bad / "psxrecomp/generated/SCPH5552_dispatch.c.txt"); + check(!psx_generated_ready(bad), "wrong suffix rejected"); + fs::create_directories(bad / "psxrecomp/generated/SCPH5552_dispatch.c"); + check(!psx_generated_ready(bad), "directory is not generated C"); + check(!bios_generated_present(bad / "psxrecomp"), "engine directory is not generated C"); + const fs::path only_bios = root / "only bios"; + put(only_bios / "psxrecomp/generated/SCPH5552_dispatch.c"); + check(!psx_generated_ready(only_bios), "missing game output rejected"); + + const fs::path embedded = root / "embedded title"; + put(embedded / "psxrecomp/psxrecomp_cli.py"); + put(embedded / "psxrecomp/recompiler/build/psxrecomp-game"); + put(embedded / "psxrecomp/recompiler/build/psxrecomp-bios"); + put(embedded / "psxrecomp/bios/SCPH5552.toml", "[recompiler]\nseeds = 'recompiler/seeds/phase2_ghidra_seeds_SCPH5552.json'\n"); + put(embedded / "psxrecomp/recompiler/seeds/phase2_ghidra_seeds_SCPH5552.json", "[]\n"); + put(embedded / "psxrecomp/bios/SCPH5552.BIN", "private fixture must not be harvested\n"); + Paths paths; + paths.config_dir = root / "config"; + paths.data_dir = root / "data"; + paths.apps_dir = root / "apps"; + paths.toolchains_dir = root / "toolchains"; + paths.sdks_dir = root / "sdks"; + paths.engines_dir = root / "engines"; + paths.catalog_dir = root / "catalog"; + Title title; + title.platform = "psx"; + title.build.generate.engine = "psxrecomp"; + const auto sdk = harvest_embedded_sdk(paths, title, embedded, "fixture"); + check(sdk.ok, "embedded SDK harvest succeeds"); + check(files_content_equal(embedded / "psxrecomp/bios/SCPH5552.toml", sdk.root / "bios/SCPH5552.toml"), "SCPH5552 profile retained exactly"); + check(files_content_equal(embedded / "psxrecomp/recompiler/seeds/phase2_ghidra_seeds_SCPH5552.json", sdk.root / "recompiler/seeds/phase2_ghidra_seeds_SCPH5552.json"), "SCPH5552 seeds retained exactly"); + check(!fs::exists(sdk.root / "bios/SCPH5552.BIN"), "owned BIOS excluded from shared SDK harvest"); + for (int i = 2; i < argc; ++i) { + check(psx_generated_ready(argv[i]), "owned package output is ready"); + check(bios_generated_present(fs::path(argv[i]) / "psxrecomp"), "owned package BIOS is present"); + } + return failures ? 1 : 0; +} diff --git a/scripts/test_psx_bios_readiness.py b/scripts/test_psx_bios_readiness.py new file mode 100644 index 0000000..10cf08e --- /dev/null +++ b/scripts/test_psx_bios_readiness.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Run production PSX readiness and embedded-SDK tests with synthetic files. + +Requires GCC or Clang with C++17. Optional roots must contain outputs from a +completed owned-input setup. This test does not install or launch a game. +""" +import argparse +import os +from pathlib import Path +import subprocess +import sys +import tempfile + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--cxx', default=os.environ.get('CXX', 'g++')) + parser.add_argument('--baseline-ref', help='Test the selected historical build.cpp; expected failures prove the regression') + parser.add_argument('--owned-root', action='append', default=[]) + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + with tempfile.TemporaryDirectory(prefix='retcomm bios test ') as work: + work = Path(work) + exe = work / ('check.exe' if os.name == 'nt' else 'check') + source = root/'src/build/build.cpp' + if args.baseline_ref: + source=work/'baseline_build.cpp' + source.write_bytes(subprocess.check_output(['git','-C',str(root),'show',args.baseline_ref+':src/build/build.cpp'])) + driver=work/'driver.cpp' + driver.write_text((root/'scripts/fixtures/psx_bios_readiness.cpp').read_text().replace('#include "../../src/build/build.cpp"', '#include "'+source.as_posix()+'"')) + cmd = [args.cxx, '-std=c++17', '-O2', '-flto', '-ffunction-sections', '-fdata-sections', + '-I'+str(root/'include'), '-I'+str(root/'third_party'), + str(driver), + str(root/'src/paths/paths.cpp'), + '-Wl,-dead_strip' if sys.platform == 'darwin' else '-Wl,--gc-sections', + '-o', str(exe)] + if os.name == 'nt': cmd += ['-lshell32'] + subprocess.run(cmd, check=True) + subprocess.run([str(exe), str(work/'fixtures'), *args.owned_root], check=True) + +if __name__ == '__main__': main() diff --git a/src/build/build.cpp b/src/build/build.cpp index 386ffb1..c6d8a2a 100644 --- a/src/build/build.cpp +++ b/src/build/build.cpp @@ -1036,7 +1036,8 @@ bool psx_generated_ready(const fs::path& src_root) { if (!game_ok) return false; const fs::path bios_gen = src_root / "psxrecomp" / "generated"; return fs::is_regular_file(bios_gen / "OpenBIOS_dispatch.c", ec) || - fs::is_regular_file(bios_gen / "SCPH1001_dispatch.c", ec); + fs::is_regular_file(bios_gen / "SCPH1001_dispatch.c", ec) || + fs::is_regular_file(bios_gen / "SCPH5552_dispatch.c", ec); } bool snes_generated_ready(const Title& title, const fs::path& src_root) { @@ -1452,11 +1453,11 @@ PackEnsureResult harvest_embedded_sdk(const Paths& paths, const Title& title, copy_rel_file(eng, dest, fs::path("recompiler/build") / name, ec); } for (const char* name : {"OpenBIOS.toml", "openbios.bin", "OpenBIOS.LICENSE", - "SCPH1001.toml"}) { + "SCPH1001.toml", "SCPH5552.toml"}) { copy_rel_file(eng, dest, fs::path("bios") / name, ec); } for (const char* name : {"openbios_elf_seeds.json", "openbios_dispatch_miss.json", - "phase2_ghidra_seeds.json"}) { + "phase2_ghidra_seeds.json", "phase2_ghidra_seeds_SCPH5552.json"}) { copy_rel_file(eng, dest, fs::path("recompiler/seeds") / name, ec); } } @@ -1970,7 +1971,8 @@ bool bios_generated_present(const fs::path& eng_or_src) { std::error_code ec; const fs::path bios_gen = eng_or_src / "generated"; return fs::is_regular_file(bios_gen / "OpenBIOS_dispatch.c", ec) || - fs::is_regular_file(bios_gen / "SCPH1001_dispatch.c", ec); + fs::is_regular_file(bios_gen / "SCPH1001_dispatch.c", ec) || + fs::is_regular_file(bios_gen / "SCPH5552_dispatch.c", ec); } // Prefer an existing engines/// tree whose key-file content_id matches