From 91564f79b649f345e2f847974bdcadda44210ddb Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Thu, 24 Sep 2026 09:58:09 -0400 Subject: [PATCH] docs: porting-notes on dead placeholders after inlining a split struct (D336); probe catalogue --- docs/dev/GE-ENV-PROBES.md | 1 + docs/porting-notes.md | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/dev/GE-ENV-PROBES.md b/docs/dev/GE-ENV-PROBES.md index 859085ca..d375ba9a 100644 --- a/docs/dev/GE-ENV-PROBES.md +++ b/docs/dev/GE-ENV-PROBES.md @@ -70,6 +70,7 @@ Two classes: | `GE_D235` | `src/boss.c` (per-stage `while (!done)` block, after `memaReset`; `#ifdef PORT`) | D235 evidence probe: logs how many GL-texture-cache entries the per-stage `videoResetTextureCache()` call purges at each level load. A cold boot into a level should log 0; a death/restart (or any transition from a level that uploaded textures) logs >0 — direct evidence of the stale-entry mechanism. The clear itself is **not** gated on this var (it's the fix, see below); the var only controls the log line. | **live** (D235) | | `GE_D235_OFF` | `src/boss.c` (same block) | **TEMP** A/B switch: skips the per-stage `videoResetTextureCache()`, i.e. reverts the D235 fix for one run. For comparing a restart with and without the fix. | **TEMP** (D235; remove when D235 closes) | | `GE_D235_RELOAD=` | `src/boss.c` (same block + `bossMainloop` retrace branch) | **TEMP** reload harness: once the title stage loads after the first level (e.g. after a scripted watch Abort), wait `` retraces, then re-enter that level via `bossSetLoadedStage()`, the front end's own mission-start call. A headless death/restart without the pointer-menu walk. Pair with `GE_INPUTSCRIPT="800:START;1200:A;1300:RIGHT;1400:A"` (abort) on `-level_XX`. | **TEMP** (D235; remove when D235 closes) | +| `GE_D235_NEXT=` | `src/boss.c` (with `GE_D235_RELOAD`) | **TEMP**: re-enter a *different* stage after the abort (level A → title → level B), used for D336 (Runway → Frigate). `GE_D85TEX=1` now also logs each stage's texture-pool size and address range (`src/game/initmttex.c`). | **TEMP** | | `GE_D75V` | `port/fast3d/gfx_pc.cpp` (`gfx_sp_vertex`, end of the copy/transform loop; cached env check) | D75 (Nintendo logo) per-batch vertex probe: for each `G_VTX` batch in frames `lo..hi`, logs frame#, source pointer, n_vertices, original dest_index, raw ob[0..2] of the first vertex, min/max of the transformed x/y/z/w across the batch, and the MP translation row. Used to prove the transform is correct (M-195) and that every batch encodes v0=0 (slot-overwrite hypothesis). Value form `"lo-hi"` (e.g. `300-900` = the logo screen); unset = off. | **live** (D75) | | `GE_D75D` | `port/fast3d/gfx_pc.cpp` (`gfx_sp_vertex` G_VTX case + `gfx_sp_tri1` rejection/emit gates; cached env check) | D75 (Nintendo logo) per-frame draw-path probe (M-196): for frames `lo..hi`, logs one line/frame with the V/T token stream (`seq`), max triangle vertex index, cumulative vertices loaded, and per-triangle accounting — trivial-reject / backface-cull counts, how many tris reach GL, how many of those are on-screen (NDC bbox ⊂ [−1,1]²), invisible-flag count, vertex-color min/max, first oml word, depth z/w range + NaN count, texture-sampler presence (`utex` = GL shader, `cutex` = combiner), combine mode, prim/env color. Used to REFUTE the slot-overwrite hypothesis (DL is streamed, max index 15) and narrow the bug to a texture-modulate material rendering black. M-197 added logo-mode `lg_*` fields (sampled only on tris with the logo combine word — the frame-level `utex`/`cutex` are first-tri-wins and were the source of the "combiner claims 0" false anomaly): `lg_utex`/`lg_tex` + tile-0 descriptor + texture buffer address/id/size for the logo material. Value form `"lo-hi"` (logo window ≈ `244-320` on a bare front-end boot); unset = off. Companion one-shot `D75DCC:` log in `gfx_generate_cc` fires when `combine_mode==0x009ffe4f19ffe4f1` to decode the logo's combine slots. | **live** (D75) | diff --git a/docs/porting-notes.md b/docs/porting-notes.md index 630f438d..cc70007e 100644 --- a/docs/porting-notes.md +++ b/docs/porting-notes.md @@ -257,6 +257,18 @@ framing — it never looked like game logic to begin with. broken until D290. When fixing a raw-offset global read, grep for every other read of the same globals (`D_80035D00`/`D_80035D04` here) before closing. +- **When a split N64 struct is made inline on PC, its placeholder fields + become dead, and any code still reading them reads uninitialised memory.** + D100 turned `player.model` into a real inline `struct Model`, but the + decomp's `field_59C..field_650` placeholders (raw N64 offsets into that + model) stayed as separate fields. `bheadAdjustAnimation` kept reading + `field_5C0` (= N64 `model.animframe1`) and `animFlipFlag` (= `model.gunhand`), + which nothing ever wrote (D336: garbage anim frames, Frigate scene wrecked, + sensitive to memory layout). After inlining a split struct, grep every + placeholder in the old range and remap each read to the real field. + Diagnostic tell: a hardware watchpoint on the "poisoned" field sees *no + writes at all*. + ## B. 16-byte PC `Gfx` / `Vtx` vs 8-byte N64 Any buffer reservation, `memcpy` size, slot stride, or pool budget