Verification of the D69 sidecars against a live run surfaced two more narrow ABI layout bugs, both the same MIPS-BE/x86-LE class as D78/D79: - D83: StandTileHeaderMid/StandTileHeaderTail bitfield packing. N64/MIPS packs the first-declared bitfield into the high bits; x86 GCC packs it into the low bits. tile->tail.hdrTail.pointCount (read pervasively for tile-size navigation) silently read the wrong nibble on PC, producing an out-of-range list_of_tilesizes index and an infinite loop in stanBuildRoomData. Fixed under #ifdef PORT by declaring both structs' fields in reverse order (verified via probe: byte-identical numeric result to N64). - D84: bgLoadRoomVtxData/bgLoadRoomPrimaryGdl/bgLoadRoomSecondaryGdl each hand-roll the BG_SEG_TO_PTR segment fold instead of calling the macro, and did it as real 64-bit pointer arithmetic -- the "+0xf1000000" doesn't wrap at 32 bits the way the macro's (u32) cast does, so the computed file offset came out ~4 GiB too large. Fixed under #ifdef PORT by doing the fold as plain u32 math at all three sites, matching BG_SEG_TO_PTR exactly. With D83+D84, load_bg_file and stanBuildRoomData complete without fault and the game proceeds into room streaming, where it hit a further crash in bgBuildRoomVtxBounds: the room primary/secondary DL binaries decompress correctly but texLoadFromGdl (the already-working model-GDL runtime converter) doesn't produce valid GBI opcodes from their content (D85, not yet root-caused -- likely a BG-specific GDL marker convention). Added a PORT-only bounds check before every vtx[] dereference so a garbage command stream can't segfault the process (produces an empty bounding box for that batch instead) -- a crash-safety net, not a geometry fix. The original D69 bug (load_bg_file faulting instantly on stage load) is now fully resolved and verified byte-for-byte against BUNKER1's stan tiles. Two new, separate issues surfaced during verification remain open: D85 (room geometry decodes wrong) and D86 (a new crash in modelInitRwData during room-triggered model instantiation, likely unrelated to bg/stan -- see docs/HANDOFF.md). Debug probes GE_D69STAN/GE_D69BB added (stan.c/ bg.c, env-gated, zero cost when unset) and kept for the next session. Docs: PCPortResearch.md D83-D86, HANDOFF.md and AGENTS.md status rewritten to reflect current state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C3xib6jjMV8o2iE4JfixAo
6.3 KiB
Handoff brief — GoldenEye 007 PC port (Phase 2: Session J — D69 resolved, D85/D86 open)
Paste-ready brief. Authoritative context: AGENTS.md,
docs/PCPortResearch.md §F (D69, D78-D86), docs/BRIEF-D69-stage-load.md.
Where things stand
D69 (the "stage load faults" milestone blocker) is RESOLVED and
verified. load_bg_file (bg.c) no longer faults on BUNKER1 (or any of
the 20+ other levels the offline converter covers). This took five
findings, all committed:
- D78
StandTile.id/.roombitfield packing (MIPS-BE vs x86-LE) —#ifdef PORTlayout fix insrc/bondtypes.h. - D79
bg_room_data's 3 offset fields declaredvoid *(never dereferenced, but grows the record on x86-64 and breaks array indexing) —#ifdef PORTu32fix insrc/game/bg.h. - D80/D81/D82 the bg
.seg+Tbg_*_stanZformat spec, the offline converter (tools_pc/d69_emit.py), and the port-layer sidecar wiring (port/src/pccg.c,port/include/pccg.h, wired intoromdata.c/libultra.c/objecthandler_2.c, same Plan-B pattern aspcmodels.c/D50). - D83
StandTileHeaderMid/StandTileHeaderTailbitfield packing (same MIPS-BE/x86-LE class as D78, found because it caused an actual infinite loop instanBuildRoomDataonce D78-D82 got that far) —#ifdef PORTreversed-field-order fix insrc/bondtypes.h. - D84 three hand-inlined
BG_SEG_TO_PTR-style segment folds inbgLoadRoomVtxData/bgLoadRoomPrimaryGdl/bgLoadRoomSecondaryGdldid the+0xf1000000fold as real 64-bit pointer arithmetic (doesn't wrap at 32 bits the way the macro's(u32)cast does) —#ifdef PORTfix, same fold asBG_SEG_TO_PTR, insrc/game/bg.c.
Regenerate the sidecar with python tools_pc/d69_emit.py ntsc-final
(writes data/pccg-ntsc-final/pccg.bin + manifest.csv, gitignored like
pcmodels-*) before running — it is not checked into git. PAL/JPN are
implemented in the converter but not regenerated or verified (those
ROMs aren't present in this dev environment).
What's still open (found during D69 verification, not the original bug)
D85 (room DL binaries decode to garbage; safety-netted, not fixed).
bgLoadRoomPrimaryGdl/bgLoadRoomSecondaryGdl decompress correctly (the
11 72 RZ header and offset are right — D84 fixed that), but the content
texLoadFromGdl (the same runtime converter already used for model
GDLs) produces from the raw N64 bytes is not valid GBI opcodes. Not
root-caused. A #ifdef PORT bounds check was added in
bgBuildRoomVtxBounds so a garbage command stream can't segfault (it just
produces an empty/degenerate bounding box for that batch) — this is a
crash-safety net, not a geometry fix. Next step: figure out what
texLoadFromGdl actually does with room-specific GBI markers — room GDLs
reference bgApplyDynamicCCRMLUT/ptrDynamic_CC_RM_LUT/
DL_LUT_PRIMARY_ADDFOG, suggesting a CC/RM-LUT-selection marker convention
models don't use — and verify csize_primary_DL_binary/
csize_secondary_DL_binary sizing end-to-end (these come from subtracting
adjacent bg_room_data offsets; D79 argued the +delta cancels in the
subtraction, but that argument hasn't been independently re-verified with
a probe).
D86 (new crash, likely unrelated to bg/stan). With D85's safety net
in, the game reaches a new segfault in modelInitRwData (model.c:6174,
called from modelInit/animInit), dereferencing a bad ModelNode
pointer. This is the existing D50-D58 model pipeline (stable for every
intro/cast model) hitting a case that's never been exercised before —
most likely a room-instantiated object (light fixture / prop / character)
whose load path differs, or a pcmodels-manifest edge case. Not
root-caused. Next step: identify which model name is being loaded at
the crash site (add a probe in load_object_fill_header or
modelInit/animInit printing the header/model name) and check whether
it's in data/pcmodels-ntsc-final/manifest.csv.
Net effect: the game now runs substantially further (through BUNKER1's
full room-streaming setup) before crashing, vs. the original instant fault
in load_bg_file. The "loads without fault" acceptance bar is not yet
fully met — D86 is the current hard blocker to reaching a rendered
frame. Recommended order: D86 first (probably a small, isolated fix, and
unblocks seeing whether D85's degenerate room geometry is otherwise
"plausible pixels" per the D69 bar), then D85, and only then worry about
D75 (pre-existing intro-3D findings, still open, still lower priority).
Debug tooling added this session (kept, env-gated, zero cost when unset)
GE_D69STAN=1—stanBuildRoomData(stan.c): logs the tile walk (address/room/mid/tail/pointCount) for the first 20 tiles and any tile whose derived tile-size would be 0 (the D83 symptom).GE_D69BB=1—bgBuildRoomVtxBounds/bgLoadRoomPrimaryGdl(bg.c): logs the room's expanded-GDL pointer, vertex-buffer pointer, first 8 decoded Gfx slots, and the compressed/decompressed primary-DL bytes around the fold (D84/D85 diagnostic).- Pre-existing
GE_D69=1(ob.c,obLoadBGFileBytesAtOffset) — left in place (HANDOFF previously said to strip it once D69 is solved; D69's original bug is solved, but D85/D86 are still open follow-ups in the same overall load path, so it stays useful for now).
Environment / build
export PATH="/c/msys64/mingw64/bin:$PATH" && ./build-pc.sh ntsc-final(~5 s). Build is GREEN with all D69 work included.- Run from the repo root, not
build-pc/. GE_PCDUMP="2100-2700:10"+tools_pc/pixcount.pyfor frame captures once D86 is fixed and a frame actually renders past stage load.- Crash log:
ge007.crash.log(repo root); symbolicate withaddr2line -e build-pc/ge007.x86_64.exe -f -C <pc>(image base0x140000000). - gdb is launch-mode only and far too slow for timing-dependent bugs;
prefer env-gated probes + the crash log (see the D85/D86 probes above
for the pattern:
#if defined(PORT)/getenv("GE_...")).
Non-negotiables (unchanged, see AGENTS.md)
- N64 build files untouched.
- Game logic unmodified except narrow, documented
#ifdef PORTABI/layout exceptions (D78/D79/D83/D84 this session, all logged inPCPortResearch.md§F). - Offline sidecar conversion preferred over runtime fixup — D80-D82 follow the D43/pcmodels pattern exactly.