Files
psxrecomp/tools/build_cli.py
Alexandros Mandravillis ff4be21c90 bios: add the SCPH-5500 and SCPH-5501 profiles
The v3.0 Japanese (SCPH-5500, 09/09/96) and US (SCPH-5501, 11/18/96)
retail BIOS images join SCPH-1001, SCPH-101 and SCPH-5552, so every v3.0
region (J, A, E) has a profile.

Both images match SCPH-1001 v2.2 byte for byte in the kernel,
ROM [0x80, 0x18000). SCPH-5501's reset vector is identical too; SCPH-5500's
differs in 24 bytes. The shell differs in both. So each profile is
SCPH1001.toml with its own identity (name, id, rom, sha256 pin, stem): the
copy windows and install slots are kernel properties and carry over
unchanged. The shell runs through the dirty-RAM interpreter, as for
SCPH5552.

Each image gets its own seed list, derived by the same rule as
phase2_ghidra_seeds_SCPH5552.json: the SCPH-1001 corpus filtered to seeds
whose 64-byte ROM window is identical in both images (534 kept, 569
dropped), plus the two architectural vectors and 6 A0/B0/C0 kernel-table
targets. The files carry only addresses, labels and rationales, plus the
SHA-256 of both ROMs.

- psx_bios_known_images.h: CRC-32 FF3EEB8C (SCPH-5500) and 8D8CB7E4
  (SCPH-5501), both 512 KiB. A build that does not link these backends
  builds one from the player's own dump as a loadable module, like
  SCPH-5552, and the netplay lobby can name the image.
- main_cli.cpp: map SCPH5500 / SCPH5501 dumps to their profiles.
- build_cli.py: package both profiles with the CLI framework.
- docs/BIOS_SELECTION.md: list them among the module-built images.
2026-10-01 09:28:13 +03:00

148 lines
5.4 KiB
Python

"""Build and package the PSXRecomp developer command-line tools."""
from __future__ import annotations
import argparse
import os
import pathlib
import platform
import shutil
import subprocess
import sys
ROOT = pathlib.Path(__file__).resolve().parent.parent
TOOLS = ("psxrecomp", "psxrecomp-toml", "psxrecomp-game", "psxrecomp-bios")
def run(command: list[str]) -> None:
print("+", subprocess.list2cmdline(command), flush=True)
subprocess.run(command, cwd=ROOT, check=True)
def platform_name() -> str:
systems = {"Windows": "windows", "Linux": "linux", "Darwin": "macos"}
machines = {
"amd64": "x86_64",
"x86_64": "x86_64",
"arm64": "arm64",
"aarch64": "arm64",
}
raw_system = platform.system()
if raw_system.startswith(("MSYS", "MINGW", "CYGWIN")):
raw_system = "Windows"
system = systems.get(raw_system, raw_system.lower())
machine = machines.get(platform.machine().lower(), platform.machine().lower())
return f"{system}-{machine}"
def find_tool(build_dir: pathlib.Path, name: str, config: str) -> pathlib.Path:
suffix = ".exe" if os.name == "nt" else ""
filename = name + suffix
candidates = (build_dir / filename, build_dir / config / filename)
for candidate in candidates:
if candidate.is_file():
return candidate
checked = ", ".join(str(path) for path in candidates)
raise FileNotFoundError(f"{filename} was not built; checked {checked}")
def main() -> int:
parser = argparse.ArgumentParser(
description="build a redistributable PSXRecomp CLI archive")
parser.add_argument(
"configuration", nargs="?", choices=("release", "debug"),
default="release", help="build configuration (default: release)")
parser.add_argument(
"--build-dir", default="recompiler/build-cli",
help="CMake build directory (default: recompiler/build-cli)")
parser.add_argument(
"--dist-dir", default="dist",
help="package output directory (default: dist)")
parser.add_argument(
"--skip-build", action="store_true",
help="package tools already present in --build-dir")
args = parser.parse_args()
config = "Release" if args.configuration == "release" else "Debug"
build_dir = (ROOT / args.build_dir).resolve()
dist_dir = (ROOT / args.dist_dir).resolve()
if not args.skip_build:
if shutil.which("cmake") is None:
parser.error("CMake is not installed or is not on PATH")
source_arg = os.path.relpath(ROOT / "recompiler", ROOT)
build_arg = os.path.relpath(build_dir, ROOT)
configure = [
"cmake", "-S", source_arg, "-B", build_arg,
f"-DCMAKE_BUILD_TYPE={config}",
"-DPSXRECOMP_STATIC_CLI=ON",
]
if not (build_dir / "CMakeCache.txt").exists() and shutil.which("ninja"):
configure += ["-G", "Ninja"]
run(configure)
jobs = str(os.cpu_count() or 2)
run([
"cmake", "--build", build_arg, "--config", config,
"--target", *TOOLS, "--parallel", jobs,
])
package = f"psxrecomp-cli-{platform_name()}"
stage = dist_dir / package
archive = dist_dir / f"{package}.zip"
dist_dir.mkdir(parents=True, exist_ok=True)
if stage.exists():
shutil.rmtree(stage)
if archive.exists():
archive.unlink()
stage.mkdir()
shutil.copy2(find_tool(build_dir, "psxrecomp", config), stage)
libexec = stage / "libexec"
libexec.mkdir()
for name in TOOLS[1:]:
shutil.copy2(find_tool(build_dir, name, config), libexec)
share = stage / "share"
share.mkdir()
shutil.copy2(
ROOT / "recompiler" / "seeds" / "phase2_ghidra_seeds.json", share)
framework = stage / "framework"
source_ignore = shutil.ignore_patterns(
"build", "build-*", "tests", "__pycache__", "*.pyc", "*.pyo",
"*.exe", "*.obj", "*.pdb", "*.lib")
shutil.copytree(ROOT / "runtime", framework / "runtime", ignore=source_ignore)
shutil.copytree(ROOT / "recompiler", framework / "recompiler", ignore=source_ignore)
bios_profiles = {
"OpenBIOS.toml": ROOT / "bios" / "OpenBIOS.toml",
"SCPH1001.toml": ROOT / "bios" / "SCPH1001.toml",
"SCPH101.toml": ROOT / "bios" / "SCPH101.toml",
"SCPH5500.toml": ROOT / "bios" / "SCPH5500.toml",
"SCPH5501.toml": ROOT / "bios" / "SCPH5501.toml",
"SCPH5552.toml": ROOT / "bios" / "SCPH5552.toml",
}
(framework / "bios").mkdir()
for filename, source in bios_profiles.items():
shutil.copy2(source, framework / "bios" / filename)
shutil.copy2(ROOT / "bios" / "openbios.bin", framework / "bios")
shutil.copy2(ROOT / "bios" / "OpenBIOS.LICENSE", framework / "bios")
shutil.copy2(ROOT / ".gitignore", framework)
shutil.copy2(ROOT / "LICENSE", framework)
shutil.copy2(ROOT / "THIRD_PARTY_ATTRIBUTION.md", framework)
shutil.copy2(ROOT / "README.md", stage)
shutil.copy2(ROOT / "LICENSE", stage)
shutil.copy2(ROOT / "THIRD_PARTY_ATTRIBUTION.md", stage)
shutil.make_archive(str(archive.with_suffix("")), "zip", dist_dir, package)
print(f"PSXRecomp CLI package: {archive}")
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except subprocess.CalledProcessError as exc:
print(f"build_cli: command failed with exit code {exc.returncode}",
file=sys.stderr)
raise SystemExit(exc.returncode)