mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-24 00:02:45 +02:00
emit_pad()'s widened-stan zero-fill assigned a 4-byte literal to an 8-byte bytearray slice. Python slice-assign with a short RHS silently deletes the surplus bytes, so `out` shrank 4 B per pad+boundpad; once len(out) dropped below a later leaf write's target offset, the write clamped to empty-at-end and inserted there, drifting/truncating the boundpad plink-name blob (Aztec pad33 "p138d2" -> "8d2") -> stanPackId reject -> NULL stan -> door model=NULL -> crash propobj.c:13601. Fix: 8 NUL bytes (only width-mismatched out[ write in the file). All 21 Usetup*Z sidecars regenerated. Aztec (-level_28) now PASSES (was C3 CRASH); Bunker1/Silo unregressed; d125_check.py 21/21 MATCH. Sweep 12 -> 13/21. Bunker2 (-level_27) still crashes: separate DOOR-tail linkedDoor layout bug in d88_propdefs.py (C3r) -- linkedDoor is resolved from linkedDoorOffset (read-before-write int id, D123 pattern). Folded into the converter write-audit. M-16 probes reverted; d125_check.py kept as the offline regression guard. Generalisable quirk added to PORT-LEARNINGS.md §C.
56 lines
2.2 KiB
Python
56 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""D125 diagnostic: does the propDefs blob in the *emitted* pccg sidecar
|
|
(after RZ round-trip) byte-match tools_pc/d88_propdefs.convert_stream()?
|
|
|
|
Tests the M-14 claim that the RAM propDefs is "zeros/garbage after record 0".
|
|
If this script says MATCH, the corruption is downstream of the sidecar
|
|
(runtime rebase / bank alloc / sizepropdef walk); if MISMATCH, it is in
|
|
d88_emit.py's emit/placement or the RZ recompress.
|
|
"""
|
|
import csv, struct, zlib, sys
|
|
from d88_propdefs import convert_stream, PROPDEF_PC_BYTES
|
|
|
|
REGION = "ntsc-final"
|
|
rom = open(f"data/ge007.{REGION}.z64", "rb").read()
|
|
fl = {}
|
|
for r in csv.reader(open("scripts/filelist.u.csv")):
|
|
if len(r) >= 3 and r[2]:
|
|
b = r[2].rsplit("/", 1)[-1]
|
|
b = b[:-4] if b.endswith(".bin") else b
|
|
fl[b] = (int(r[0]), int(r[1]))
|
|
|
|
man = {}
|
|
for r in csv.reader(open(f"data/pccg-{REGION}/manifest.csv")):
|
|
if r and r[0].startswith("Usetup"):
|
|
man[r[0]] = (int(r[1]), int(r[2]))
|
|
sidebin = open(f"data/pccg-{REGION}/pccg.bin", "rb").read()
|
|
|
|
be = lambda b, o: struct.unpack_from(">I", b, o)[0]
|
|
targets = sys.argv[1:] or sorted(man)
|
|
for nm in targets:
|
|
a, s = fl[nm]
|
|
dec_n64 = zlib.decompress(rom[a:a + s][2:], -15)
|
|
pd_n64, intro_n64 = be(dec_n64, 12), be(dec_n64, 8)
|
|
want, n64len, pclen = convert_stream(dec_n64, pd_n64, intro_n64)
|
|
|
|
off, size = man[nm]
|
|
dec_pc = zlib.decompress(sidebin[off:off + size][2:], -15)
|
|
# PC header: 8-byte slots, low 4 = LE offset
|
|
pd_pc = struct.unpack_from("<I", dec_pc, 8 * 3)[0] # propDefs = field 3
|
|
intro_pc = struct.unpack_from("<I", dec_pc, 8 * 2)[0] # intro = field 2
|
|
got = dec_pc[pd_pc:pd_pc + len(want)]
|
|
|
|
if got == want:
|
|
print(f"MATCH {nm:18} pd@{pd_pc:#x} len={len(want)}")
|
|
else:
|
|
# first differing byte + which record it lands in
|
|
d = next((i for i in range(min(len(got), len(want))) if got[i] != want[i]), None)
|
|
rec, o = 0, 0
|
|
while o < (d or 0):
|
|
t = want[o + 3]
|
|
o += PROPDEF_PC_BYTES.get(t, 4)
|
|
rec += 1
|
|
print(f"MISMATCH {nm:18} pd@{pd_pc:#x} want={len(want)} got={len(got)} "
|
|
f"first diff @+{d} (~record {rec}, off {o:#x}); "
|
|
f"gotlen_vs_intro: {intro_pc - pd_pc} vs pclen {pclen}")
|