Files
BFM-decomp/tools/make_snd_used.py
T
Drew T cb7a280994 refactor(phase-9): T8a — de-default the EXE-curation helpers (thread --exe/--vram-base)
- make_snd_used.py + make_apicard_used.py: argparse --exe/--vram-base (default the
  EXE's); thread into psyq_identify subprocess + link_object (required post-T8b).
  RLO/RHI/EXCLUDE_ADDR kept as the EXE's region data (EXE-specific, documented)
- gen_lib_subsegs.py: --vram-base/--exe (default EXE); threaded into psyq_identify
  and the pos->fileoff arithmetic (module VRAM_BASE constant removed)
- split_src_region.py: --symbols overrides SYMS_PATH (default the EXE's symbols.us.txt)
- checks: snd_used (60) + apicard_used (22) regenerate BYTE-IDENTICAL; gen_lib_subsegs
  libgte -> 22 blocks/53 objs; clean build -> 143dbb89
2026-06-15 20:55:28 -06:00

79 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Build the curated .run/obj40/snd_used dir = the combined libspu+libsnd sound region (Phase 8).
libspu and libsnd are tightly interleaved in 0x8003A444..0x8004239C (the tail of the 800 subseg), so
they are linked as ONE combined region rather than two passes. This:
- places both libraries' objects (psyq_identify) and merges them by vram,
- for an aliased address (>1 object, same masked .text) picks the object whose linked .text
byte-matches the EXE (psyq_link.link_object) — the real one,
- EXCLUDES 4 ADDRESSES that don't reconcile in the combined region (ALL candidates there stay
byte-identical stubs — excluding by address, not name, since the alias twin fails identically):
0x3C438 (S_R/S_W), 0x3D424 (S_GRMDT/FB/T) — scattered-.bss commons referenced at a minority
address the region's single defsym can't satisfy (cookbook §9.1, cross-object form),
0x3D94C (S_IH/UT_RON) — false placement: 0x3D94C is INSIDE libsnd SSSTART.o,
0x3FA64 (VM_F.o, 237 ins) — scattered-.bss commons (the one real value loss).
- copies the survivors into .run/obj40/snd_used.
The build is byte-identical with OR without snd_used (stub fallback), so a fresh clone need not run
this unless it wants the SDK objects linked. Regenerate: tools/psyq_build_libs.sh LIBSPU LIBSND first.
"""
import os, re, shutil, subprocess, sys
from collections import defaultdict
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from psyq_link import link_object # noqa: E402
# EXE-curation tool: --exe/--vram-base default to the retail EXE's values (threaded explicitly into
# psyq_identify + link_object, which require them post-T8). RLO/RHI/EXCLUDE_ADDR are the EXE's sound
# region + the 4 unreconcilable addresses (EXE-specific; an overlay would supply its own).
EXE = "extracted/retail/SLUS_007.26"
VRAM_BASE = 0x8000F800
RLO, RHI = 0x8003A444, 0x8004239C
EXCLUDE_ADDR = {0x8003C438, 0x8003D424, 0x8003D94C, 0x8003FA64} # see module docstring
def place(lib, exe, vram_base):
out = subprocess.check_output(["python3", "tools/psyq_identify.py", f".run/obj40/{lib}",
"--vram-base", hex(vram_base), "--exe", exe], text=True)
d = {}
for ln in out.splitlines():
m = re.match(r"\s+0x([0-9A-Fa-f]+)\s+(\S+\.o)\s+\((\d+) ins\)", ln)
if m:
d[m.group(2)] = int(m.group(1), 16)
return d
def main():
import argparse
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--exe", default=EXE)
ap.add_argument("--vram-base", default=hex(VRAM_BASE))
args = ap.parse_args()
exe_path, vram_base = args.exe, int(args.vram_base, 0)
exe = open(exe_path, "rb").read()
byaddr = defaultdict(list)
for lib in ("libspu", "libsnd"):
for nm, a in place(lib, exe_path, vram_base).items():
byaddr[a].append((lib, nm))
dst = ".run/obj40/snd_used"
shutil.rmtree(dst, ignore_errors=True)
os.makedirs(dst)
n = 0
for a in sorted(byaddr):
if not (RLO <= a < RHI) or a in EXCLUDE_ADDR:
continue
cands = byaddr[a]
pick = next(((lib, nm) for lib, nm in cands
if link_object(f".run/obj40/{lib}/{nm}", a, name=nm, exe_bytes=exe,
vram_base=vram_base)["ok"]),
cands[0])
lib, nm = pick
shutil.copy(f".run/obj40/{lib}/{nm}", f"{dst}/{nm}")
n += 1
print(f"snd_used: {n} objects (libspu+libsnd combined; {len(EXCLUDE_ADDR)} addresses excluded: "
f"{[hex(x) for x in sorted(EXCLUDE_ADDR)]})")
if __name__ == "__main__":
main()