docs: M-17 session — D126/D127/D128/D129, 13->18/21 levels pass

LEVEL-STATUS: Bunker2/Depot/Surface2 (D126), Surface1 (D127), Control
(D128) now PASS. Only C2/C2-GDL remains (Runway intermittent, Facility,
Jungle). Cuba loads+renders but bare-boot end-credits path still faults
(D76/D129). HANDOFF next task = C2-GDL model relocation (own session).
PORT-LEARNINGS: D126 corollary (trailing runtime list ptr in ROM record),
D128 (raw hardcoded N64 struct stride bites the same as Gfx/Vtx sizing).
This commit is contained in:
James Dansereau committed 2026-08-29 20:27:20 -04:00
1 parent af4e0697f7
commit 7159fff544
3 files changed
+79 -40

No files matched your search

+47 -23
View File
@@ -78,33 +78,57 @@ Full workstream breakdown (WS1–WS6) in `docs/PLAN-linear-level-sweep.md`.
`HANDOFF.md`, `AGENTS.md`, `CLAUDE.md`): the D121 + D122 entries in
`docs/PCPortResearch.md` §F/§H and this file's edits. Commit or fold in.
## Next task — converter write-audit (C3r / C4 / C5 / C6)
## Done this session (M-17) — 4 commits, 13→18/21 PASS
**D125 is LANDED (M-16b).** `tools_pc/d88_emit.py:374` now writes 8 NUL
bytes; all 21 `Usetup*Z` sidecars regen'd; **Aztec `-level_28` PASSES**
(was C3 crash), Bunker1/Silo unregressed, `d125_check.py` 21/21 MATCH.
M-16 probes reverted, scratch deleted, `d125_check.py` kept. Sweep 12 →
13/21. Full write-up: §H D125 M-16b.
- **D126** (`e851e2ab`) — objective sub-records `criteria_picture` (30),
`criteria_roomentered` (32), `criteria_deposit` (33),
`setup_objective_text` (35) each end in a `T *next` list pointer that
`set_parent_cur_obj_*` / `setup_briefing_text_entry_parent` write while
walking the setup stream. Widens 4→8B and 8-aligns at offset 16 on PC →
struct is 24B/6w (N64 16/20); the old N64-sized emit meant the runtime
8-byte `->next` store clobbered the *next* propdef record's header →
walk desync → command indices drifted ~100 → `setupDoor` `linkedDoor`
resolution landed on the wrong record. Fixed `d88_propdefs.py`
(`PROPDEF_PC_BYTES[30/32/33/35]=24` + typed handler) +
`loadobjectmodel.c` `sizepropdef` PORT returns 6. **Cleared C3r Bunker2
+ C4 Depot + C6 Surface2** in one change.
- **D127** (`1129f63d`) — `sndPlaySfx` guards a bogus `ALSound*` (the
converted libaudio bank has fewer/rearranged `soundArray` slots than
N64; audio is Phase-3 parked). **Cleared C7 Surface1.**
- **D128** (`749feb9f`) — `sub_GAME_7F0B37EC` special-portal marker used
the hardcoded N64 8-byte `bg_portal_data_entry` stride / `controlbytes1@6`;
on PC the struct is 16B with `controlbytes1@10`, so `|= 2` scribbled
into the middle of a portal's `offset_portal` pointer → `bg.c:5723`
crash on any level with a `specialportalarray` entry. `#ifdef PORT`
uses the struct accessor. **Cleared C5 Control.**
- **D129** (`<this commit>`) — `langGet` bounds-checks the slot id and
resolved bank pointer. Bare `-level_XX` boots that reach the
cast/credits text path (Cuba, `bondviewRenderCredits`, D76 area)
reference banks the menu flow never loaded. Reduces the Cuba bare-boot
end-credits crash; **Cuba itself loads + renders 300+ frames fine** and
the credits work through the real front-end flow.
The last 3 sessions' converter bugs are one family — width/endianness/
stride errors in `tools_pc/d*_emit.py` (D112 `put_f32`, D120 opcode-0x18,
D125 slice width). Pi audited every `out[` write in `d88_emit.py`;
`d43_emit.py` + the BG sidecar emitters never got that pass, and the open
crashes line up with exactly those files:
## Next task — C2 / C2-GDL model-GDL relocation (Runway + Facility + Jungle)
- **C3r** Bunker2 — DOOR-tail `linkedDoorOffset`/`linkedDoor` layout in
`d88_propdefs.py` vs compiled PC `DoorRecord` (`offsetof`/`sizeof`
cross-check; D123 read-before-write-id pattern).
- **C6** Surface2 — `PitemZ_entries[modelid]` (D122/D123 cont.,
`d43_emit.py` / `d88_propdefs.py`).
- **C4** Depot — BG tile/room table (`prop.c:902`).
- **C5** Control — BG portal `offset_portal` unresolved/BE (`bg.c:5723`).
The last 3 crashes are **one class**: the model / prop GDL relocation
(`texLoadFromGdl` / `sub_GAME_7F0762E0`, `objecthandler_2.c`) writes
non-16-aligned `dst` because it mixes N64 8-byte `Gfx` and PC 16-byte
stride in the `replacementgdl` / `name` offset math → garbage `G_SETTIMG`
w1 → `import_texture_i8` fault (Runway, Facility). Jungle renders ~300
frames then hits a related explosion-DL `G_MTX` (`gfx_sp_matrix`).
**Brief: `docs/BRIEF-C2gdl-model-reloc.md` — its own focused session**
(D80/D82/D83 area). Files: `src/game/objecthandler_2.c`, `src/game/tex.c`
(`texLoadFromGdl`), `src/game/model.c` (`modelNodeReplaceGdl`),
possibly `tools_pc/d43_emit.py` model texture-blob offsets.
Audit `d43_emit.py` + BG sidecar emit paths for slice-width mismatches,
un-byteswapped fields, N64-vs-PC stride math. Then the structural piece:
**C2-GDL** (Runway + Facility) — `docs/BRIEF-C2gdl-model-reloc.md`, its
own focused session. Defer C2m Jungle (matrix), C7 Surface1 (add a narrow
`#ifdef PORT` `sndSetupSound` load guard only — audio is Phase 3).
Everything else: hand `docs/LEVEL-PLAYTEST.md` to the user for the WS6
completion pass (real input, per-level objective checklist).
**Regen gotcha:** `d69_emit.py` rewrites `data/pccg-<r>/pccg.bin` from
scratch with only the 52 bg/stan rows — you MUST follow it with
`d88_emit.py --regen` to re-add the 21 `Usetup*Z` rows, or every level
falls back to the raw N64 setup and crashes identically in
`proplvreset2`. Always run the full `d43 && d69 && d88 --regen` chain.
Sweep note: `level_sweep.sh` was flaky this session (spurious NO-FRAMES on
known-good levels under machine load / the 24 s watchdog + D117
+23 -17
View File
@@ -18,29 +18,33 @@ Build = `f2beae4b` + `tools_pc/d88_propdefs.py` (D123) + `port/src/gimgfixup.c`
| Train | 25 | **PASS** 87.0% | — | — |
| Caverns | 39 | **PASS** 91.6% | — | — |
| Egypt | 32 | **PASS** 90.9% | — | — |
| Cuba | 54 | **PASS** 92.5% | — | — |
| Cuba | 54 | **PASS** 92.5% (loads+renders 300+ frames; bare-boot end-credits path `bondviewRenderCredits` still faults intermittently — D76/D129, not a real-flow blocker) | — | — |
| Dam | 33 | **PASS** 83.3% | — (C1 fixed, D123) | — |
| Frigate | 26 | **PASS** 90.4% | — (C1 fixed, D123) | — |
| Statue | 22 | **PASS** 80.1% | — (C1 fixed, D123) | — |
| Streets | 29 | **PASS** 91.6% | — (C1 fixed, D123; no C2 crash this run — timing) | — |
| Cradle | 41 | **PASS** 55.4% (low — partial render, no crash) | — (C1 fixed, D123) | — |
| Runway | 35 | CRASH | `import_texture_i8` gfx_pc.cpp:821 (fault 0x721b8ee8) — same as Facility | **C2** |
| Runway | 35 | CRASH (intermittent, ~3/4 runs; `0x1400d1d83`) | `import_texture_i8` — same as Facility | **C2** |
| Facility | 34 | CRASH | `import_texture_i8` gfx_pc.cpp:821 (fault 0x72181ee8) — model-GDL relocation align bug, D124, NOT fixed | **C2** |
| Jungle | 37 | CRASH | `gfx_sp_matrix` gfx_pc.cpp:1046 (fault 0x401c68e0) — C2 texture crash fixed (D124), now explosion-DL `G_MTX` (D75/matrix family) | **C2m** |
| Aztec | 28 | **PASS** 90.8% | — (C3 fixed, D125) | — |
| Bunker2 | 27 | **PASS** 91.5% | — (C3r fixed, D126) | — |
| Depot | 30 | **PASS** 79.8% | — (C4 fixed, D126) | — |
| Control | 23 | CRASH | `sub_GAME_7F0BA2D4` bg.c:5723 (`portal_pts->numPoints`, via chrprop.c:62) | **C5** |
| Control | 23 | **PASS** 90.8% | — (C5 fixed, D128) | — |
| Surface2 | 43 | **PASS** 70.4% | — (C6 fixed, D126) | — |
| Surface1 | 36 | CRASH | `sndSetupSound` snd.c:653 (fault 0x56220001…, ascii-ish) | **C7** |
| Surface1 | 36 | **PASS** 77.5% | — (C7 guarded, D127) | — |
**16 / 21 PASS.** D123 cleared C1 on all 6. D124 cleared Jungle's texture
**18 / 21 PASS.** D123 cleared C1 on all 6. D124 cleared Jungle's texture
crash (now hits an explosion-DL matrix crash). D125 cleared C3-Aztec.
**D126** (objective sub-record `->next` pointer widens 4→8B → 8-byte store
clobbers the next propdef record's header → walk desync → wrong command
indices) cleared **C3r Bunker2 + C4 Depot + C6 Surface2** in one fix
(`d88_propdefs.py` types 30/32/33/35 → 24B/6w). 5 crashes remain in 4
classes: C2 Runway+Facility, C2m Jungle, C5 Control, C7 Surface1.
(`d88_propdefs.py` types 30/32/33/35 → 24B/6w). **D127** guarded a bogus
ALSound* in the parked audio path → **C7 Surface1**. **D128** fixed a
hardcoded-N64-stride portal-flag write → **C5 Control**. **3 crashes
remain, all one class:** C2 Runway + Facility + C2m Jungle — the model /
prop GDL relocation misaligns on the 8→16B `Gfx` stride
(`docs/BRIEF-C2gdl-model-reloc.md`).
Notes: Streets passed this run but the C1 agent saw a `gfx_sp_matrix`
crash — likely the same explosion-DL `G_MTX` (C2m) as Jungle, timing-
@@ -163,16 +167,18 @@ id from a propDef record). Fault addr 0x0. Files: `tools_pc/d88_propdefs.py`,
crash on load, not silence. May be dodge-able with a narrow guard until
audio lands. Files: `src/snd.c`, `port/src/` audio stubs.
## Next (5 crashes, 4 classes — priority order)
1. **C2** Runway + Facility — model-GDL relocation align bug in
`objecthandler_2.c`/`texLoadFromGdl` (D80/D82/D83 area). Shared infra,
2 levels + likely helps Jungle. Biggest win.
2. **C2m** Jungle — explosion-DL `G_MTX` (`gfx_sp_matrix`), D75/matrix
family.
3. **C5** Control — BG portal `g_BgPortals[].offset_portal` unresolved/BE,
`bg.c:5723` via `chrprop.c:62`. BG sidecar (`d69_emit.py`).
4. **C7** Surface1 — `sndSetupSound` `snd.c:653`; parked-audio, may just
need a narrow load guard until Phase 3.
## Next (3 crashes, 1 class)
1. **C2 / C2-GDL** Runway + Facility — model/prop GDL relocation writes
non-16-aligned `dst` (N64 8B `Gfx` vs PC 16B stride) in the
`texLoadFromGdl` / `sub_GAME_7F0762E0` path → garbage `G_SETTIMG` w1 →
`import_texture_i8` fault. `docs/BRIEF-C2gdl-model-reloc.md` — its own
focused session (D80/D82/D83 area).
2. **C2m** Jungle — renders ~300 frames then an explosion-DL `G_MTX`
(`gfx_sp_matrix`), D75/matrix family. May be downstream of the same
GDL-reloc infra.
Everything else loads + renders + survives the no-input window.
Hand `docs/LEVEL-PLAYTEST.md` to the user for the WS6 completion pass.
Re-run `tools_pc/level_sweep.sh` after each class fix; keep
`-level_09`/`-level_20` green (`tools_pc/framediff.py`).
+9
View File
@@ -48,6 +48,15 @@ expressed in N64 `Gfx`/`Vtx` units is **half-size** on PC.
- Instances: D50.6 (texCopyGdls copied only w0 of each 16-byte slot),
D58 (DL reserve 0x100→0x200), D85 (`bgWidenRoomGdl` 8→16 + bswap),
D95 (2× master-DL buffer + raised mempool ceiling).
- **Also bites RAW hardcoded struct-stride writes, not just Gfx/Vtx.**
D128: `sub_GAME_7F0B37EC` did `((u8*)g_BgPortals)[(portal<<3)+6] |= 2`
— the N64 `bg_portal_data_entry` is 8B (`ptr@0, cr1@4..cb2@7`); on PC
the widened `offset_portal` ptr makes it 16B (`cr1@8..cb2@11`), so the
write landed in the middle of another portal's pointer. Fix = use the
struct accessor under `#ifdef PORT` (every other site already does:
`g_BgPortals[portal].controlbytes1 |= PORTALFLAG_SPECIAL`). Grep for
`<< 3` / `* 8` / `+ 6` style raw offsets into any struct that gained a
pointer field.
## C. Big-endian rodata / ROM data read on little-endian PC