D115: THROWMTX/THROWPOS/THROWPREV in gunfire.c were raw byte offsets ((u8*)g_CurrentPlayer + handnum*sizeof(struct hand) + 0xAD8/0xB08/0xB48). Both the stride (PC sizeof(struct hand) ~0x968 vs N64 0x3B8, D102) and the base offset are N64-sized, so on x86-64 the address lands inside the inline gait Model / bondhead-matrix region of struct player and matrix_4x4_copy(THROWMTX,..) scribbles 64 bytes of live render state on every casing-ejecting shot. The offsets are exactly hands[handnum].throw_item_pos_related[_prev] and its translation row - macros now use those field accessors under #ifdef PORT; N64 build unchanged. Full survey in docs/AUDIT-M6-player-offsets.md (10 sites, 3 HIGH fixed here, 2 MED open incl. the likely 'weapon model doesn't draw' cause). D113 (investigation, no code change): the 'portal-BFS under-reach / void' premise is stale - the BFS output is correct for BUNKER1's topology (rooms 26/27 are behind PORTALFLAG_DISABLED closed-door portals, culled identically on N64). The void is the closed-door models obj 153/154 not rendering in their doorway -> D114 track. D114 (investigation, no code change): joint-matrix chain + d43_emit.py float emission verified clean post-D112; basemtx is a proper right-handed +Y-up lookat, joint render_pos coherent and upright. All plan hypotheses disproven. Residual = a horizontal mirror in the shared port/fast3d viewport / MP-matrix path vs GE's left-handed screen convention (also explains the open D75 mirrored-text / logo items). Still open. Plan + agent reports: docs/PLAN-M6-playable.md. §F: D113/D114/D115.
4.6 KiB
Plan — Session M-6: toward a playable, low-glitch BUNKER1 PC build
Baseline: D112 committed (67d674da). Build green. -level_09 renders
BUNKER1 continuously, crash-free. Three tracks below, in priority order.
Each track: investigate → root-cause → narrow #ifdef PORT fix or
write-up per AGENTS.md non-negotiables (no game-logic changes; ABI /
layout / format exceptions only, each documented in
docs/PCPortResearch.md §F).
Track 1 — Portal-BFS under-reach ("the void") [highest visual impact]
Symptom: in BUNKER storage area (rooms 27–29) the per-frame visible room count collapses to 1–2; large sky-fill void where an adjacent room should show through an open doorway. Portal visibility is otherwise healthy post-D106 (room 18 → 8 rooms deep on enabled doorways).
Known-good / ruled out: currentPlayerGetProjectionMatrix(),
g_CurrentPlayer->screensize, the intentional g_RoomLoadBudget 200→3
FP-cam drop, zfar, the plane-side metric cull (bg.c:4135/4144).
Suspects: sub_GAME_7F0B5864 screen-space portal projection
(bg.c:1619) — sibling of the D106 z==0 near-plane case, but for
partially-off-screen (not straddling) portals; bgQueuePortalTraversal
(bg.c:3814/3843) depth/dedup; bgDetermineVisibleRooms (bg.c:4615)
seed set; sub_GAME_7F0B5528 clip-point generation (bg.c:1551);
transform3Dto2DWithZScaling (bondview.c:730).
Deliverable: root-cause writeup in §F ("D113"); if a narrow PORT fix is obviously correct (e.g. another degenerate-box / clamp mismatch), apply it, else stop and document.
RESULT (Track 1 agent): NOT a portal-BFS bug. The BFS output is
correct for BUNKER1's topology — rooms 26/27 sit behind portals 25/26
which carry PORTALFLAG_DISABLED (closed doors), culled identically on
N64. Stable 3 visible rooms {29,28,25}, not "1–2". The void is the
closed-door prop models (obj 153/154) rendering with a wrong world
transform — their leaf DLs are emitted (GE_D96) but land outside the
doorway, leaving the wall hole unoccluded → sky. Folds into Track 2
(same model-matrix defect as the inverted guard / mislocated crate). No
bg.c change. Probes reverted, tree clean.
Track 2 — Character pose / orientation
Symptom: post-D112 a BUNKER guard renders as a coherent humanoid but appears inverted. Also a mispositioned static crate prop (top-left in the control room).
Known-good: joint rotations from the anim bitstream; render_pos[]
positions now sane (~±60 units) after the D112 f32 fix; basemtx from
camGetWorldToScreenMtxf() (bondview.c:824) was a clean orthonormal
lookat.
Suspects: matrix handedness / lookat sign in the joint base matrix;
modelBuildGroupMatrices / process_02_position / subcalcmatrices
composition order on LE; a converter sign/axis issue in d43_emit.py
group Origin or matrix rodata that survived the D112 fix (byte order
right, axis convention wrong); guMtxF2L / Mtxf→Mtx path.
Deliverable: root-cause writeup in §F ("D114"); narrow fix if obviously correct.
Track 3 — struct player raw-offset audit [gate to playability]
Symptom: 1P weapon model doesn't draw. Landmine (§F D100): raw
hardcoded-offset accessors into struct player / struct hand are NOT
PORT-adjusted and are already PC-wrong.
Known instances: gunfire.c THROWMTX_OFFSET 0xAD8 / THROWPOS
0xB08 / THROWPREV 0xB48 (all g_CurrentPlayer + handoffset + K);
plus D102 (weapon Model pool) already fixed.
Task: enumerate every raw byte-offset / hardcoded-size access into
struct player, struct hand, and siblings across src/game/ (grep for
+ 0x, handoffset, (u8 *), hardcoded 0x2A80-class sizes, pointer
arithmetic on player). For each: N64 offset vs PC offset (struct is
0x2A8-class larger per D100), whether it's on a BUNKER1-reachable path,
severity. Produce a table + recommended fix order. Do NOT fix yet —
this is a survey feeding a follow-up.
Deliverable: docs/AUDIT-M6-player-offsets.md table + §F pointer.
Execution
- Tracks 1, 2, 3 dispatched as parallel investigation subagents (read-mostly; each returns a writeup + optional narrow patch).
- Integrator (this session) reviews each, applies/adjusts verified narrow
fixes, rebuilds (
export PATH="/c/msys64/mingw64/bin:$PATH" && ./build-pc.sh ntsc-final), spot-checks-level_09, commits per-track with §F docs. - Repro:
./build-pc/ge007.x86_64.exe -level_09 -ml0 -me0 -mgfx100 -mvtx50 -mt700 -ma150. Frame capture:GE_PCDUMP="<range>:<stride>"+tools_pc/pixcount.py/tools_pc/pixcount.py. - Sidecar regen (if a converter changes):
python tools_pc/d43_emit.py ntsc-final.