Files
goldeneye-pc-port/tools_pc/d43_gdldump.py
T
James Dansereau 73ea03734d PC port: phase 2 — D48 review: re-plan D43 as offline pre-conversion (Plan B)
- D48 finding in PCPortResearch.md §F: d43_convert.py does not emit bytes
  (layout+remap validation only); Plan B = Python emit pass -> data/pcmodels-<region>/
  RZ sidecars served via port-layer hw_address/rom_size table patch (no C
  converter); Plan A kept as fallback with corrected staging guard (D_N64+D_PC<=avail)
- Moved all 23 d43_*.py investigation scripts from gitignored build-pc/ to tracked
  tools_pc/ (spec is now versioned); d43_cover.py path ref updated; converter
  re-run from new location: ALL CLEAN
- HANDOFF.md rewritten as two-session prompt: Session A = independent review of
  Plan B (8 claims, each with falsification criterion -> finding D49), Session B =
  execute (emit pass + round-trip validator, romdata plumbing, one-line hook,
  pixel-assert soak); verified-facts converter spec preserved
- AGENTS.md phase status updated to D48 re-plan
2026-08-22 18:43:04 -04:00

93 lines
3.5 KiB
Python

# D43 helper: dump real GDL command streams from model files (N64 layout),
# showing w1 top-byte distribution per opcode. Usage: python d43_gdldump.py <name-substr>
import csv, struct, sys, zlib, os, re
from collections import Counter
ROM = r"data/ge007.ntsc-final.z64"
rom = open(ROM, "rb").read()
rows = list(csv.reader(open(r"scripts/filelist.u.csv")))
hdrfiles = set()
for root, dirs, files in os.walk("assets"):
for fn in files:
if fn.lower().endswith("modelfileheader.inc.c"):
hdrfiles.add(os.path.join(root, fn))
nsnt = {}
for f in sorted(hdrfiles):
for line in open(f):
m = re.search(r"MODELFILEHEADER\((.*)\)\s*;?\s*$", line)
if not m: continue
a = [x.strip() for x in m.group(1).split(",")]
if len(a) < 9: continue
try: nsnt[a[0]] = (int(a[4], 0), int(a[8], 0))
except ValueError: pass
def be32(b, o): return struct.unpack_from(">I", b, o)[0] & 0xFFFFFF
flt = sys.argv[1] if len(sys.argv) > 1 else ""
limit = int(sys.argv[2]) if len(sys.argv) > 2 else 4
shown = 0
for r in rows:
if len(r) < 3 or not r[2]: continue
base = r[2].rsplit("/", 1)[-1]
if base.endswith(".bin"): base = base[:-4]
if not (base[0] in "CGP" and base.endswith("Z") and "_stan" not in base): continue
if flt and flt.lower() not in base.lower(): continue
try: addr, size = int(r[0]), int(r[1])
except ValueError: continue
key = None
for cand in (base, base[1:], base[:-1], base[1:-1]):
if cand in nsnt: key = cand; break
if not key: continue
NS, NT = nsnt[key]
try: out = zlib.decompress(rom[addr:addr + size][2:], -15)
except Exception: continue
D = len(out)
R0 = 4 * NS + 12 * NT
# collect DL / DLCOLLISION record gdl offsets (full walk: Child+Next+Parent)
stack = [R0]; seen = set(); gdls = []
while stack:
o = stack.pop()
if o in seen or o >= D: continue
seen.add(o)
op = struct.unpack_from(">H", out, o)[0] & 0xff
data = be32(out, o + 4)
parent = be32(out, o + 8); nxt = be32(out, o + 0xC)
child = be32(out, o + 0x14)
if op in (4, 24):
p = be32(out, data); s = be32(out, data + 4)
if p: gdls.append((p, "P"))
if s: gdls.append((s, "S"))
for q in (child, nxt, parent):
if q: stack.append(q)
print("== %s D=0x%X NS=%d NT=%d gdls=%d" % (base, D, NS, NT, len(gdls)))
topbytes = Counter(); opcounts = Counter()
for goff, tag in gdls[:2]:
# walk the GDL: commands are 8B slots; stop at ENDDL or invalid opcode
i = goff; n = 0
while i + 8 <= D and n < 400:
w0 = be32(out, i); w1 = be32(out, i + 4)
c = w0 >> 24
opcounts[c] += 1
if c == 0x1d: break # ENDDL
i += 8; n += 1
print(" %s @0x%X (%d slots, ends at 0x%X)" % (tag, goff, n, i))
for k in range(min(n, 14)):
w0 = be32(out, goff + 8 * k); w1 = be32(out, goff + 8 * k + 4)
print(" [%2d] c=%02X w0=%08X w1=%08X" % (k, w0 >> 24, w0, w1))
for goff, tag in gdls:
i = goff; n = 0
while i + 8 <= D and n < 400:
w0 = be32(out, i); w1 = be32(out, i + 4)
c = w0 >> 24
if c == 0x1d: break
topbytes[(c, (w1 >> 24) & 0xff)] += 1
i += 8; n += 1
print(" opcode/topbyte histogram:")
for (c, tb), cnt in sorted(topbytes.items()):
print(" c=%02X top(w1)=%02X x%d" % (c, tb, cnt))
shown += 1
if shown >= limit: break