mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-24 00:02:45 +02:00
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.
279 lines
17 KiB
Markdown
279 lines
17 KiB
Markdown
# 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.**
|