Files
goldeneye-pc-port/docs/dev/LEVEL-STATUS.md
T
James Dansereau 5c3944582b docs: v0.1.0 alpha framing + hygiene pass (#25)
Update the public-facing docs to the M-49 state (v0.1.0 alpha, first
full-campaign playtest: 19/21 missions completable, D191 crashes Bunker ii
+ Statue, D193 AI pacing is the top flaw).

- README: lead with what it is + status, research angle second; new
  Quick start section; Status/Download rewritten for the alpha + dual
  platform; "By the numbers" refreshed (~3 weeks, ~320 commits, ~190
  findings, ~49 sessions, 69 PORT files).
- .github/release-notes.md: v0.1.0 title + known-issues rewrite.
- .github/social-preview.html: status/stat lines (regen + re-upload the
  PNG separately).
- docs/index.md, docs/dev/LEVEL-STATUS.md: M-49 status.
- docs/dev/findings.md, docs/dev/GRAPHICS-BACKLOG.md: D191-D197 +
  D148/D170/D173 M-49 escalations.

Hygiene:
- Fix the `&` literal in internals.md H1.
- Repoint two ../README.md links that 404 on the Pages site.
- Standardise British -> American spelling across the public docs
  (behaviour->behavior, colour->color, catalogue->catalog,
  centre->center, etc.). Deep internal logs left untouched.
- .gitignore: document the local-notes convention (docs/HANDOFF.md,
  docs/BACKLOG.md, and a new docs/dev/notes/ drop for roadmaps and
  session plans) so it survives a fresh clone.
2026-09-04 00:13:43 -04:00

279 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LEVEL-STATUS — 21 solo-level load+render sweep (WS4)
Method: bare `./build-pc/ge007.x86_64.exe -level_XX` (per-level `-m*` pools
auto-injected, D121), `GE_PCDUMP="80-260:40"`, ~24 s watchdog then
`taskkill //F //IM ge007.x86_64.exe`, `ge007.crash.log` symbolicated with
`addr2line -e build-pc/ge007.x86_64.exe -f -C <PC>` (image base
0x140000000). "render %" = `tools_pc/pixcount.py` non-clear on the last
captured frame. Binary at `f2beae4b` + this session's build.
**M-49 (2026-09-04) — full-campaign playtest, v0.1.0 win64 bundle.**
User played every solo mission on the packaged build (fresh save). **19 / 21
completable start to finish.** Only two crashes in the whole game — **Bunker ii
(`-level_27`) and Statue (`-level_22`)**, same fault PC `0x1400801b1`
(`modelGetNodeRwData` node-tree walk), one root cause = **D191** (pointer-width
/ byte-swap family, findings §F). This retires the old "expect crashes past the
level intro" framing for 19/21 levels. Biggest non-crash complaint: **D193** —
AI locomotion too slow, breaks Cradle's scripted progression. v0.1.0 tagged off
this state.
**M-30 re-sweep (2026-08-31, build after D157/D120/RC2): 21 / 21 PASS — no
crash logs.** First all-green sweep incl. Cuba (54) — the D129 credits-path
crash no longer reproduces on a bare `-level_54` boot (Cuba rendered 92.5%
crash-free through the window). Verifies that the M-30 changes — D157
(objective difficulty byte-offset), RC2 (`Video.FixMipTextures` mip-upload
clip, default on), D120 (`d43_emit.py` PointUsage emit), the `GE_SAVELOG`/
`GE_UNLOCK_ALL` `#ifdef PORT` diagnostics, and the front-end mouse-pointer
mode — introduced no level-load regression. Machine was idle; `level_sweep.sh`
default window/watchdog.
M-25 re-sweep (2026-08-30, port-QoL build `863f436b`): **20 / 21 PASS**,
unchanged. Caverns (39) flagged CRASH `0xc000001d` once under sweep load →
2/2 clean on retry (D117/D134 concurrent-load flakiness, not a regression).
Cuba (54) still the only real crash (known D129 credits path, parked).
**Silo (20):** PASS. A full-length `GE_PCDUMP` capture froze at ~frame 320
(silo→Bond camera descent) twice — but both those runs, *and* the pre-M-25
baseline comparison, were on a machine still loaded from the sweep. On an
idle machine Silo runs fine (user drove it live past ~1800 frames, VI
pacemaker healthy, no heartbeat stall). Classic D117/D134 load-sensitive
stall, not a deterministic bug and not an M-25 regression. If it recurs on
an idle box, count N runs before believing it (porting-notes.md §E).
Last full re-sweep: 2026-08-29 (M-22), after D134 landed (SP/DP task-done
event no longer dropped into a full sched interruptQ — the "frame-2 hang"
that M-13..M-21 wrote off as D117/machine-load flakiness). Build = HEAD
`a8f27c16`. Optimized `level_sweep.sh` (early-exit once the GE_PCDUMP set
is on disk / STALL_SECS freeze) — full 21-level sweep now ~7 min.
**M-22 result: 20 / 21 PASS.** Every level loads + renders + survives the
capture window EXCEPT Cuba. Facility (34) and Jungle (37) — the M-20
re-verify failures — both PASS cleanly now, confirming D134 was their
cause. Only remaining crash:
- **Cuba (54)** — `textMeasure` (`textrelated.c:770`) ← `bondviewRenderCredits`
(`bondview2.c:8912`), AV @ `0x1400c3b77`, fault addr NULL, after ~frame
900. This is the **known D129 residual**: the bare `-level_54` boot
reaches the cast/credits scroll referencing a text bank the real
front-end flow would have loaded. Not a real-flow blocker; Cuba loads +
renders 300+ frames fine. Parked with D76 (`docs/dev/GRAPHICS-BACKLOG.md`).
| Level | # | Status | Crash site | Class |
|---|---|---|---|---|
| Bunker1 | 09 | **PASS** (M-20: re-verified — 1800 frames crash-free, no `ge007.crash.log`, direct `-level_09` boot at ~90us/frame). The M-19 "boot crash `frames=0` PC `0x1400066fc`" did **not** reproduce on `1fc3cff6` in three separate runs → it was sweep flakiness / D117 nondeterminism under machine load, not a real regression. | — |
| Silo | 20 | **PASS** 91.7% | — | — |
| Archives | 24 | **PASS** 91.1% | — | — |
| Train | 25 | **PASS** 87.0% | — | — |
| Caverns | 39 | **PASS** 91.6% | — | — |
| Egypt | 32 | **PASS** 90.9% | — | — |
| 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 | **PASS** | — (D130: `romdataFixupFont` glyph-relayout aliasing) | — |
| Facility | 34 | **PASS** (M-20 re-verified on an idle machine: frame 1500+, no crash. The M-20 mid-session "boot crash `frames=0`" was concurrent-load flakiness — three subagents/sessions sharing the box. Fix stands: D130.) | — (D130: `romdataFixupFont` corrupted glyph `#`/`"` pixeldata → fast3d AV) | — |
| Jungle | 37 | **PASS** (M-20 re-verified on an idle machine: frame 1500+, no crash. M-19/D131: 2400+ crash-free. The M-20 mid-session "frame-2 hang" was concurrent-load flakiness.) | — (D131: `osVirtualToPhysical()` truncated a compiled `.bss` matrix ptr in `explosionRenderPropSmoke` → `seg_addr` now restores the module high word) | — |
| 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 | **PASS** 90.8% | — (C5 fixed, D128) | — |
| Surface2 | 43 | **PASS** 70.4% | — (C6 fixed, D126) | — |
| Surface1 | 36 | **PASS** 77.5% | — (C7 guarded, D127) | — |
**19 / 21** (M-19). D131 cleared C2m Jungle (explosion-DL `G_MTX` truncated
compiled-symbol pointer). **But `-level_09` (Bunker1) regressed to a boot
crash sometime between M-18 and now** — reproduces on clean master, so it
is not an M-19 change; bisect D125→HEAD. Net 20→19 until 09 is refixed.
**(M-18) 20 / 21 PASS.** D130 cleared C2 Facility + Runway — the crash was
`romdataFixupFont` corrupting fontchar glyphs 0/1/2 (in-place 24→32B relayout
aliases for low indices), NOT the model-GDL relocation the D124-Facility
addendum suspected. Only Jungle (C2m, explosion-DL `G_MTX`, ~frame 300)
remains.
**(older) 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). **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-
dependent (D117 nondeterminism). Cradle renders only 55% — watch for a
partial-load issue when it gets a playtest. Frigate/Statue vary 68–90%
run to run.
## Crash classes (most-impactful first)
### C1 — chr / AI-record pointer deref — FIXED (D123)
**RESOLVED.** Root cause: `tools_pc/d88_propdefs.py` (D122's `OBJ_TAIL_DESC`)
zeroed the widened `VehichleRecord/AircraftRecord.ailist` slot instead of
carrying its pre-populated int AI-list id, so `prop.c:1764/1786` →
`ailistFindById(0)` → `GAILIST_AIM_AT_BOND` → `ai()` ran a CHR aim list
against `ChrEntityp==NULL`. Fixed via `OBJ_ID_WORDS`. See §F/§H D123.
Dam/Frigate/Statue/Cradle now PASS; Runway/Streets fall through to C2.
<details><summary>original triage notes</summary>
`chrIsNotDeadOrShot(ChrRecord *self)` faults on `self->actiontype` with
`self` bogus (Dam: Rdi=0x1401296a0 → an image address; fault addr 0x8).
Reached from `chraidata.c:61` (`m_AimAtBond[]` AI-list region — backtrace
frame is inside the global AI-list rodata blob, i.e. the return address
was corrupted OR an AI-list entry is being called as a function pointer).
Pointer-width / BE-rodata family (porting-notes.md §A / §C): a per-level
`Usetup*Z` guard record (`GuardRecord` type 9 / `GuardAttributeRecord`
type 18) or chr-spawn pointer not converted / not pointer-width-adjusted,
OR the AI-list bytecode pointers in `chraidata.c` rodata are BE and get
called raw. **Biggest single win — 6 levels.**
</details>
### C2 — fast3d bad texture pointer (D124) — SPLIT into two causes
**Jungle (`0xabcd0824`) — FIXED (D124).** `gimgSyncCompiledGlobalDLs()`
never copied the resolved texture pointers into the compiled
`globalDL_0xNNN` explosion DLs (slot-detect keyed on a marker `texLoad()`
had already erased); the arrays kept link-time `IMAGESEG` words and the
first explosion-DL draw fed `0xABCDxxxx` into fast3d. Latent on every
level. Fix: `port/src/gimgfixup.c` detects the slot from the compiled
array. Jungle now renders ~300 frames then hits a *separate* explosion-DL
`G_MTX` crash (`gfx_sp_matrix` gfx_pc.cpp:1046 — D75/matrix family).
**Facility (`0x72181ee8`) — NOT fixed, separate cause.** Bogus `G_SETTIMG`
w1 in a model/prop GDL produced by the runtime `texLoadFromGdl()`
relocation in `sub_GAME_7F0762E0` (`objecthandler_2.c:82`): its model-GDL
output `dst` pointers are non-16-aligned (N64 8B vs PC 16B `Gfx` stride
mix in the `replacementgdl`/`name` offset math). Open D80/D82/D83
"model/room GDL runtime conversion unverified" area — shared infra, needs
a dedicated pass. Files: `src/game/objecthandler_2.c`, `src/game/tex.c`
(`texLoadFromGdl`), `src/game/model.c` (`modelNodeReplaceGdl`).
**M-14 update (partial, still NOT fixed):** crash cmd pinned —
`G_SETTIMG w0=0xfd900000 w1=0x72181ee8` where `w1` is a garbage
`tex->data` from the texture-pool path (`texLoadFromModelFileHeader` →
`texLoad`), not solely the GDL command-stream copy. Every model file
loads at an odd (`&15==1`) `objheader->Switches` base
(`mempAllocBytesInBank` does no alignment) and `delta&15` != 0.
Full write-up + next-step probe plan: findings.md §F D124-Facility
addendum. Likely fix site: the `d43_emit.py`/`pcmodels` sidecar's model
texture-blob offsets (N64 8B-Gfx vs PC 16B-Gfx GDL extent).
### C3/C6 update (M-14/M-15, D125) — offline pipeline RULED OUT, bug is runtime
Symptom: converted `propDefs` blob in RAM does not match the offline output
(after record 0 it is zeros/garbage), so the runtime `sizepropdef()` walk
drifts and `setupDoor`/`modelLoad` get the wrong `pdefIndex`/modelid.
**M-15: the offline side is proven correct.** `tools_pc/d125_check.py`
byte-compares the emitted `pccg.bin` propDefs slice (post-RZ-roundtrip, at
the relocated header offset) to `convert_stream()` → **MATCH for all 21
levels**. The M-13 overseer hypothesis (pass-1 delta / `pd_end` mismatch in
`d88_emit.py` ~L308–340) is **disproven**: instrumented run shows
`tiled_pd_end == H[intro]` and `_n64len == pd_end - pd_start` exactly for
all 21, next region always `intro`. `sizepropdef()` PORT strides re-checked
vs `PROPDEF_PC_BYTES/4` — all 28 match.
**→ The corruption is at/after runtime load.** Prime suspect:
`decompressdata()` truncation in `port/src/rzdecomp.c` for the larger
converted setup files (the `while (ret == Z_OK)` inflate loop), or the
STAGE bank buffer / `langLoadToAddr` (`prop.c:1274`) overwriting the tail.
Next step: probe `decompressdata()` `ret`/`produced` vs expected size for
`UsetupsevbZ`. See §F/§H D125.
### C3 — Aztec FIXED (D125); Bunker2 residual (C3r)
**Aztec** was D125 (boundpad plink-name blob drift from the
`d88_emit.py:374` slice-width bug corrupting `stanPackId` → NULL stan →
door `model=NULL`). Fixed + regen'd, Aztec PASSES.
**Bunker2 (C3r)** — `door7F054FB4` `propobj.c:13536`, `var_s1` = `0xffff..`
walked from `linkedDoor`. `door->linkedDoor` is resolved at setup from
`door->linkedDoorOffset` (`prop.c:1204-1206`, N64 struct word 32 / 0x80) —
a read-before-write int id, D123's `ailist` pattern. `d88_propdefs.py`'s
DOOR handler (`DOOR_TAIL_PTR_WORDS`, tail loop from word 32) vs the
compiled PC `DoorRecord` layout is the suspect: either `linkedDoorOffset`
lands at the wrong PC byte, or a tail pointer word's 8-align cursor
diverges from the compiler's struct layout. Bunker2 has linked
double-doors; Aztec's doors are singletons (`linkedDoorOffset==0`) so it
never exercised this path. Needs `offsetof`/`sizeof` cross-check of PC
`DoorRecord` vs the converter cursor. Files: `tools_pc/d88_propdefs.py`,
`src/bondtypes.h` (DoorRecord), `src/game/prop.c`, `src/game/propobj.c`.
### C4 — prop tile-walk room deref (1: Depot)
`prop.c:902` — `*arg1 = sp4C->room` where `sp4C` came back bad from
`walkTilesBetweenPoints_NoCallback`. Likely a BG tile/room table not
converted for Depot. Files: `src/game/prop.c`, `src/game/bg.c`, BG
sidecar.
### C5 — bg portal points (1: Control)
`bg.c:5723` — `portal_pts = g_BgPortals[idx].offset_portal; portal_pts->numPoints`.
`offset_portal` unresolved / BE. Via `chrprop.c:62` (chr line-of-sight
through portals). Files: `src/game/bg.c`, BG portal sidecar.
### C6 — modelLoad PitemZ header (1: Surface2)
`loadobjectmodel.c:393` — `PitemZ_entries[modelid].header->RootNode`,
`header` NULL / `modelid` OOB. Direct D122 continuation (prop/item model
id from a propDef record). Fault addr 0x0. Files: `tools_pc/d88_propdefs.py`,
`tools_pc/d43_emit.py`, `src/game/loadobjectmodel.c`.
### C7 — sndSetupSound (1: Surface1)
`snd.c:653`. Audio is a parked subsystem (Phase 3) but this is a hard
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 (1 crash)
1. **C2m** Jungle `-level_37` — renders ~300 frames then an explosion-DL
`G_MTX` (`gfx_sp_matrix` gfx_pc.cpp:1046, fault 0x401c68e0), D75/matrix
family. NOT related to D130 (Facility/Runway were a font-relayout bug, now
fixed). `docs/BRIEF-C2gdl-model-reloc.md` is now largely obsolete — its
model-GDL-relocation premise was disproven (D130); the Jungle crash is a
compiled/explosion-DL matrix pointer, closer to D75/D124-Jungle territory.
Everything else loads + renders + survives the no-input window.
Hand `docs/dev/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`).
---
## With-input crash sweep (M-30, 2026-08-31)
Scripted controller-0 input (walk fwd + fire pulses + turns) via
`GE_INPUTSCRIPT`, ~46-70 s per level, `GE_PCDUMP` capture. Checks
`ge007.crash.log` + VI-pacemaker health. Machine was loaded (concurrent
agents) → frame progression slow on some runs but VI posts steady = no
hang. All retried on a settled machine where slow.
| # | Level | With-input result |
|---|----------|-------------------|
| 33 | Dam | PASS (frame 300+ dump, no crash) |
| 09 | Bunker1 | PASS (VI pacemaker healthy, no crash; slow under load) |
| 20 | Silo | PASS (VI pacemaker healthy, no crash; slow under load) |
| 23 | Control | PASS (frame 600+, no crash) |
| 32 | Egypt | PASS (frame 600, no crash — first run was load-slow, clean on retry) |
| 54 | Cuba | PASS (frame 1200+, no crash) |
| 39 | Caverns | PASS — **D158 (KF7 muzzle-flash `stackpad2[-8]`) holds**, fire no longer crashes |
Earlier this session (per M-30 handoff), with-input PASS:
Facility 34, Runway 35, Surface1 36, Frigate 26, Statue 22, Bunker2 27,
Surface2 43, Streets 29, Depot 30, Train 25, Jungle 37, Cradle 41, Aztec 28.
**With-input sweep: 21/21 crash-free.** No new ABI/layout crashes found.
### stackpad / negative-stack-index audit
`grep -rnE '\)\[-[0-9]|[a-z0-9_]\[-[0-9]+\]|stackpad' src/game/*.c src/*.c`:
only `gunfire.c:830/836` (`((f32*)stackpad2)[-8]`) is a true decomp
register-alloc stackpad hack — already fixed by D158 (`#ifdef PORT`
real local `kf7_flashscale`). `front.c:5246-47` declares `s32 stackpad;`
locals but never negative-indexes them (benign, and front.c is off-limits).
All other `[-N]` hits are legitimate pointer walks (`gun.c` spline
`current[-1]`, `stan.c` `stack[-1]`, `initpathtablelinks.c`
`validationGroupCursors[-3]` already D89-handled, `options.c` `vtx[-1]`,
`decompress.c` `src[-1]`). `othermodemicrocode.c:368 (aa)[-4]` is a
texture-pool pointer walk, not a stack hack. **No further stackpad bugs.**