Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions docs/BUILD_PACKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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 <commit>` to run the same tests against an
older build implementation, or `--owned-root <setup-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
Expand Down
65 changes: 65 additions & 0 deletions scripts/fixtures/psx_bios_readiness.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
40 changes: 40 additions & 0 deletions scripts/test_psx_bios_readiness.py
Original file line number Diff line number Diff line change
@@ -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()
10 changes: 6 additions & 4 deletions src/build/build.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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/<name>/<pin>/ tree whose key-file content_id matches
Expand Down