mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-24 00:02:45 +02:00
459 lines
35 KiB
Markdown
459 lines
35 KiB
Markdown
# v0.4.0 Plan — local working document (NOT pushed; update as items move)
|
||
|
||
Created 2026-09-21 from a fresh triage of `findings-index.csv`,
|
||
`GRAPHICS-BACKLOG.md`, `docs/BACKLOG.md`, `ROADMAP-1.0.md` (M-139 phase order),
|
||
`QOL-INVENTORY.md`, and the 2026-09-20 HANDOFF.
|
||
|
||
**Release goal (user-set):** fix everything that still makes the port not 1:1
|
||
with the N64 original; *then* improvements outside the OG game, plus stability.
|
||
Sequencing follows M-139's R → Q → F order, split into four phases below.
|
||
|
||
## Baseline
|
||
|
||
- **v0.3.0 is shipped** (tag on origin at `4827bc86`); `main` is 7 docs-only
|
||
commits ahead. Branch `release/v0.4.0` off `main` when Phase 1 starts.
|
||
- All 20 solo missions (plus the ending-credits sequence) load/render/crash-free; campaign completable end-to-end
|
||
(Agent, playtested). Steady 60 fps. Audio done. Saves done.
|
||
- Known-issue list = README "Known issues" section (D303, D245, D308/
|
||
D306, D307, D311, D318 disclosure, widescreen-stretch, D249,
|
||
D240/D241, D257/D259/D281 AllUnlocked caveat). ~~D252~~ retired (M-188),
|
||
~~D75~~ fixed (M-197, user-confirmed), ~~D236~~ fixed (M-198, pass 26 —
|
||
awaiting play-test sign-off).
|
||
|
||
## Triage of every open item (as of this triage)
|
||
|
||
### Bucket A — Fidelity (port diverges from N64 behavior/appearance/sound)
|
||
|
||
| ID | Item | State / notes |
|
||
|----|------|---------------|
|
||
| D320 | 17 aim-hold AI lists in 8 levels carry the as-authored D318 softlock race; Facility `ai_19`, Control `ai_9`, Depot `ai_12` high/med-high | Static sweep done; **live probe-verification owed** (GE_D318T-style timeline on the 3 lists). If one freezes: generalize `d318watchdog.c` (parameterize list/offset/chr/entityid), not per-list copies. N64 softlocks permanently — watchdog stays a port-side recovery, disclosed like D318. |
|
||
| D252 | Rainbow particles still intermittent on both platforms after the vertex-buffer fix | Second cause unrooted; prime suspect `import_texture_rgba16` fire-tile width/height from tile line size (32×7 vs 16×14). README known issue. |
|
||
| D245 | Water seam between two patterns, moves with view (REOPENED) | M-142 probe: tc-shift k flips during ordinary rotation; opt-in `GE_D245_FIXEDSHIFT` candidate build-verified, needs A/B vs N64 reference before making it default. |
|
||
| D303 | Wrong extra long muzzle flash (M16 + others) | **Investigated — pipeline + data ruled out.** Flash path (`gunUpdateAndFire` → `rwmtx[1]/[2]`, `dorottex` DLPRIMARY arms) is byte-identical game code; M16's `MuzzleFlashExtension`=6.0 is the legitimate max stat; Gm16Z flash-node data byte-faithful through d43. Headless runs can't fire weapons (guards don't shoot an idle player), so a TEMP `GE_D303=1` probe in `gfx_pc.cpp` logs every very-tall-thin emitted triangle with full RDP state — **blocked on a live firing capture** (playtest brief item 17). |
|
||
| D306/D308 | Z-fighting: Dam intro/truck wheels + general/broader instances | **Investigated — real fighting confirmed, port-vs-faithful open.** Depth precision is equivalent to N64 (24-bit both sides; GE's RSP writes full 16.16 post-transform coords, not s16), so level-authored coincident geometry is the prime suspect and likely fights on N64 too. TEMP `GE_ZF=1` flicker probe in `gfx_pc.cpp` (recurrence-filtered; `GE_ZFBOX` region filter) shows sustained oscillation (counts ~1000) in the Dam-intro truck region. Closing it as faithful needs an N64 reference of the same spot; a PC-only mitigation (float depth) would diverge from N64 and needs a rule-2-style decision. |
|
||
| D307 | In-level security camera props face backwards on some levels (Bunker) | **Investigated 2026-09-21 — no port-side bug found.** Full orientation path traced (`setupCctv` → per-frame `RotY(unkC8)×unk84`); every input verified N64-faithful against the ROM (PcctvZ switch data, boundpads incl. the legitimate load-time room-scale ×1.8542, record config words); all math helpers are byte-identical `src/game` code. Remaining suspect if it genuinely differs from N64: fast3d handling of the non-orthonormal lens matrix. **Blocked on user repro detail** (which camera/level + screenshot + ideally N64 compare). `GE_D307=1` diagnostic kept in-tree. |
|
||
| D236 | Surface 1 billboard trees render as a solid wall of texture | **FIXED (M-198, pass 26, `425237f9`):** root cause was a texture-BINDING bug — `TEXTURETYPE_DETAIL` cards drawn from the detail tile (opaque IA8 noise) instead of the base image; `gfx_lod_tile_offset` now samples tile 1 when the pair differs (`Video.DetailBaseTile`, default on). Live-verified Surface 1/2 + Depot; other levels provably untouched. Awaiting user play-test sign-off; probes stripped (`0503d335`). |
|
||
| D235 | Wrong textures after death/restart (level reload), multiple levels | **Headless verification 2026-09-23 (`2a6bc2bb`)**: fix clean on reload; bug NOT reproduced even with the fix off. Needs the real die → mission select → restart A/B (`GE_D235_OFF=1`). |
|
||
| D288 | Silo intro: huge flat untextured triangle sweeps the frame (frames ~562–741 of bare `-level_20`) | **Does NOT reproduce on the current build (2026-09-23, `30cf76b8`)**; D271 A/B ruled out as the fixer. Close if the Silo playtest does not see it. |
|
||
| D294 | Statue: CCCP monument ground renders solid black, self-recovers in 10–30 s | **FIXED pending user playtest** (2026-09-22) — plan row was stale. |
|
||
| D291 | Dam: texture glitches when the truck drives past | Not investigated. |
|
||
| D249 | Distant geometry drops out on Streets/Egyptian at default FOV | M-142 ruled out far-clip + LOD-multiplier; narrowed to portal/room-visibility BFS. **First check overlap with D271's portal-culling fix** (branch state, see Phase 0). Needs live screenshot+location. |
|
||
| D195 | Control: transparency/alpha-blended surfaces render wrong (glass partitions, holographic displays) | **FIXED** (index: gfx_sp_vertex fix) — plan row was stale (2026-09-23 reconcile). |
|
||
| D197 | Character face textures wrap around the head instead of clamping (Silo, intermittent) | **CLOSED** (user playtest v0.2.0-pre) — plan row was stale (2026-09-23 reconcile). |
|
||
| D182(2)/D176(b) | File-select background degrades after round-trip (comb/interlacing); Surface tree-line static — shared "texture pitch/shear" defect | M-36 Family A; `import_texture_*` line-size assumption. Distinct from the disproven D183 hypothesis. |
|
||
| D176(a) | Sky: RDPHALF tri-raster decode; horizon-band coverage (Defect 2) still open | Path B draft in `scratchpad/sky.patch`, phase-fold fix applied, **never visually verified** — one `-level_22` screenshot owed. Branch `land/d176a-sky-perspective-scroll` exists (forgejo remote) — audit + port to main. |
|
||
| D75 | Front-end logos: Nintendo logo degenerate (tiny orbiting fragment); Rareware filtering wrong | **FIXED (M-197, user-confirmed live 2026-09-21)**: D50 sidecar converter dropped embedded texture blobs. Remaining: pal/jpn sidecar regen on ROM-holding machines. |
|
||
| D290 | Watch in-game "controller" section shows no control-pad graphic | **FIXED 2026-09-23 (`a5804d10`)**: D264-pattern template read in `watchRenderController` + 3 pointer-width/raw-Gfx sites. Sibling sweep also found + fixed **D331** (shell casings never rendered, `94dff811`). |
|
||
| D174/D172 | Blood: decals never interactively verified (D120 needs sidecar regen); spray color fixed but owed an eyeball | BUNKER1 firefight with regenerated sidecars (`d43 && d69 && d88 --regen`). |
|
||
| D240/D241 | Gunshot SFX cadence drifts; PP7/AK47 goes silent under heavy fire near looping sounds | D241 high-confidence = D207 reproduced (check D207 entry status first — index row is stale/ambiguous). Fold into the D207 voice-cap family. |
|
||
| D322 | Long-session audio degradation (issue #87, v0.3.0 full campaign): from Silo onward audio worsens; by Caverns/Cradle OST inaudible + SFX "come and go"; restart cures → session-accumulating state | OPEN — static triage done (teardown audit clean, mixer stateless, D202 leak class covered only for ownerless `count==-1` loops and only validated single-level; D305 already records a pool/list desync). Ranked: voice-pool exhaustion/counter drift > queue starvation > evtq saturation. `GE_D322` pool-telemetry probe shipped 2026-09-21; needs a campaign capture with `GE_D322=1 GE_D204=1`. |
|
||
| D311 (residual) | Upstream anim-poison source still open (freeze class guarded) | `oldspeed=-1.75e18` at transition start; `/ unkb0` and `/ timespeed` denominators at model.c:3436/:3477. Known issue in v0.3.0 notes. |
|
||
| D258 | PAL/JP asset conversion broken at the source-data level | Repair path verified, not applied. Region fidelity — NTSC-only users unaffected, but it blocks "faithful in every region" and any JP/PAL ROM use. |
|
||
| D160 (residual) | Cutscene reliability PARTIAL (D243 family mostly fixed via M-189/M-190) | Residual = whatever the live GE_D160/GE_D243 campaign pass shows. |
|
||
|
||
### Bucket B — Stability (crashes/freezes/hangs; not N64-parity per se)
|
||
|
||
| ID | Item | State / notes |
|
||
|----|------|---------------|
|
||
| D289 | Dam computer-room crash (Deck, one occurrence, never repro'd) | Needs a human re-test on a current build (D255+). If it never recurs → close as untracked. |
|
||
| D287 | Windows: real, non-self-recovering freeze after ~15–20 min idle + click-back | One confirmed occurrence; `gfxFrameMsgQ` full is a lead. Consider crash-dump wiring on Windows for the next hit. |
|
||
| D286 | Deck: ~8 s self-recovering frame freeze on Caverns | One confirmed occurrence, no repro trigger. |
|
||
| D175 | Brief stutter/hang (door-open on Runway/Surface) | Matches known transient-hang classes; reproduce under `tools_pc/debug.ps1` idle-box before believing it's new. |
|
||
| D285 (residual) | Audio-thread race fix was PARTIAL | Re-check the thread-lock state vs D305's NULL guard coverage. |
|
||
| D281 | AllUnlocked breaks audio + RMB aim when enabled with zero saves written | User-facing mitigation shipped; proper fix = make the feature robust or gate it harder (see Phase 3 decision). |
|
||
| — | getenv caching in hot paths (**DONE 2026-09-23, `d9d7cd35`**) (post-release cleanup, HANDOFF M-192) | D302 fixed one instance; sweep for stragglers. Cheap perf/stability hygiene. |
|
||
| D117 | Frame nondeterminism (GE_DETERM attempted, not achieved) | Parked as a research item; do not block the release on it. |
|
||
|
||
### Bucket C — Improvements / QoL (outside OG game; after fidelity)
|
||
|
||
| ID | Item | Notes |
|
||
|----|------|-------|
|
||
| Widescreen | Currently **stretched 4:3**, not native — README discloses this | The big one. `WIDESCREEN-FOV-PLAN.md` Option B / CenterHUD path; needs the G-class sign-off decision (CenterHUD touches game code). |
|
||
| D237 | F10 overlay: mouse-wheel scroll, more settings, category grouping | Pure port-layer; wheel event already reaches the port. |
|
||
| D314/D315 | F10 flicker (user-deprioritized) + main-vs-in-game menu scaling | D315 deferred from v0.3.0; bigger design question — lands naturally with the overlay work. |
|
||
| D238 | Mouse sensitivity rework (master sens, RMB aim sens, Y-gain parity) | D304 already merged hipfire+F10 into one control; remainder is feel tuning in `input.c`. |
|
||
| D239 | Click-to-lock parity: delete always-grab mode 0 + config key | Small; closes D192 by deletion. |
|
||
| D226 | Player-adjustable HUD scale (ammo counter, pickup text, dialogue) | One funnel (`textRenderOutlined`); scale-about-anchor, menus excluded. |
|
||
| Rumble (D224) | Wire `osMotor*` → `SDL_GameControllerRumble` (PD precedent exists) | Deprioritized M-87; pick up if gamepad support is in scope for v0.4.0 — user call. |
|
||
| Per-pad tuning / hot-plug | PD incremental hot-plug + remembered seats; deadzone/scale/rumble per pad | Medium; real-controller users. |
|
||
| Window polish | HiDpi, center window, maximize/default-fullscreen | Small P-class wins. |
|
||
| Unlocked FPS | Plan parked; premise changed (D248/D250 fixed — default is now a true 60) | Phase 0 re-measurement before any work; motion interpolation stays out. |
|
||
|
||
### Bucket D — Infra / housekeeping
|
||
|
||
- **D244** self-hosted CI runner link failure — fix before the release tag so
|
||
CI can build win+linux bundles (or confirm GitHub runners suffice).
|
||
- Post-release v0.3.0 cleanup (HANDOFF): remove GE_OBJT probes
|
||
(`chraction.c` chrSet/UnsetStageFlags, `propobj.c` GAS line); M-183/M-185
|
||
clamp removal pending full-cutscene re-verification; D309 probe decision
|
||
(keep for v0.4.0 debugging or remove).
|
||
- Working-tree archaeology: ~~uncommitted D236 second-pass state~~ **RESOLVED
|
||
before this plan was written — `532092c7`/`6807555c` (Sep 15) already landed
|
||
the D266 IA4/texedge decode + D271 portal-culling fixes on main and dropped
|
||
the D267–D280 probes.** Consequence: Phase 1 item 9 (D249) can proceed with
|
||
the D271 fix in-tree; the D236 tree-wall work continues from D276's oml/fmt=3
|
||
lead on top of the landed decode fixes.
|
||
|
||
## Phases
|
||
|
||
### Phase 0 — Hygiene (½–1 session; do before any fidelity work)
|
||
1. Branch `release/v0.4.0` off `main`.
|
||
2. ~~Resolve the uncommitted D236/D271 tree state~~ already landed (`532092c7`).
|
||
3. Remove GE_OBJT probes + playtest-folder `.bat`; decide D309 probe fate;
|
||
getenv-cache sweep.
|
||
4. Fix/confirm CI runner (D244) so release tooling works end-to-end.
|
||
5. Carry-over verifications from the v0.3.0 handoff that were owed:
|
||
Runway AllUnlocked toggle re-test, menu-click spot check (D316/D317 both
|
||
closed — just confirm no regression on the new tree).
|
||
|
||
### Release gate (independent of the phases below)
|
||
|
||
**D322 (long-session audio degradation, issue #87)** is a campaign-breaking
|
||
defect in shipped v0.3.0 and does not wait for the phase ladder: the
|
||
`GE_D322` probe is already built and smoke-tested — request the campaign
|
||
capture (`GE_D322=1 GE_D204=1`) from the reporter now, and run its decision
|
||
rules as soon as the log lands. If the capture implicates a port-side leak,
|
||
the fix slots into Phase 2; if it's a rule-2 behavioral difference, it gets
|
||
its own sign-off item.
|
||
|
||
### Phase 1 — Fidelity wave (the release's core)
|
||
Ordered by (crash risk → visibility → effort); each item ends with its own
|
||
verification (headless where possible, human eyeball/ear as merge gate):
|
||
|
||
1. **D320 live verification + watchdog generalization** — the only open
|
||
item that can hard-lock progression on 8 levels; probes already exist.
|
||
2. **D289 re-test** (crash class; one human pass, then close or chase).
|
||
3. **Batch "one-off geometry/texture anomalies": D288 + D294 + D291** —
|
||
all are single-location, bounded, visually documented; likely share a
|
||
fast3d import/culling root cause. One investigation thread.
|
||
4. ~~**D252 rainbow particles second cause**~~ — **retired (M-188):** the
|
||
user's symptom closed as a duplicate of the D219 fix (live retest clean);
|
||
decode + dimensions proven correct (M-141/M-156). Re-open only on a
|
||
fireball-rainbow re-report.
|
||
5. **D245 water seam** — candidate fix exists; A/B against N64 reference,
|
||
then flip default (A1-class fidelity call).
|
||
6. **D303 + D306/D308 z-fighting family** — small visual parity items; batch
|
||
per commit cadence. (~~D307 camera flip~~ investigated 2026-09-21: no
|
||
port-side bug found, all inputs verified N64-faithful — blocked on user
|
||
repro detail; see Bucket A row.) D303 + D306/D308 both investigated
|
||
2026-07-19: no port-side bug found in either (pipeline/data/precision all
|
||
verified N64-faithful); capture probes `GE_D303` and `GE_ZF` are in-tree —
|
||
both now blocked on user-side captures (firing repro / N64 reference).
|
||
Nothing further to do agent-side until those land.
|
||
7. ~~**D236 tree wall**~~ — **done (M-198, pass 26):** `TEXTURETYPE_DETAIL`
|
||
tile-binding fix in fast3d (`Video.DetailBaseTile`); live-verified,
|
||
probes stripped. Only the user play-test sign-off remains.
|
||
8. ~~**D235 texture reload leak**~~ — **done (M-193):** port-side
|
||
`TextureCacheMap` now cleared on every stage transition via
|
||
`videoResetTextureCache()` in boss.c; 21/21 sweep pixel-identical; live
|
||
death/restart confirm is a playtest item.
|
||
9. **D249 culling** — *after* the D271 overlap check; live screenshot+location
|
||
first, BFS instrumentation second.
|
||
10. **Audio parity: D322 long-session degradation first** (issue #87 — the
|
||
only audio report that breaks a full campaign; `GE_D322` probe already
|
||
shipped, get the campaign capture, then apply the decision rules in the
|
||
finding), **then D240/D241** (fold into D207 family) + verify D230's bass
|
||
fix by ear on the elevator track.
|
||
11. **Front-end fidelity batch: D290 watch pad graphic, D182(2)/
|
||
D176(b) pitch-shear defect, D176(a) sky Defect 2** (verify the patch
|
||
visually first — it's been unverified for weeks). (~~D75 logos~~ fixed
|
||
M-197; ~~D195 Control alpha~~ FIXED per findings; ~~D197 face wrap~~
|
||
CLOSED at v0.2.0-pre playtest — both were stale in this list.)
|
||
12. **D174/D172 blood verification** (regen sidecars, BUNKER1 firefight).
|
||
13. **D258 PAL/JP asset conversion repair** — apply the verified repair path;
|
||
re-run the region sweep. Decide scope: ship JP/PAL-accurate or document
|
||
NTSC-only for v0.4.0 (user call).
|
||
14. **D311 upstream anim-poison source** — close out the last open thread of
|
||
the freeze class so the known-issue bullet can be dropped.
|
||
|
||
Exit criteria: every README "Known issues" bullet either fixed or explicitly
|
||
re-disclosed with a v0.5.0 pointer; no new fidelity regressions (21-level
|
||
sweep after each landing).
|
||
|
||
### Phase 2 — Stability wave
|
||
1. **D287/D286 freeze investigation** — wire Windows crash dumps (or an
|
||
in-process fault log) so the next occurrence is diagnosable; pattern-match
|
||
against `gfxFrameMsgQ` lead and the D309 teardown-race class.
|
||
2. **D175 stutter** — idle-box repro under `tools_pc/debug.ps1`; likely a
|
||
known transient-hang class, confirm and close or chase.
|
||
3. **D285 residual** — re-audit audio thread-locking vs D305's guard.
|
||
4. **D281 AllUnlocked no-save path** — make it robust (root-cause the
|
||
sndHandleEvent/aim interaction) or gate the toggle until a save exists.
|
||
User call on which.
|
||
5. Long-idle soak test (both platforms) as the stability acceptance bar.
|
||
|
||
### Phase 3 — Improvements / QoL (only after Phases 1–2 are green)
|
||
Order by user-visible value:
|
||
1. **Native widescreen** (letterbox/center instead of stretch) — the biggest
|
||
remaining "feels wrong" item; decide the CenterHUD G-class sign-off first.
|
||
2. **F10 overlay wave: D237 (wheel + categories + more settings) + D314/D315.**
|
||
3. **Input feel: D238 sensitivity rework, D239 delete always-grab, window
|
||
HiDpi/center; rumble + per-pad tuning if gamepad is in scope (user call).**
|
||
4. **D226 HUD scale** (F10 slider candidate alongside FovScale).
|
||
5. Unlocked FPS: re-measure Phase 0 against the post-D248/D250 baseline and
|
||
decide whether v0.4.0 or v0.5.0.
|
||
|
||
### Phase 4 — Release ritual
|
||
1. `./build-pc.sh ntsc-final` clean; `/linkcheck`; full 21-level
|
||
`tools_pc/verify.sh sweep --json`.
|
||
2. Full campaign playtest sign-off (user), incl. the D320-probe levels and a
|
||
water level + Surface + Silo (the Phase 1 visual fixes).
|
||
3. Refresh README Known Issues, `.github/release-notes.md`, findings index;
|
||
regenerate `agentic-development.md` stats from git (still owed since
|
||
v0.3.0 prep).
|
||
4. Push `release/v0.4.0` → tag `v0.4.0` → CI draft bundles → validate both
|
||
artifacts → fast-forward main.
|
||
|
||
## Explicitly out of scope for v0.4.0
|
||
- macOS/ARM, LAN multiplayer.
|
||
- Motion interpolation / sim-rate changes (UNLOCKED-FPS non-goals).
|
||
- D117 frame determinism (research-parked).
|
||
- **Mod support and HD texture packs — both post-1.0, both researched; see below.**
|
||
|
||
### Post-1.0, researched and tracked (do not start in v0.4.0)
|
||
|
||
Both were investigated 2026-09-22 against the Perfect Dark PC port fork
|
||
`perfect-dark-dabs-mod-dabs-mod` (**MIT**, Ryan Dwyer — same license as this
|
||
tree; `AGENTS.md` already names the PD port a standing reference, so code may be
|
||
adapted with attribution). Fuller sequencing notes live in the local
|
||
`docs/dev/notes/MODERN-PORT-FEATURES-BACKLOG.md` §5/§6, which is gitignored — the
|
||
durable record is `docs/dev/findings.md` and the summary here.
|
||
|
||
**Mod support (ROM patches) — viable, medium–large.** Findings **D325–D328**.
|
||
- Community mods *repack* the ROM, so vanilla offsets are useless: Goldfinger 64
|
||
(12→24 MB, 797/803 slices differ) and even the content-neutral Random Eye
|
||
randomizer (identical ROM size, yet 803/803 slices differ — content shifted
|
||
+7392). Both die in `tools_pc/d43_emit.py:273` on a vanilla-offset zlib slice.
|
||
- **The fix is not a loader rewrite.** PD's `port/src/modimport.c` states our own
|
||
constraint verbatim and instead diffs the patched ROM against stock, extracting
|
||
what differs into a **mod directory** its loader already reads. The game keeps
|
||
running its stock ROM at fixed compiled offsets.
|
||
- GE's file table is in the ROM after all (D327 said otherwise; corrected by
|
||
D328): 1172-compressed data segment at ROM `0x21990` — one raw inflate, which
|
||
`port/src/rzdecomp.c` already wraps — table at `0x252c4`, 727 records of 12
|
||
bytes, pointers `0x80020d90`-based, size by differencing adjacent addresses,
|
||
exactly `obInit` (`src/game/ob.c:126`). Verified: **681 exact offset+size
|
||
matches vs `filelist.u.csv`, 0 mismatches**; discovery needs **no hardcoded
|
||
offsets** and reads Random Eye's own table to **704/726 files byte-identical**.
|
||
- Remaining work: extraction in C (proven), patch decoding (PD's `rompatch.c` is
|
||
an MIT VCDIFF/BPS/IPS decoder — **removes the GPLv2 xdelta3 constraint**),
|
||
converters taking the extracted table, and the real effort: a
|
||
**mod-directory override layer** — `port/src/romdata.c` reads the ROM only and
|
||
has no analogue of PD's `romfile.moddir` file slots.
|
||
- **Hard limit:** mods that *add levels* need new stage-table entries in
|
||
`src/game` — rule 2, not the ABI/layout exception. Either a sign-off or scope
|
||
down to the existing 20 slots. ASM/code mods stay impossible (ROM code is never
|
||
executed here).
|
||
|
||
**HD texture packs — medium, well-scaffolded. Answers issue #100** ("High
|
||
Resolution Textures", open, `enhancement` — a user asking whether HD textures can
|
||
be used with the port). Architecture studied from PD's
|
||
`port/include/texpack.h` + `port/fast3d/gfx_pc.cpp`; no finding written yet, so
|
||
this entry and backlog §6 are the record.
|
||
- **Identity is the whole problem, not decoding.** The renderer only ever sees
|
||
the pointer `gDPSetTextureImage` was handed, so PD keeps an address →
|
||
texture-number registry populated where `texLoad()` runs and invalidates it
|
||
when the pool drops (a stale entry names the *wrong* texture). Then three-tier
|
||
matching: texture number → texel checksum (for textures embedded in model
|
||
files) → per-record. Packs are `textures/<texnum>.png`; decode happens on a
|
||
worker so loading does not hitch; the hook is inside `import_texture`, gated so
|
||
it costs one lookup when off.
|
||
- Most scaffolding exists here: our `gfx_pc.cpp` is the same lineage
|
||
(`gfx_texture_cache`, `:92,579`), `g_Textures[].texturenum` exists
|
||
(`src/game/chr.c:3894`), and **`GE_TEXDUMP` already dumps textures**
|
||
(`port/fast3d/gfx_pc.cpp:1205`) — it just names them by sequence
|
||
(`r000_f3_s1_64x64.bin`) rather than texture number, so dumps cannot round-trip.
|
||
Renaming that is the single change turning the probe into a pack dumper. A PNG
|
||
decoder is the one new dependency, and zlib is already required.
|
||
- Step 1 (the identity registry) is the only novel design decision, and it
|
||
records identity where textures load — a `src/game` touch needing `#ifdef PORT`
|
||
plus a §F note. Settle that at design time, not mid-implementation.
|
||
|
||
## Session log (agent-driven, 2026-09-20 evening)
|
||
|
||
**Phase 0 — done except user-side items:**
|
||
- `release/v0.4.0` cut off `main` (which already carried the D266/D271 fixes,
|
||
`532092c7` — the "uncommitted tree state" item was stale; plan updated).
|
||
- D322 probe work committed (`7bb43260`); GE_OBJT probes removed (D321 closed,
|
||
M-192 item 7, `56ac2ab8`); D309 probe **kept** (inert, cheap, recurrence
|
||
value for the D309/D310 freeze class).
|
||
- getenv hot-path sweep (D302 follow-up): function-level scan of all GE_*
|
||
sites found no frame/triangle-rate stragglers in fast3d; cached four
|
||
per-frame/per-hit guards in `src/game/bg.c` (GE_D104 room pass, GE_D63
|
||
primary+secondary render logs, GE_D154 inner GDL walk) — all `#ifdef PORT`,
|
||
mechanical.
|
||
- D244 (self-hosted CI runner): needs `pacman -Syu` on the box itself —
|
||
**user-side action**, not locally fixable; fallback (`#ifdef PORT` thin
|
||
wrapper for the `#pragma weak` alias) only if the toolchain update fails.
|
||
- 21-level sweep gate: running on the new tree (see `scratchpad/sweeps/`).
|
||
- Carry-over D316/D317 + Runway AllUnlocked re-tests are interactive → folded
|
||
into the big playtest brief below.
|
||
|
||
**Phase 1 — in flight:**
|
||
- Item 1 (D320): generic pin-signature detector added to `d318TimelineTick`
|
||
(`GE_D318T=1` now covers any chr: ACT_ATTACK + live-fire attacktype +
|
||
byte-stable animframe for 10 s → one `D320T: PIN?` line per chr, repeating
|
||
every 10 s while pinned). Detection only — the recovery watchdog stays
|
||
hardcoded to ai_22 per D320's own instruction until a second list freezes.
|
||
Live verification of Facility ai_19 / Control ai_9 / Depot ai_12 beats =
|
||
playtest work (see brief).
|
||
- Item 3 (D288/D294/D291 batch): D288 probe refined per M-155 follow-up
|
||
(any_behind_camera==0 only — the w<0 bbox math was a false lead; oml word
|
||
now logged for asset ID). D294: `GE_D104` room-visibility trace confirmed
|
||
sufficient (curRoom/roomsDrawn/budget + per-room load state every 10 s);
|
||
needs live Statue repro at FovScale≈130 + max draw distance. D291: still
|
||
needs live Dam truck details — lowest value of the three.
|
||
- Item 13 (D258 PAL/JP): **deferred pending user scope call** (ship
|
||
multi-region or document NTSC-only for v0.4.0) — no PAL/JP ROMs available
|
||
here to verify a blind CSV rename against.
|
||
- Item 4 (D235 wrong textures after death/restart): **mechanism found + fix
|
||
applied (M-193).** M-108's refutation of the game-side CI-cache theory
|
||
stands, but the *port* has its own `TextureCacheMap` (source-address keyed,
|
||
no content check) that was never invalidated on stage transitions —
|
||
`videoResetTextureCache()` existed with zero callers. Now wired into the
|
||
per-stage reset block in boss.c (`#ifdef PORT`, after `memaReset`).
|
||
Pixel-identical re-uploads; golden sweep running to confirm. Live
|
||
death/restart confirmation + a `GE_D235` >0 capture are playtest items.
|
||
- D311 upstream poison source (M-194): static pass done — all
|
||
`sub_GAME_7F06FE90` callers pass arg2=16.0f (denominator safe); the
|
||
old→new lerp only circulates values; `bheadAdjustAnimation` is the one
|
||
path where a live player-velocity corruption reaches `model->speed`. Both
|
||
speed-write choke points (`modelSetAnimSpeed`, `modelSetAnimation2`) now
|
||
log `D311: … ANOMALY` on |v|>1e4/NaN — next Caverns occurrence is a
|
||
one-line diagnosis. No env var (D156-guard pattern).
|
||
- Item 4 (D252 rainbow particles): **effectively closed — no agent work
|
||
left.** Re-read the finding this session: M-141 refuted the width/height
|
||
suspect (line=4 → correct 16×14, cross-confirmed by the GE_TEXDUMP
|
||
is_fire_shape filter), M-156 proved the ROM bytes decode to a clean
|
||
white-hot→ember progression, and **M-188 closed the user's actual symptom
|
||
(lingering residue sprites) as a duplicate of the D219 fix** — user
|
||
retested live: "seems fine now." Only a *fireball* rainbow re-report would
|
||
re-open it. Plan item 4 retired.
|
||
- Item 10 (D207 alarm klaxon): **blocked on user repro info, not agent-
|
||
workable.** M-77b implemented the designed last-resort LOOPED-preemption
|
||
pass + `[D207-DROP]`/`[D207-YIELD]` counters, BUNKER1 forced-alarm soak
|
||
showed zero drops / zero yields (pass 1 always finds a victim), and the
|
||
change was reverted. The finding now asks for: which level, which
|
||
weapon/situation, and a `GE_AUDIOTRACE=1` capture (or clip) of the failing
|
||
alarm — then decide between heavy-level 8/8 starvation (re-apply the
|
||
reverted pass), an alarm-stop-path leak, or pure perception (klaxon mix
|
||
volume vs N64). Added to the playtest brief as a low-effort ask.
|
||
- Full 21-level golden sweep: **21/21 PASS** on the final tree (`bb508e5c`),
|
||
confirming D235 fix + D311 probe + all Phase 0 changes are pixel-identical.
|
||
- **D75 (Nintendo logo) investigation — M-195, no root cause yet but two
|
||
suspects eliminated.** (a) Fresh `GE_D63` run: all 23 logo Primary GDLs
|
||
emitted every frame, zero unknown-opcode warnings → emission + DL
|
||
consumption fine; PC GDLs byte-faithful to N64 source. (b) New `GE_D75V`
|
||
per-batch vertex probe: transformed coords match the wordmark geometry
|
||
exactly (MP translation matches the lookat) → basematrix / f32→s32 /
|
||
matrix-list suspects eliminated. **Leading hypothesis:** every `G_VTX`
|
||
batch encodes v0=0, so multi-batch nodes overwrite shared vertex slots
|
||
0..n−1 (only the last batch survives; higher-indexed triangles read stale
|
||
slots) — consistent with the single tiny orbiting fragment observed. Next:
|
||
ROM-dump one node's GDL stream and cross-reference `G_TRI4` index ranges
|
||
against per-batch counts; fix would be port-layer fast3d slot management.
|
||
Probe code (`gfx_pc.cpp`, `videoGetFrameCount()` in video.c/h) uncommitted
|
||
at end of session — commit with the doc updates.
|
||
- **D307 (CCTV facing backwards) investigated — no port-side bug found.**
|
||
Env-gated `GE_D307=1` probe in `setupCctv` dumps every input + derived
|
||
orientation per record. Bunker1 (`UsetupsevbunkerZ`) decode vs live: model
|
||
switch offset = `PcctvZ` (NOT `GcameraZ`, that's the gun) sw[0]
|
||
bit-for-bit; boundpads match the pccg sidecar once the legitimate load-time
|
||
room-scale (`pad->pos *= get_room_data_float2()`, ≈1.8542 here) is applied;
|
||
record config words (incl. `unkCC`) match; all matrix/atan2 helpers are
|
||
byte-identical `src/game` code. Conclusion: PC orientation values == what
|
||
the N64 computes; if the symptom is real vs N64, the remaining suspect is
|
||
fast3d's handling of the non-orthonormal lens matrix (`unk84` row 0 scaled
|
||
0.1×). Blocked on user repro detail (which camera/level + screenshot).
|
||
Findings entry updated; probe kept in-tree (D60/D88 pattern).
|
||
- **D303 (extra long muzzle flash) investigated — no port-side bug found yet,**
|
||
blocked on live capture. Mapped the full flash pipeline (`gunUpdateAndFire`
|
||
builds `rwmtx[1]` = yaw×scale×flashext and `rwmtx[2]` from `Switches[2]`; the
|
||
flare arms are `dorottex` DLPRIMARY nodes) — all byte-identical game code,
|
||
matrix helpers verified index-for-index. Ruled out: weapon stats (M16's
|
||
flashext=6.0 is the legitimate max, sniperrifle matches; the ext≥3 ⊆
|
||
CAN_DUAL_WIELD overlap is design coincidence) and model conversion (Gm16Z
|
||
DLPRIMARY records + vertex blocks byte-faithful per d43 round-trip). Added a
|
||
TEMP `GE_D303=1` probe in `gfx_pc.cpp` (cached-getenv; logs very-tall-thin
|
||
emitted triangles with blend/oml/comb/prim/env/w) — 75 s unattended runs on
|
||
Bunker1/Dam/Depot show only static scene geometry, so the artifact needs a
|
||
live firing repro. Next: user fires the M16 under `GE_D303=1` and shares the
|
||
log lines; the spurious quad's RDP state will identify its source.
|
||
- **D306/D308 (z-fighting family) investigated — real fighting confirmed, cause
|
||
framed.** Verified GE's RSP writes post-transform x/y/z/w as full 32-bit 16.16
|
||
(not s16 8.8), so the port's float pipeline + `GL_DEPTH24_STENCIL8` matches N64
|
||
depth precision — no port-side precision deficit exists. Built a TEMP
|
||
`GE_ZF=1` flicker probe in `gfx_pc.cpp` (96×54 screen-cell grid, per-cell depth
|
||
history, requires non-monotonic a-b-a oscillation within GE_ZFTOL recurring ≥5×
|
||
at the same cell; `GE_ZFBOX` region filter) — earlier center-pair and simple
|
||
flicker variants saturated on tessellation noise. Dam (`-level_33`) unattended:
|
||
sustained hits in the lower-left truck region with per-cell oscillation counts to
|
||
~970 → D306's "transparent areas" are real depth-test alternation. Prime suspect
|
||
is level-authored coincident geometry (fights on N64 too); confirming port-only
|
||
needs an N64 reference of the same spot, and a mitigation (e.g. float depth)
|
||
would diverge from N64 — parked pending that.
|
||
- **Plan/doc refresh:** D75 (M-197) and D236 (M-198) marked fixed throughout
|
||
(Bucket A rows, Phase 1 items 6/7/11, playtest brief); findings index
|
||
regenerated; README Known Issues to match.
|
||
|
||
### Big playtest brief (for the user, when the tree is final)
|
||
|
||
Build: `./build-pc.sh ntsc-final`. Set **`GE_D322=1 GE_D204=1`** for the whole
|
||
campaign (D322 pool telemetry — the release gate). For the three D320 beats,
|
||
add **`GE_D318T=1`** on: Facility (the captive-scientists monologue/execution
|
||
beat), Control (the ai_9 guard beat), Depot (the execution beat) — watch for
|
||
`D320T: PIN?` lines; a frozen run keeps printing every 10 s.
|
||
|
||
Checklist beyond the normal play:
|
||
1. **D322**: full campaign, share `ge007.log` after (decision rules in the
|
||
finding).
|
||
2. **D316/D317 regression confirm**: click menu rows at a few window sizes,
|
||
esp. low rows (crop overscan bars) with SafeAreaCrop on/off; toggle
|
||
Game.AllUnlocked back and forth in-game.
|
||
3. **D289**: Dam computer room — interact with the terminals (was a one-off
|
||
crash, likely D255-class, already fixed — confirm it stays gone).
|
||
4. **D294**: Statue, set FovScale ≈130 + max DrawDistance/LodDistance, walk
|
||
past the CCCP monument at ground level; if the black-ground window hits,
|
||
note the time (a `GE_D104=1` run there is the follow-up capture).
|
||
5. **D291**: Dam — watch for texture glitches as the truck drives past;
|
||
screenshot + which surface.
|
||
6. **D175**: door-open on Runway/Surface — note any stutter/hang.
|
||
7. **D287**: leave a level idle+unfocused 15–20 min, click back (the
|
||
non-self-recovering freeze; one prior occurrence).
|
||
8. **D320 beats** as above with `GE_D318T=1`.
|
||
9. Audio by-ear: elevator track bass (D230 verify), PP7/AK47 under heavy fire
|
||
near looping sounds (D240/D241).
|
||
10. **D235**: on any level, die and restart (or reload a save) in the same
|
||
session, then look around — textures should be correct (no swapped/wrong
|
||
stage textures). Run once with `GE_D235=1` and share the log if anything
|
||
looks off (a `>0` count means a stale-cache hit slipped through).
|
||
11. **D288 (Silo)**: play Silo normally; if you see the wrong-color target /
|
||
geometry anomaly, set `GE_D288=1 GE_PCDUMP=1` for a 30–60 s run around it
|
||
and share log + captures (the probe logs the offending oml word for asset
|
||
ID).
|
||
12. **D311 (Caverns)**: play Caverns ~2 min of normal combat; if the anim-
|
||
speed freeze/poison recurs, the log will now carry a one-line
|
||
`D311: … ANOMALY` naming the corrupting call site — share it.
|
||
13. **D207 (alarm klaxon)**: if gunfire goes quiet during an alarm, note the
|
||
level + weapon + situation; ideally re-run that spot with
|
||
`GE_AUDIOTRACE=1` and share the log (or a short clip).
|
||
14. **D75 (front-end logos)**: confirm the Nintendo/Rareware logos + legal
|
||
page still render correctly (fixed M-197; user-confirmed once already).
|
||
15. **D236 (Surface trees)**: Surface 1/2 — confirm the treeline renders as
|
||
discrete tree sprites, not a wall (fixed M-198; probes stripped).
|
||
16. **D307 (security cameras)**: if any in-level CCTV camera looks like it's
|
||
facing backwards, note which level + which camera (screenshot) — the
|
||
orientation math is verified N64-faithful, so a concrete repro is what's
|
||
needed to keep chasing it.
|
||
17. **D303 (extra long muzzle flash)**: run with `GE_D303=1` in your terminal,
|
||
fire the M16 (and any other gun that shows the extra upward flash) until
|
||
you see it, then share the `D303:` log lines from around that moment —
|
||
each line carries the RDP state of a candidate quad and will identify the
|
||
spurious draw call's source.
|
||
|
||
## Conventions reminder
|
||
- One findings entry per investigation; next labels after **D322**.
|
||
- Fidelity fixes: port-layer or documented A1/ABI exception only; genuine
|
||
behavioral differences need the rule-2 sign-off (`docs/dev-process.md`).
|
||
- Every subagent reads `docs/porting-notes.md` first and appends to it.
|