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
This commit is contained in:
Drew T
2026-06-15 20:55:28 -06:00
parent 5e0d80a887
commit cb7a280994
4 changed files with 63 additions and 27 deletions
+22 -13
View File
@@ -28,13 +28,12 @@ import os, re, subprocess, sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from psyq_link import section_table # noqa: E402
VRAM_BASE = 0x8000F800
def placements(elf_dir, win):
def placements(elf_dir, win, vram_base, exe):
cmd = ["python3", "tools/psyq_identify.py", elf_dir]
if win:
cmd += [f"0x{win[0]:X}", f"0x{win[1]:X}"]
cmd += ["--vram-base", hex(vram_base), "--exe", exe]
objs = []
for ln in subprocess.check_output(cmd, text=True).splitlines():
m = re.match(r"\s+0x([0-9A-Fa-f]+)\s+(\S+\.o)\s+\((\d+) ins\)", ln)
@@ -56,13 +55,23 @@ def contiguous_blocks(objs):
def main():
if len(sys.argv) not in (6, 8):
sys.exit(__doc__)
elf_dir, libbase, gamebase = sys.argv[1], sys.argv[2], sys.argv[3]
rlo, rhi = int(sys.argv[4], 0), int(sys.argv[5], 0)
win = (int(sys.argv[6], 0), int(sys.argv[7], 0)) if len(sys.argv) == 8 else None
import argparse
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("elf_dir")
ap.add_argument("libbase")
ap.add_argument("gamebase")
ap.add_argument("region_lo")
ap.add_argument("region_hi")
ap.add_argument("window", nargs="*", help="optional placement window: win_lo win_hi")
ap.add_argument("--vram-base", default="0x8000F800", help="fileoff->vram delta (EXE default)")
ap.add_argument("--exe", default="extracted/retail/SLUS_007.26", help="target binary (EXE default)")
a = ap.parse_args()
elf_dir, libbase, gamebase = a.elf_dir, a.libbase, a.gamebase
rlo, rhi = int(a.region_lo, 0), int(a.region_hi, 0)
win = (int(a.window[0], 0), int(a.window[1], 0)) if len(a.window) >= 2 else None
vram_base = int(a.vram_base, 0)
objs = placements(elf_dir, win)
objs = placements(elf_dir, win, vram_base, a.exe)
inreg = [o for o in objs if rlo <= o[0] < rhi]
skipped = [o for o in objs if not (rlo <= o[0] < rhi)]
if not inreg:
@@ -88,18 +97,18 @@ def main():
for lo, hi, b in ranges:
if lo > pos:
nm = gname()
out.append(f" - [0x{pos - VRAM_BASE:X}, c, {nm}]"
f" # game code (vram 0x{VRAM_BASE + (pos - VRAM_BASE):08X}-0x{lo:08X})")
out.append(f" - [0x{pos - vram_base:X}, c, {nm}]"
f" # game code (vram 0x{vram_base + (pos - vram_base):08X}-0x{lo:08X})")
li += 1
sub = f"{libbase}{li}"
stubs.append(sub)
names = f"{b[0][1]}..{b[-1][1]}" if len(b) > 1 else b[0][1]
out.append(f" - [0x{lo - VRAM_BASE:X}, c, {sub}]"
out.append(f" - [0x{lo - vram_base:X}, c, {sub}]"
f" # {libbase} block {li}: {len(b)} obj ({names}) vram 0x{lo:08X}-0x{hi:08X}")
pos = hi
if pos < rhi:
nm = gname()
out.append(f" - [0x{pos - VRAM_BASE:X}, c, {nm}]"
out.append(f" - [0x{pos - vram_base:X}, c, {nm}]"
f" # game code (vram 0x{pos:08X}-0x{rhi:08X})")
print("\n".join(out))
+16 -5
View File
@@ -11,22 +11,32 @@ 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 (threaded into psyq_identify +
# link_object, which require them post-T8). RLO/RHI are the EXE's 800c2 region (EXE-specific).
EXE = "extracted/retail/SLUS_007.26"
VRAM_BASE = 0x8000F800
RLO, RHI = 0x80061F38, 0x80062888
def place(lib):
out = subprocess.check_output(["python3", "tools/psyq_identify.py", f".run/obj40/{lib}"], text=True)
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)
return {m.group(2): int(m.group(1), 16)
for ln in out.splitlines()
if (m := re.match(r"\s+0x([0-9A-Fa-f]+)\s+(\S+\.o)\s+\((\d+) ins\)", ln))}
def main():
exe = open(EXE, "rb").read()
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 ("libapi", "libcard"):
for nm, a in place(lib).items():
for nm, a in place(lib, exe_path, vram_base).items():
byaddr[a].append((lib, nm))
dst = ".run/obj40/apicard_used"
shutil.rmtree(dst, ignore_errors=True)
@@ -36,7 +46,8 @@ def main():
if not (RLO <= a < RHI):
continue
lib, nm = next(((l, m) for l, m in byaddr[a]
if link_object(f".run/obj40/{l}/{m}", a, name=m, exe_bytes=exe)["ok"]),
if link_object(f".run/obj40/{l}/{m}", a, name=m, exe_bytes=exe,
vram_base=vram_base)["ok"]),
byaddr[a][0])
shutil.copy(f".run/obj40/{lib}/{nm}", f"{dst}/{nm}")
n += 1
+17 -5
View File
@@ -22,13 +22,18 @@ 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):
out = subprocess.check_output(["python3", "tools/psyq_identify.py", f".run/obj40/{lib}"], text=True)
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)
@@ -38,10 +43,16 @@ def place(lib):
def main():
exe = open(EXE, "rb").read()
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).items():
for nm, a in place(lib, exe_path, vram_base).items():
byaddr[a].append((lib, nm))
dst = ".run/obj40/snd_used"
@@ -53,7 +64,8 @@ def main():
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)["ok"]),
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}")
+8 -4
View File
@@ -145,10 +145,14 @@ def inject(dstpath, movepath):
def main():
if len(sys.argv) >= 5 and sys.argv[1] == "trim":
trim(sys.argv[2], int(sys.argv[3], 0), int(sys.argv[4], 0), sys.argv[5])
elif len(sys.argv) == 4 and sys.argv[1] == "inject":
inject(sys.argv[2], sys.argv[3])
global SYMS_PATH
args = sys.argv[1:]
if "--symbols" in args: # Phase 9: per-binary symbol file (default = the EXE's)
k = args.index("--symbols"); SYMS_PATH = args[k + 1]; del args[k:k + 2]
if len(args) >= 5 and args[0] == "trim":
trim(args[1], int(args[2], 0), int(args[3], 0), args[4])
elif len(args) == 3 and args[0] == "inject":
inject(args[1], args[2])
else:
sys.exit(__doc__)