forked from Ports/goldeneye-pc-port
D115: fix gunfire THROW* raw struct player offsets; M-6 D113/D114 findings
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.
This commit is contained in:
1 parent
64698f6b23
commit
7bdb19e96f
5 files changed
+342
-13
No files matched your search
@@ -0,0 +1,125 @@
|
||||
# AUDIT-M6 — `struct player` / `struct hand` raw-offset pass
|
||||
|
||||
Survey only (Track 3 of `docs/PLAN-M6-playable.md`). No source changed, no
|
||||
build. Feeds a follow-up fix session. Companion to §F D98/D100/D101/D102.
|
||||
|
||||
## Method
|
||||
|
||||
Grep of `src/` for: `handoffset`, `(u8 *)g_CurrentPlayer`, `g_CurrentPlayer + 0x`,
|
||||
`+ 0x[3-hex]`, `sizeof(struct hand|player)`, `* sizeof(struct`, hardcoded
|
||||
`0x2A80`/`0x2A70`, `->hands[i] +`, punned `->m[` / inline-Model accesses,
|
||||
`bondheadmatrices`, `copy_of_body_obj_header`, `field_59C..field_6CC`,
|
||||
`field_654`, `gaitRwData`. Cross-checked every hit against the live
|
||||
`struct player` / `struct hand` definitions in `src/game/bondview.h`
|
||||
(player @ line 314, hand @ line 119).
|
||||
|
||||
## PC layout facts (from bondview.h + bondtypes.h)
|
||||
|
||||
- `struct hand` (bondview.h:119): N64 `sizeof` ≈ **0x3B8** (base 0x870→0xC28
|
||||
inside `player`). PC adds: `audioHandle` +4, `rocket` (`AttachedObj*`) +4,
|
||||
`mtxlist` (`Mtxf*`) +4, and the D102 `#ifdef PORT` tail
|
||||
`struct Model weaponModel` (~0x2A8) + `u32 weaponRwPool[192]` (0x300).
|
||||
**PC `sizeof(struct hand)` ≈ 0x968** — roughly **+0x5B0** vs N64, and *not*
|
||||
a clean multiple, so `handnum * sizeof(struct hand)` de-syncs for `handnum==1`.
|
||||
- `struct player` pointer growth **below N64 offset 0x594** (contradicts the
|
||||
D100 note that “anything below 0x594 is unaffected”): `cameratile` (0x34),
|
||||
`prop` (0xA8), `bodyModel` (0xD4), `autoaim_target_y` (0x130),
|
||||
`autoaim_target_x` (0x140) → **+0x14** by offset ~0x144. `field_10C4`..
|
||||
and the `bloodImg*` / `InvItem*` / `textoverride*` blocks add much more
|
||||
higher up.
|
||||
- `model` (N64 0x598): D100 made it an **inline `struct Model` (~0x2A8)**
|
||||
replacing an 8-byte pointer → **+0x2A0** for everything after it, before
|
||||
`field_59C..field_6CC` (which D100 grep-verified dead except `field_654`).
|
||||
- `ptr_hand_weapon_buffer[2]` +0x8; `copy_of_body_obj_header[2]` — inline
|
||||
`ModelFileHeader` grew 0x20→0x30 (4 pointers ×+4) → **+0x20**;
|
||||
`item_related[2]` `struct texpool` pointer growth; `bondheadmatrices` /
|
||||
`viewports` unchanged.
|
||||
- Net delta from `player` base to `hands[]`: **≈ +0x2E0** (0x14 + 0x2A0 +
|
||||
0x08 + 0x20 + texpool). So N64 `hands[0]` @ 0x870 is at PC **≈ 0xB50**;
|
||||
N64 `hands[0].throw_item_pos_related` @ 0x870+0x268 = **0xAD8** is at PC
|
||||
**≈ 0xDB8**.
|
||||
|
||||
## Findings table
|
||||
|
||||
| # | file:line | accessor | targets (N64 field) | N64 off | PC off (real) | PORT-adj? | BUNKER1 FP reachable? | severity | recommended fix |
|
||||
|---|-----------|----------|---------------------|---------|---------------|-----------|----------------------|----------|-----------------|
|
||||
| 1 | `src/game/gunfire.c:4960` `THROWMTX` (macro) used @ 5043,5047,5070,5126,5360,5364,5387,5443 | `(Mtxf*)((u8*)g_CurrentPlayer + handoffset + 0xAD8)` (`0xAD0` EU) | `hands[handnum].throw_item_pos_related` | 0xAD8 (h0) / 0xAD8+0x3B8 (h1) | ≈0xDB8 (h0) / ≈0xDB8+0x968 (h1) | **NO** | **YES** — `sub_GAME_7F068508` runs on every shot with a cartridge (PP7/KF7/…), solo only (`getPlayerCount()>=2` early-out) | **HIGH — memory corruption.** Raw 0xAD8 on PC lands inside the inline `model` / `field_59C` region of `struct player`; `matrix_4x4_copy(THROWMTX,…)` writes 64 bytes there. `handnum==1` (dual wield) additionally uses the wrong stride. Corrupts the gait `Model` / bondhead matrices → wrong Bond-body render or a delayed fault in model code. | Replace macro body with `(&g_CurrentPlayer->hands[handnum].throw_item_pos_related)`. Drop `THROWMTX_OFFSET`, `handoffset`. |
|
||||
| 2 | `src/game/gunfire.c:4961` `THROWPOS(k)` used @ 5112-5114, 5164-5166, 5429-5431, 5481-5483 | `((f32*)((u8*)g_CurrentPlayer + handoffset + 0xB08))[k]` (`0xB00` EU) | `hands[handnum].throw_item_pos_related.m[3][k]` (translation row) | 0xB08 | ≈0xDE8 | **NO** | **YES** (same path as #1) | **HIGH — silent-wrong + reads corrupted memory.** Feeds `casing->vel` from the frame-to-frame delta of a garbage location → casings fly wrong / NaN, and the read is OOB-ish relative to intent. | `g_CurrentPlayer->hands[handnum].throw_item_pos_related.m[3][k]` |
|
||||
| 3 | `src/game/gunfire.c:4962` `THROWPREV(k)` used @ 5112-5114, 5164-5166, 5429-5431, 5481-5483 | `((f32*)((u8*)g_CurrentPlayer + handoffset + 0xB48))[k]` (`0xB40` EU) | `hands[handnum].throw_item_pos_related_prev.m[3][k]` | 0xB48 | ≈0xE28 | **NO** | **YES** (same path) | **HIGH** (same as #2) | `g_CurrentPlayer->hands[handnum].throw_item_pos_related_prev.m[3][k]` |
|
||||
| 4 | `src/game/gunfire.c:5031` and `:5348` | `handoffset = handnum * sizeof(struct hand)` | array stride for `hands[handnum]` | 0x3B8 | 0x968 (and non-uniform) | **NO** (the raw math is only consumed by #1–#3) | YES | **HIGH** — root of #1–#3’s `handnum==1` error; harmless once #1–#3 use `hands[handnum].field`. | delete; fold into the field accessors above. |
|
||||
| 5 | `src/game/gunfire.c:600-661` weapon-model setup (`copy_of_body_obj_header[handnum]` → `modelInit(HAND_WEAPON_MODEL, mdlhdr, HAND_WEAPON_RWPOOL)`; `hand->mtxlist = rwmtx`; `render_pos = (RenderPosView*)rwmtx`) | field-name access + D102 macros | `hands[h].weaponModel` / `weaponRwPool` / `mtxlist`; `copy_of_body_obj_header[h]` (inline `ModelFileHeader`) | — | — | **YES (D102)** for the Model/pool pun; `copy_of_body_obj_header` is a plain inline struct (layout-consistent) | YES — this *is* the 1P weapon draw path | **MED — prime suspect for “weapon model doesn’t draw.”** `rwmtx` is `dynAllocate`’d (transient per-frame arena) but `weaponModel.render_pos` is set to it once at setup and consumed at render (`gunRenderFirstPersonGunModels:1605`); on N64 `render_pos` aliased the *persistent* `mtxlist` field, here it can point at reused arena memory. Also `flashvisptr = HAND_WEAPON_RWPOOL + node->…RwDataIndex` (gunfire.c:640) assumes N64 word indices into a pool whose PC records are ~2×. | Verify `dynAllocate` lifetime vs. render; consider persisting the matrix list. Re-check `weaponRwPool[192]` sizing against `modelCalculateRwDataLen(mdlhdr)` on PC. Confirm `subdraw` matrix path (shared with Track 2). |
|
||||
| 6 | `src/game/bondview2.c:3155-3162` (PORT branch of `sub_GAME_7F07E7CC`) | `(Model*)&g_CurrentPlayer->something_with_watch_object_instance`; `static u8 watchRwPool[0xC8]`; `g_CurrentPlayer->step_in_view_watch_animation = 0` | watch-menu item preview Model punned at N64 player+0x230, pool at +0x2EC | 0x230 / 0x2EC | 0x244 / 0x300-ish | **YES (D56)** but suspect | YES — open watch menu / cycle to an item page | **MED — under-reservation.** D56 hosts the pool in a `0xC8` static (N64 capacity) but PC RW records are wider; and `animInit` writes a full inline `struct Model` (~0x2A8) starting at `&something_with_watch_object_instance`, overrunning `field_234..watch_scale_destination..` (N64 those were the tail of the 0xBC Model hole; on PC they are live watch fields). | Give the watch Model real inline storage in `struct player` under `#ifdef PORT` (D100 pattern) + size `watchRwPool` from `modelCalculateRwDataLen`. |
|
||||
| 7 | `src/game/gunfire.c:4951-4957` | `#define THROWMTX_OFFSET 0xAD8` etc. | (the constants behind #1–#3) | — | — | NO (EU/non-EU split only, both N64) | — | INFO — remove with #1–#3. | delete block. |
|
||||
| 8 | `src/game/player.c:136` | `mempAllocBytesInBank((sizeof(struct player)+0xF)&~0xF, …)` | player allocation | — | — | **YES (D98)** | — | OK — listed for completeness; correct. | none |
|
||||
| 9 | `src/game/initBondDATAdefaults.c:103` | `animInit(&g_CurrentPlayer->model, &player_gait_object_header, g_CurrentPlayer->gaitRwData)` | inline gait Model + `gaitRwData[256]` | — | — | **YES (D100)** | YES | OK. | none |
|
||||
| 10 | `src/game/bondhead.c:217-480`, `bondview2.c:922-9102` | `&g_CurrentPlayer->model`, `bondheadmatrices[0].m[..]` (field-name) | inline gait Model + head matrices | — | — | **YES** (D100 makes field access correct; single TU layout) | YES | OK — no raw offsets; safe as long as D100 inline-Model stays. | none |
|
||||
|
||||
### Not player/hand but same class (noted, out of scope for the fix list)
|
||||
|
||||
- `src/game/gunfire.c:5513` `render_data.mtxlist = (Mtxf*)model_matrices` — local, fine.
|
||||
- `initexplosioncasing.c`, `initobjects.c`, `propobj.c`, `glass2.c`,
|
||||
`initunk_005520.c` — all `N * sizeof(struct X)` allocations, already
|
||||
`sizeof`-correct (D57 covered the rwdata capacities).
|
||||
- `ramromreplay.c` `+0x110/+0x1F8/+0x21E` — `ramromfilestructure`, handled by D66/D87, unrelated.
|
||||
|
||||
## Total
|
||||
|
||||
**10 distinct offset sites; 3 live HIGH-severity bugs (findings #1–#3, one
|
||||
underlying cause #4), 1 dead constant block (#7), 2 MED items to verify
|
||||
(#5, #6), 4 already-correct (#8–#10).**
|
||||
|
||||
All 3 HIGH bugs are the same construct in `gunfire.c` and are reachable in
|
||||
BUNKER1 first-person the instant the player fires a weapon that ejects a
|
||||
casing (every ballistic weapon). They are NOT reachable before the first
|
||||
shot, so they do not explain a weapon model that is missing from frame 1 —
|
||||
but once firing starts they corrupt the inline gait `Model` region and will
|
||||
produce cascading render/anim breakage or a fault.
|
||||
|
||||
## Where the “1P weapon model doesn’t draw” bug most likely lives
|
||||
|
||||
Not in a surviving raw offset — D102 already gave the weapon `Model` and its
|
||||
RW pool dedicated storage, and `gunRenderFirstPersonGunModels` /
|
||||
`gunTickHandState` use field names. Best candidates, in order:
|
||||
|
||||
1. **Finding #5 — the D102 wiring in `gunfire.c:600-661`.** `weaponModel.render_pos`
|
||||
is pointed at a `dynAllocate`’d transient (`rwmtx`) that on N64 aliased the
|
||||
*persistent* `hand->mtxlist` field; if the arena is recycled between the
|
||||
hand-tick setup and `gunRenderFirstPersonGunModels`, the model draws with
|
||||
garbage / off-screen matrices (looks like “not drawn”). Check `dynAllocate`
|
||||
lifetime and whether `weaponModel` should own its matrix list on PC.
|
||||
2. **The `subdraw` / `subcalcmatrices` / `process_02_position` matrix
|
||||
composition** — shared with Track 2 (inverted characters). A handedness /
|
||||
lookat-sign error there hides the weapon model just as it inverts guards.
|
||||
3. **`field_87F` gating** (`gunfire.c:520`, `:587-598`) — if the setup branch
|
||||
that sets `field_87F = 1` and calls `modelInit` isn’t taken (e.g.
|
||||
`get_ptr_weapon_model_header_line(item)` returns NULL because
|
||||
`ptr_hand_weapon_buffer` / weapon-model load failed), the render loop
|
||||
`continue`s and nothing is emitted. Cheap to instrument.
|
||||
|
||||
## Recommended fix order
|
||||
|
||||
1. **#1–#4 & #7 (gunfire.c THROW macros)** — one mechanical change, no layout
|
||||
work: rewrite `THROWMTX`/`THROWPOS`/`THROWPREV` as
|
||||
`g_CurrentPlayer->hands[handnum].throw_item_pos_related[_prev]` field
|
||||
accesses (`.m[3][k]` for the POS/PREV variants), delete `handoffset` and
|
||||
the `THROW*_OFFSET` defines. Wrap in `#ifdef PORT` keeping the N64 macro
|
||||
for the N64 build (or, since the field access is identical semantics on
|
||||
both, use it unconditionally — verify byte-match). Log as §F “D115”.
|
||||
2. **#5** — investigate the weapon-model `render_pos` / `mtxlist` lifetime and
|
||||
`weaponRwPool` sizing; this is the likely playability blocker. May fold
|
||||
into the Track-2 matrix work.
|
||||
3. **#6** — give the watch preview Model inline PORT storage + sized pool
|
||||
(only matters once the player opens the watch in-level).
|
||||
4. Add a debug assert in `gunfire.c` weapon-model setup logging
|
||||
`mdlhdr->numRecords` vs `weaponRwPool` capacity and `field_87F` state, to
|
||||
settle candidate 3 above.
|
||||
|
||||
## §F pointer
|
||||
|
||||
Add to `docs/PCPortResearch.md` §F after D112: *“**D115 (audit)** —
|
||||
`struct player`/`struct hand` raw-offset pass: see
|
||||
`docs/AUDIT-M6-player-offsets.md`. Live HIGH bugs = `gunfire.c` THROWMTX/POS/PREV
|
||||
macros (`+handoffset+0xAD8/0xB08/0xB48`), not PORT-adjusted; fix = field
|
||||
accessors. MED = D102 weapon `render_pos` transient-arena aliasing (likely
|
||||
the ‘weapon model doesn’t draw’ cause) and the D56 watch-Model pool
|
||||
under-reservation.”*
|
||||
+27
-13
@@ -54,20 +54,34 @@ screens draw content. See §F "D108–D112".
|
||||
complete leaf DLs (~15k/run, valid ptrs; fast3d transforms them). The
|
||||
storage-room void is NOT closed doors failing to render.
|
||||
|
||||
**Real remaining work, in order:**
|
||||
1. **Portal-BFS under-reach in the storage area** — per-frame visible
|
||||
room count drops to 1–2 in rooms 27–29 (`GE_D104`/probes now removed).
|
||||
`projmatrix` + `screensize` verified valid; the 200→3 `g_RoomLoadBudget`
|
||||
drop is intentional N64 FP-cam behaviour. Likely `bgQueuePortalTraversal`
|
||||
/ `bgDetermineVisibleRooms` portal-projection. This is the void.
|
||||
2. **Character pose / orientation polish** — post-D112 a BUNKER guard
|
||||
renders but appears inverted; suspect a matrix-handedness / lookat
|
||||
sign item in the joint base matrix. Also the mispositioned static
|
||||
crate prop (top-left in the control room).
|
||||
3. **`struct player` offset pass** — weapon model doesn't draw; gate to
|
||||
playability (landmine below + D102).
|
||||
**Real remaining work, in order (updated session M-6):**
|
||||
1. **Shared fast3d screen mirror (D114 → D75 class)** — the single most
|
||||
likely cause of: inverted BUNKER guard, mispositioned Nintendo logo,
|
||||
mirrored front-end text, AND the storage-room "void" (D113 proved the
|
||||
portal BFS is correct — the void is the closed-door models obj 153/154
|
||||
rendering outside their doorway). The joint-matrix chain and the model
|
||||
converter were **verified clean** in M-6 (see §F D114); the residual is
|
||||
an X (± Y) flip in `port/fast3d`'s viewport / MP-matrix path vs GE's
|
||||
left-handed screen convention (`gmain.s` / `bondview.c:726`). Suspects:
|
||||
`gfx_calc_and_set_viewport` / `gfx_adjust_viewport_or_scissor`
|
||||
(`gfx_pc.cpp:1739/1771`), `MP_matrix` (`gfx_pc.cpp:1122`). **Rebuild
|
||||
with D112+D115 and eyeball a guard first** — D115 stops a per-shot
|
||||
64-byte scribble that could itself present as "inverted".
|
||||
2. **Weapon model doesn't draw (D115 #5)** — D102 pointed `weaponModel.
|
||||
render_pos` at a transient `dynAllocate` arena; on N64 it aliased the
|
||||
persistent `hand->mtxlist`. Gate to playability. May fold into #1.
|
||||
3. **Rest of the `struct player` offset pass** — D115 fixed the HIGH
|
||||
`gunfire.c` THROW* bugs; #6 (watch-preview Model pool) + the broader
|
||||
audit remain (`docs/AUDIT-M6-player-offsets.md`).
|
||||
4. Add an f32 value spot-check to `d43_emit.py`'s verify pass (it only
|
||||
checks pointers/opcodes today — this bug passed "ALL CHECKS PASSED").
|
||||
checks pointers/opcodes today — the D112 bug passed "ALL CHECKS PASSED").
|
||||
|
||||
**Session M-6 (this session) — see §F D113/D114/D115, `docs/PLAN-M6-playable.md`,
|
||||
`docs/AUDIT-M6-player-offsets.md`, `docs/AGENT-WORKFLOW.md`,
|
||||
`docs/PORT-LEARNINGS.md`.** D113: portal BFS is correct, not the void.
|
||||
D114: matrix chain + converter verified clean, residual = shared fast3d
|
||||
mirror (open). D115: player raw-offset audit + `gunfire.c` THROW* fix
|
||||
(uncommitted). Build green.
|
||||
|
||||
**This session's fixes (all committed, master):**
|
||||
|
||||
|
||||
@@ -3441,6 +3441,88 @@ values — a float spot-check is worth adding. Probes `GE_D96GATE /
|
||||
D96DL / D108 / D109 / D110 / D111 / D112 / GE_TRICNT` were all removed
|
||||
after the fix.
|
||||
|
||||
**D113 (session M-6) — "portal-BFS under-reach / storage-room void" was a
|
||||
STALE premise; there is no BFS bug.** A subagent dumped BUNKER1's runtime
|
||||
portal table and traced every accept/reject decision of `sub_GAME_7F0B7F84`
|
||||
(non-EU/LEFTOVERDEBUG copy, `bg.c:4091`; queue = 5-arg `bgQueuePortalTraversal`
|
||||
`bg.c:3843` — confirmed the compiled ones). With the camera in room 29 the
|
||||
BFS visits portals 25,26,27,28: portals 25 (r29↔r26) and 26 (r29↔r27)
|
||||
carry `PORTALFLAG_DISABLED` (`controlbytes1 & 1`) because door props obj
|
||||
153/154 spawn `openPosition==0` → `doorDeactivatePortal`, and nothing
|
||||
re-opens them in the fly-through — so they are culled at `bg.c:4138`, a
|
||||
site that is **not** `#ifdef PORT`-guarded and behaves identically on N64.
|
||||
Result: a stable 3 visible rooms {29,28,25} (D104 probe agrees), which is
|
||||
the correct answer for this topology, not "1–2". No s32/pointer
|
||||
truncation, no `min>max`/NaN reaching an accept flip, no depth-cap
|
||||
(`D_8004489C=0xF`) or visited-set (`>=9`) early-out firing. `sub_GAME_7F0B5864`
|
||||
is fine post-D106. **The void = the two closed-door models (obj 153/154)
|
||||
filling rooms 26/27's doorways not rendering where they should** —
|
||||
`GE_D96` shows room 29's prop pass emits their leaf DLs, so the door
|
||||
geometry exists but its world transform puts it outside the doorway,
|
||||
leaving the wall hole unoccluded → sky. This is the D114 model-transform
|
||||
track, not visibility. No `bg.c` change. Probes reverted.
|
||||
|
||||
**D114 (session M-6) — character inversion / mispositioned crate:
|
||||
OPEN, matrix chain verified sane, residual is a shared fast3d mirror
|
||||
(D75 class).** A subagent statically traced the whole joint-matrix chain
|
||||
(`chr.c:2564` → `subcalcmatrices` → `modelUpdateMatrices` →
|
||||
`process_02_position` `model.c:1662` → `modelBuildGroupMatrices`
|
||||
`model.c:1318` → `matrix_4x4_multiply_homogeneous` →
|
||||
`bondviewTransformManyPosToViewMatrix` → `matrix_4x4_f32_to_s32`
|
||||
`matrixmath.c:495` → `gfx_sp_matrix`) and an env-gated `GE_D114` probe
|
||||
(24 joint builds, 3+ BUNKER guards). Findings: `basemtx` from
|
||||
`camGetWorldToScreenMtxf()` is a proper orthonormal right-handed lookat
|
||||
(+Y up, `up ≈ (-0.09, 0.96, -0.26)`, `right = up × forward`), scaled by
|
||||
`bgGetLevelVisibilityScale()` = 0.1 (also applied to the room path —
|
||||
expected); joint `render_pos` are coherent and **upright** (head y ≈ 280
|
||||
> pelvis ≈ 230 > limb ends ≈ 132); view-space z negative (in front,
|
||||
correct). Converter clean: `put_f32` is now the only float writer and is
|
||||
correct post-D112; `matrix_4x4_f32_to_s32` is byte-identical to
|
||||
`guMtxF2L`; `matrix_4x4_set_lookat` / `guPerspectiveF` /
|
||||
`matrix_4x4_multiply_homogeneous` match N64 and are native-LE. So the
|
||||
plan's hypotheses (converter axis/sign, lookat handedness, compose
|
||||
order, F2L) are all **disproven**. Leading residual: a horizontal (±
|
||||
vertical) mirror in the **shared fast3d viewport / MP-matrix path**, not
|
||||
the model path — consistent with the open D75 notes ("front-end text
|
||||
draws mirrored", "Nintendo logo mispositioned"): a flip is only visible
|
||||
on asymmetric content (humanoid, text, logo) and invisible on a boxy
|
||||
room. GE bakes a left-handed screen convention into its own transforms
|
||||
(`transform3Dto2DCoords`, `bondview.c:726`: `screenX = center −
|
||||
x·invz·scale`); if GE's RSP (`rsp/graphics/gmain.s`) applies an X flip
|
||||
that `port/fast3d` does not, every model and room is mirrored. Suspects:
|
||||
`gfx_calc_and_set_viewport` / `gfx_adjust_viewport_or_scissor`
|
||||
(`gfx_pc.cpp:1739/1771`), `MP_matrix` vertex transform
|
||||
(`gfx_pc.cpp:1122`). This is a fast3d-correctness gap → write-up, not a
|
||||
narrow ABI patch. Also: **rebuild with D112+D115 and eyeball a guard
|
||||
first** — the D115 `gunfire.c` fix stops a per-shot 64-byte scribble into
|
||||
the inline-Model / bondhead-matrix region that could itself have caused
|
||||
"inverted". The crate: static-prop placement comes via a per-prop
|
||||
`basemtx` from the Usetup `PropRecord` position/rotation — check
|
||||
`d88_emit.py` separately from the character question. Probe reverted;
|
||||
re-apply snippet in the M-6 agent report.
|
||||
|
||||
**D115 (session M-6) — `struct player` / `struct hand` raw-offset audit +
|
||||
first fix.** Full survey in `docs/AUDIT-M6-player-offsets.md` (10 offset
|
||||
sites; 3 live HIGH, 1 dead const block, 2 MED, 4 already-correct).
|
||||
**Fixed:** `gunfire.c:4960-4962` `THROWMTX` / `THROWPOS(k)` / `THROWPREV(k)`
|
||||
were raw byte offsets — `(u8*)g_CurrentPlayer + handoffset + 0xAD8/0xB08/
|
||||
0xB48` with `handoffset = handnum * sizeof(struct hand)`. Both the stride
|
||||
(`sizeof(struct hand)` ≈ 0x968 PC vs 0x3B8 N64, 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 shot from a casing-ejecting weapon** (`sub_GAME_7F068508`,
|
||||
solo only). The offsets are exactly
|
||||
`hands[handnum].throw_item_pos_related[_prev]` and its translation row, so
|
||||
the macros now use those field accessors under `#ifdef PORT` (N64 build
|
||||
keeps the byte-offset macros unchanged). **Still open (MED):** #5 — the
|
||||
D102 weapon-model `render_pos` is pointed at a `dynAllocate`'d transient
|
||||
arena that on N64 aliased the persistent `hand->mtxlist`; likely the
|
||||
"1P weapon model doesn't draw" cause, may fold into D114. #6 — the D56
|
||||
watch-preview Model pool (`watchRwPool[0xC8]`) is N64-sized and the
|
||||
inline Model overruns live watch fields. See the audit doc for the full
|
||||
table and recommended fix order.
|
||||
|
||||
**`data/` deletion + recovery (session M-3).** `git worktree remove
|
||||
--force` on an agent worktree that had a directory *junction*
|
||||
`worktree/data → main/data` followed the junction and deleted the real
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# 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`.
|
||||
@@ -4957,9 +4957,23 @@ CasingRecord* casingCreate(ModelFileHeader* header, Mtxf* mtx)
|
||||
#define THROWPOS_PREV_OFFSET 0xB48
|
||||
#endif
|
||||
|
||||
#ifdef PORT
|
||||
/* D115: these were raw byte offsets into `struct player` (via `handoffset =
|
||||
* handnum * sizeof(struct hand)` + a hardcoded intra-hand offset). Both the
|
||||
* stride and the base offset are N64-sized; on x86-64 `struct player` and
|
||||
* `struct hand` are much larger (D98/D100/D102), so the raw math lands inside
|
||||
* the inline gait Model / bondhead-matrix region and `matrix_4x4_copy(THROWMTX,
|
||||
* ..)` scribbles 64 bytes of live state on every shot. The offsets are exactly
|
||||
* `hands[handnum].throw_item_pos_related[_prev]` and its translation row, so
|
||||
* use the field accessors directly. See docs/AUDIT-M6-player-offsets.md. */
|
||||
#define THROWMTX (&g_CurrentPlayer->hands[handnum].throw_item_pos_related)
|
||||
#define THROWPOS(k) (g_CurrentPlayer->hands[handnum].throw_item_pos_related.m[3][k])
|
||||
#define THROWPREV(k) (g_CurrentPlayer->hands[handnum].throw_item_pos_related_prev.m[3][k])
|
||||
#else
|
||||
#define THROWMTX ((Mtxf *) ((u8 *) g_CurrentPlayer + handoffset + THROWMTX_OFFSET))
|
||||
#define THROWPOS(k) (((f32 *) ((u8 *) g_CurrentPlayer + handoffset + THROWPOS_OFFSET))[k])
|
||||
#define THROWPREV(k) (((f32 *) ((u8 *) g_CurrentPlayer + handoffset + THROWPOS_PREV_OFFSET))[k])
|
||||
#endif
|
||||
|
||||
extern const f32 g_CasingSwitchScale;
|
||||
extern const f32 g_PistolCasingHorizontalSpeed;
|
||||
|
||||
Reference in new issue
Block a user