From 61766e8aefe916f4ea4746269923caa996feff80 Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Mon, 21 Sep 2026 23:26:18 -0400 Subject: [PATCH] =?UTF-8?q?D307:=20investigate=20CCTV=20facing-backwards?= =?UTF-8?q?=20=E2=80=94=20no=20port-side=20bug=20found;=20GE=5FD307=20diag?= =?UTF-8?q?nostic=20in-tree?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setupCctv orientation path traced end-to-end and every input verified N64-faithful against the ROM for Bunker1: PcctvZ (not GcameraZ) switch data bit-for-bit, boundpads match the pccg sidecar once the legitimate load-time room-scale (pad->pos *= get_room_data_float2(), ~1.8542) is applied, record config words incl. unkCC match; all matrix/atan2 helpers are byte-identical src/game code. PC orientation values are what the N64 computes; if the symptom genuinely differs from N64 the remaining suspect is fast3d's handling of the non-orthonormal lens matrix (unk84 row 0 scaled 0.1x). Blocked on user repro detail. Also: plan/README/findings refreshed for M-197 (D75 fixed, user-confirmed) and M-198 (D236 closed); D252 retired; findings index regenerated. --- README.md | 12 ++------ docs/dev/V0.4.0-PLAN.md | 57 ++++++++++++++++++++++++++----------- docs/dev/findings-index.csv | 6 ++-- docs/dev/findings.md | 16 ++++++++--- src/game/prop.c | 34 ++++++++++++++++++++++ 5 files changed, 92 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 69febc4f..9b5fa32c 100644 --- a/README.md +++ b/README.md @@ -98,10 +98,6 @@ filtering, FOV, sensitivity); Windows and Linux, including Steam Deck. **Known issues:** -- Particle colours: a real fix landed (a corrupted-vertex-buffer bug), but - the rainbow effect can still occur intermittently on either platform, not - every time or on every level — a second cause hasn't been found yet - (D252). - Some muzzle flashes draw an extra, erroneous long flash straight up from the gun (seen on the M16, among others), overlaid on the normal, correctly drawn flash. Cosmetic only (D303). @@ -111,7 +107,9 @@ filtering, FOV, sensitivity); Windows and Linux, including Steam Deck. (D306). Minor and cosmetic. - In-level security camera props (not the player's own view) can occasionally end up facing backwards on some levels, seen on Bunker - (D307). + (D307). The orientation math has been verified faithful to the N64; if you + see it, a report of which level and which camera is what's needed to keep + chasing it. - Rarely, Bond's model can briefly render out of place after an animation transition — purely visual: no effect on gameplay or saves, and it clears on its own or by re-entering the level (D311). @@ -119,10 +117,6 @@ filtering, FOV, sensitivity); Windows and Linux, including Steam Deck. ~10 s before resuming the scripted shootout — a latent race that exists in the N64 original too (where it softlocks permanently); the port detects and auto-recovers it (D318). -- Some front-end 3D models are off: the spinning Nintendo logo renders as two - white blobs and the Rareware logo's texture filtering looks wrong (D75). -- Surface 1's 2D billboard trees render as a solid wall of tree texture - instead of discrete sprites (D236). Under active investigation. - **Widescreen is stretched, not native**: on non-4:3 windows the whole frame (world and HUD) is stretched horizontally to fill your display — about 33% wider than original at 16:9, like a 4:3 video in a TV's "stretch" diff --git a/docs/dev/V0.4.0-PLAN.md b/docs/dev/V0.4.0-PLAN.md index 6f6e769e..92eb4add 100644 --- a/docs/dev/V0.4.0-PLAN.md +++ b/docs/dev/V0.4.0-PLAN.md @@ -14,9 +14,11 @@ Sequencing follows M-139's R → Q → F order, split into four phases below. 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 (D252, D303, D245, D308/ - D306, D307, D311, D318 disclosure, D75, D236, widescreen-stretch, D249, - D240/D241, D257/D259/D281 AllUnlocked caveat). +- 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) @@ -29,8 +31,8 @@ Sequencing follows M-139's R → Q → F order, split into four phases below. | 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) | Reported only, not investigated. | | D306/D308 | Z-fighting: Dam intro/truck wheels + general/broader instances | Two reports, likely one family; investigate together. | -| D307 | In-level security camera props face backwards on some levels (Bunker) | Reported only. | -| D236 | Surface 1 billboard trees render as a solid wall of texture | Longest-running graphics bug (16+ passes). IA4 I2:A2 decode + AC-edge fixes are **in-tree uncommitted** and owe a game-wide IA4 regression sweep — commit or shelve decision first. Second-pass suspects: fog range > TEXTLOD/mip > density/culling > palette; D276 reframed oml=0xc81049d8/fmt=3 as primary suspect. | +| 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 | State-leak on re-entry: texture pool / CI-cache not fully reset. Needs fresh-boot-vs-reload confirmation + surfaces + screenshot, then instrument `texInitPool`/CI-cache clear. | | D288 | Silo intro: huge flat untextured triangle sweeps the frame (frames ~562–741 of bare `-level_20`) | Visually confirmed + precisely bounded; root cause unknown. | | D294 | Statue: CCCP monument ground renders solid black, self-recovers in 10–30 s | Not investigated. | @@ -40,7 +42,7 @@ Sequencing follows M-139's R → Q → F order, split into four phases below. | D197 | Character face textures wrap around the head instead of clamping (Silo, intermittent) | `G_TX_CLAMP` → GL mapping + non-PoT shifts (D167 family). Try `Video.WrapFix=1` on a repro first. | | 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 | **M-195 progress (2026-09-21):** emission + transform both proven correct (D63/D75V probes; zero unknown-opcode warnings; GDLs byte-faithful). Leading hypothesis: all `G_VTX` batches encode v0=0 → multi-batch nodes overwrite shared vertex slots 0..n−1, only the last batch survives. Next: cross-reference one node's G_TRI4 index ranges against per-batch counts (ROM dump) to confirm; fix is port-layer fast3d slot management if so. | +| D75 | Front-end logos: Nintendo logo degenerate (tiny orbiting fragment); Rareware filtering wrong | **M-195 progress (2026-09-21):** emission + transform both proven correct (D63/D75V probes; zero unknown-opcode warnings; GDLs byte-faithful). Leading hypothesis: all `G_VTX` batches encode v0=0 → multi-batch nodes overwrite shared vertex slots 0..n−1, only the last batch survives. Next: cross-reference one node's G_TRI4 index ranges against per-batch counts (ROM dump) to confirm; **SUPERSEDED by M-197 (same day): FIXED.** The D50 sidecar converter (`d43_emit.py`) reserved + remapped the models' embedded texture-blob spans but never copied the pixel bytes → TEXEL0 sampled a zeroed buffer → black-on-black. Tooling-only fix (blob raw-copy + round-trip validation); sidecars regenerated; **user-confirmed live** ("the images and legal page and Nintendo logo are good now"). Three files affected: `PnintendologoZ`, `PgoldeneyelogoZ`, `PlegalpageZ`. **Remaining:** pal/jpn sidecar regen on machines holding those ROMs. | | D290 | Watch in-game "controller" section shows no control-pad graphic | Not investigated (front-end). | | 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. | @@ -130,11 +132,13 @@ verification (headless where possible, human eyeball/ear as merge gate): 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 + D307 camera flip** — small visual - parity items; batch per commit cadence. -7. **D236 tree wall** — commit/shelve the in-tree decode fixes first, then - continue from D276's oml/fmt=3 lead. Longest pole; consider dispatching as - a subagent investigation while 4–6 land. +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.) +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 @@ -146,10 +150,10 @@ verification (headless where possible, human eyeball/ear as merge gate): 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: D75 logos, D290 watch pad graphic, D182(2)/ +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), D195 Control alpha, - D197 face wrap. + D197 face wrap. (~~D75 logos~~ fixed M-197.) 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 @@ -285,6 +289,21 @@ Order by user-visible value: 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). +- **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) @@ -327,10 +346,14 @@ Checklist beyond the normal play: 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)**: screenshot the Nintendo logo screen (still - expected-broken: only a tiny fragment renders — M-195 suspects vertex-slot - overwrite in fast3d) and the Rareware logo — confirms whether the v0.4.0 - tree changed anything there. +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. ## Conventions reminder - One findings entry per investigation; next labels after **D322**. diff --git a/docs/dev/findings-index.csv b/docs/dev/findings-index.csv index b97c6354..35d73611 100644 --- a/docs/dev/findings-index.csv +++ b/docs/dev/findings-index.csv @@ -7,7 +7,7 @@ D43-D50,RESOLVED,"model-file loading → offline sidecar ""Plan B"" (d43_emit.py D51-D58,RESOLVED,"font pixeldata, osGetCount tick rate, model RW pools, cseq BE header, synth slots, RLE menu bg, watch raw offsets, gun-barrel DL",resolved D59-D68,RESOLVED,"intro render: blood-RLE clobber, DMA validate, OSMesgQueue, HEADS/BODIES sentinels, romCopy width, image_entry, Globalimagetable BE→LE",resolved D70-D74,RESOLVED,"intro-logo pixels: C-array bswap, UV path, sinf/cosf DVAL(), texture-import truncation",resolved -D75,OPEN,front-end 3D model transforms (D77 audio split out below —,"OPEN (parked). M-32 triage (see §F ""D75 ADDENDUM""):" +D75,FIXED,front-end 3D model transforms (D77 audio split out below —,"FIXED (M-197, 2026-09-21; user-confirmed live):" D76 · D164,FIXED,Legal screen only draws line 1 —,"FIXED — this row and the ## D164 entry below were stale; the proposed one-line #ifdef PORT fix was actually committed 8dfc578f4 (2026-08-31), well before this correction (2026-09-18)." D159,FIXED,"front-end wallet-Bond photo ""interlaced""/combed (RC1 / D149) —",FIXED (#ifdef PORT no-op the swap in image.c) D161,FIXED,Depot (-level_30) ceiling = bright-blue speckle + radial rays (B2).,FIXED (port/fast3d/gfx_pc.cpp) @@ -132,7 +132,7 @@ D164,FIXED,"Disclaimer / legal screen only draws the first line (D76 root cause, D165,FIXED,"Front-end menu cursor doesn't feel like a mouse pointer (M-31, user bug B5 follow-up).","FIXED (port/src/input.c; Input.MenuPointerMode, Input.HipfirePitchSpeed added)." D166,FIXED,Hipfire mouse pitch is digital while yaw is analog —,FIXED (port/src/input.c).… D235,OPEN,Wrong textures load after dying/restarting a level —,OPEN — needs: does it repro on a *fresh* level boot (rules out reload-state) vs only after death/restart (confirms it); which surfaces; a screenshot.… -D236,OPEN,"Surface 1: 2D billboard trees render as a wall/sheet of tree texture instead of discrete camera-facing tree sprites (M-106, user QA report).","OPEN — pass 24 (2026-09-21, static) DECODED THE TREE TEXTURE (new tools_pc/texdecode.py ports image.c's LOOKUP/HUFFLOOKUP/RLELOOKUP codecs)." +D236,CLOSED,"Surface 1: 2D billboard trees render as a wall/sheet of tree texture instead of discrete camera-facing tree sprites (M-106, user QA report).","CLOSED (M-198, pass 26, 425237f9; probes stripped 0503d335):" D237,OPEN,"QoL ask (M-106, user QA):",OPEN — not designed; port-only D238,OPEN,"QoL ask (M-106, user QA):",OPEN — not designed; port-only (port/src/input.c + config.c) D239,CLOSED,"QoL ask (M-106, user QA):",CLOSED — executed in the v0.2.0 prep pass (port-only)… @@ -184,7 +184,7 @@ D303,OPEN,"Some muzzle flashes (M16 named specifically, ""and some others"") dra D304,FIXED,"Mouse sensitivity menu overhaul (user request, 2026-09-18):","FIXED — see full entry for both the range/parity pass and the same-day follow-up (collapsed to a single Input.MouseSensitivity menu row + fixed overlayUpdateScroll()'s unconditional bottom-anchor, th…" D305,FIXED,"Steam Deck SIGSEGV on the v0.3.0 pre-release build (Cavern, early in the level, Game.AllUnlocked previously enabled on the session/save) —",FIXED (defensive guard) — D306,OPEN,"Dam intro/truck wheels render with odd transparent-looking areas, possibly z-fighting (user QA, 2026-09-18).","OPEN — reported only, not investigated." -D307,OPEN,"In-level security camera entities can be flipped facing the wrong direction (backwards) on some levels, e.g.","OPEN — reported only, not investigated." +D307,OPEN,"In-level security camera entities can be flipped facing the wrong direction (backwards) on some levels, e.g.",OPEN (investigated 2026-09-21) — D308,OPEN,"Occasional z-fighting confirmed on some levels' geometry, general/broader than the D306 truck-wheel instance (user QA, 2026-09-18).","OPEN — reported/confirmed by the user, not investigated." D309,FIXED,Complete non-self-recovering freeze (Windows) returning from Caverns to the intro screen —,FIXED via D310 — user-playtest-confirmed 2026-09-18 (Caverns→intro clean; the spin record itself observed resuming correctly in the post-fix probe log). D310,FIXED,"RULE-2-SIGNOFF: chraiitemsize() mis-sizes 1-byte AI_PRINT records (NUL-scans forward), corrupting AI execution for any list containing an active PRINT —",FIXED — user-playtest-confirmed 2026-09-18 (v2: diff --git a/docs/dev/findings.md b/docs/dev/findings.md index 6a1edfd5..fc976ea6 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -391,7 +391,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D59–D68 | intro render: blood-RLE clobber, DMA validate, OSMesgQueue, HEADS/BODIES sentinels, romCopy width, image_entry, Globalimagetable BE→LE | resolved | | D69 · D78–D84 | stage load (`load_bg_file`): bg/stan offline sidecar (`d69_emit.py`) + StandTile/bg_room_data ABI | resolved | | D70–D74 | intro-logo pixels: C-array bswap, UV path, sinf/cosf `DVAL()`, texture-import truncation | resolved | -| D75 | front-end 3D model transforms (D77 audio split out below — resolved, unrelated cause) | OPEN (parked). **M-32 triage (see §F "D75 ADDENDUM"): (a) D73-scope-gap RULED OUT (gu tree fully endian-clean). Splits in two: Bug 1 = logo/photo transform = the parked D114/D116 fast3d viewport mirror (not game code). Bug 2 = absent animated models = category (b). M-32b runtime probe (`GE_D75=1`): `render_pos`/`dynAllocate`-arena hypothesis RULED OUT (render_pos valid + fresh each frame), model instances valid (nMtx 21/1), zero fast3d DL warnings — failure is downstream in `drawjointlist`/`dotube` vtx/node-DL resolution or an off-screen `basemtx`. Needs a drawjointlist-level probe. M-33 (upright captures, D168 fixed): "Bug 1 = D114/D116 mirror" retracted; **gun-barrel Bond RENDERS fine** (walk + fire, upright) — the "absent" reports were the flipped capture; **Nintendo logo genuinely broken** (renders as 2 white blobs, shifted left — a real `logoinst` transform/geometry bug, not a flip); cast roll not re-captured. See §F "D75 Bug 2 — M-33 UPDATE". **v0.2.0-pre playtest (user):** Nintendo logo still broken; Rareware logo nearly right but its texture filtering looks off; MISSION COMPLETE / mode-select screens have no 3D models at all, so the earlier "absent mode-select model" framing was inaccurate.** | +| D75 | front-end 3D model transforms (D77 audio split out below — resolved, unrelated cause) | **FIXED (M-197, 2026-09-21; user-confirmed live): the D50 sidecar converter dropped the models' embedded texture blobs (`PnintendologoZ`/`PgoldeneyelogoZ`/`PlegalpageZ`) — `d43_emit.py` now raw-copies + round-trip-validates them; NTSC sidecars regenerated. Remaining: pal/jpn regen on ROM-holding machines.** Historical trail — M-32 triage (see §F "D75 ADDENDUM"): (a) D73-scope-gap RULED OUT (gu tree fully endian-clean). Splits in two: Bug 1 = logo/photo transform = the parked D114/D116 fast3d viewport mirror (not game code). Bug 2 = absent animated models = category (b). M-32b runtime probe (`GE_D75=1`): `render_pos`/`dynAllocate`-arena hypothesis RULED OUT (render_pos valid + fresh each frame), model instances valid (nMtx 21/1), zero fast3d DL warnings — failure is downstream in `drawjointlist`/`dotube` vtx/node-DL resolution or an off-screen `basemtx`. Needs a drawjointlist-level probe. M-33 (upright captures, D168 fixed): "Bug 1 = D114/D116 mirror" retracted; **gun-barrel Bond RENDERS fine** (walk + fire, upright) — the "absent" reports were the flipped capture; **Nintendo logo genuinely broken** (renders as 2 white blobs, shifted left — a real `logoinst` transform/geometry bug, not a flip); cast roll not re-captured. See §F "D75 Bug 2 — M-33 UPDATE". **v0.2.0-pre playtest (user):** Nintendo logo still broken; Rareware logo nearly right but its texture filtering looks off; MISSION COMPLETE / mode-select screens have no 3D models at all, so the earlier "absent mode-select model" framing was inaccurate.** | | D76 · D164 | **Legal screen only draws line 1 — root-caused (M-31)**: linker-adjacency assumption broken by mingw. — full `## D164` entry at file tail | **FIXED** — this row and the `## D164` entry below were stale; the proposed one-line `#ifdef PORT` fix was actually committed `8dfc578f4` (2026-08-31), well before this correction (2026-09-18). Docs simply never caught up to the merge. | | D159 | front-end wallet-Bond photo "interlaced"/combed (RC1 / D149) — `texSwapAltRowBytes` odd-row 8-byte pre-swap (N64 RDP odd-line TMEM XOR compensation) not reversed by fast3d | FIXED (`#ifdef PORT` no-op the swap in `image.c`) | | D161 | Depot (`-level_30`) ceiling = bright-blue speckle + radial rays (B2). A CI8 tile drawn with `gsDPSetTextureLUT(G_TT_NONE)` was decoded against the stale `rdp.palette` → garbage. Fix: `#ifdef`-free narrow route CI→I when `palette_fmt == G_TT_NONE` in `gfx_pc.cpp import_texture()`. | FIXED (`port/fast3d/gfx_pc.cpp`) | @@ -539,7 +539,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D233 | **All levels (intermittent): indoor level geometry invisible near doors — you can see through walls/floors into rooms you can't see into, while world objects…** — full `## D233` entry at file tail | **FIXED + VISUALLY VERIFIED (M-107 fix, M-108 verify).** See the other D233 row for full detail. | | D234 | **Dam: guard towers along the dam structure invisible except the first one (M-106, user QA report).** — full `## D234` entry at file tail | **VISUALLY VERIFIED FIXED (M-108), mechanism unexplained.** See the other D234 row for detail. | | D235 | **Wrong textures load after dying/restarting a level — not Facility-specific, seen on multiple levels (M-106, user QA report; clarified same session).** — full `## D235` entry at file tail | OPEN — needs: does it repro on a *fresh* level boot (rules out reload-state) vs only after death/restart (confirms it); which surfaces; a screenshot.… | -| D236 | **Surface 1: 2D billboard trees render as a wall/sheet of tree texture instead of discrete camera-facing tree sprites (M-106, user QA report).** — full `## D236` entry at file tail | OPEN — **pass 24 (2026-09-21, static) DECODED THE TREE TEXTURE** (new `tools_pc/texdecode.py` ports image.c's LOOKUP/HUFFLOOKUP/RLELOOKUP codecs). **`tex1198` LOD 0 = 64×17 RGBA5551, 44.2% opaque, and its 1-bit alpha renders as an unmistakable treeline silhouette** — pass 23's identification confirmed by decode, not inference. **Rare authored a six-level LOD pyramid that PRESERVES the silhouette and its alpha coverage (44.2% → 43.1% → 38.8%), each level a separately-authored image with its own compmethod.** Since `XLU_SURF` has no alpha test (pass 22), averaging that 0/1 alpha into fractional mip alpha would smear the minified treeline into exactly the reported band — so 'authored LOD chain replaced by driver-generated mips' is now the leading mechanism, with hard ground truth behind it. Corrects pass 23: only `tex1198` is a treeline (`tex1199` 89.4% opaque denser foliage; `tex1200`/`tex1201` 100% opaque detail textures sharing the class). Two decoder bugs found while porting (missing per-image byte-align, `image.c:1096`; a GUESSED rather than ported RLE fudge) — both produced confident wrong pictures. **Pass 25 (live) answered all three questions: (1) YES 64×17 RGBA16 intact, pool ≡ offline decode; (2) YES alpha survives (44.2% opaque); (3) NO — no code path uploads authored chains as GL mips.** New: `tex1198`–`1201` are **never drawn from an idle spawn** (zero RGBA16 imports in 4800 frames) — the spawn wall is painted by other classes (cf D276/D280); the drawn tree-class card is **`tex149` IA8 32×64 hard cutout, byte-exact import, no authored chain**. Mip mechanism located: `texGetDepthAndSize` sizes LOADBLOCKs from the header `lods` field even for single-image textures (live A/B: 2808 B vs 2048 B real data), and the port's RC2 clip + `glGenerateMipmap`/trilinear (observed live) box-filters hard 0/F alpha into fractional mips → XLU smear. Pass 26 fix directions in entry (cheapest: no mip gen for no-chain textures). | +| D236 | **Surface 1: 2D billboard trees render as a wall/sheet of tree texture instead of discrete camera-facing tree sprites (M-106, user QA report).** — full `## D236` entry at file tail | **CLOSED (M-198, pass 26, `425237f9`; probes stripped `0503d335`): root cause = `TEXTURETYPE_DETAIL` tile binding — fast3d drew 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; awaiting user play-test sign-off.** Historical trail — pass 24 (2026-09-21, static) DECODED THE TREE TEXTURE** (new `tools_pc/texdecode.py` ports image.c's LOOKUP/HUFFLOOKUP/RLELOOKUP codecs). **`tex1198` LOD 0 = 64×17 RGBA5551, 44.2% opaque, and its 1-bit alpha renders as an unmistakable treeline silhouette** — pass 23's identification confirmed by decode, not inference. **Rare authored a six-level LOD pyramid that PRESERVES the silhouette and its alpha coverage (44.2% → 43.1% → 38.8%), each level a separately-authored image with its own compmethod.** Since `XLU_SURF` has no alpha test (pass 22), averaging that 0/1 alpha into fractional mip alpha would smear the minified treeline into exactly the reported band — so 'authored LOD chain replaced by driver-generated mips' is now the leading mechanism, with hard ground truth behind it. Corrects pass 23: only `tex1198` is a treeline (`tex1199` 89.4% opaque denser foliage; `tex1200`/`tex1201` 100% opaque detail textures sharing the class). Two decoder bugs found while porting (missing per-image byte-align, `image.c:1096`; a GUESSED rather than ported RLE fudge) — both produced confident wrong pictures. **Pass 25 (live) answered all three questions: (1) YES 64×17 RGBA16 intact, pool ≡ offline decode; (2) YES alpha survives (44.2% opaque); (3) NO — no code path uploads authored chains as GL mips.** New: `tex1198`–`1201` are **never drawn from an idle spawn** (zero RGBA16 imports in 4800 frames) — the spawn wall is painted by other classes (cf D276/D280); the drawn tree-class card is **`tex149` IA8 32×64 hard cutout, byte-exact import, no authored chain**. Mip mechanism located: `texGetDepthAndSize` sizes LOADBLOCKs from the header `lods` field even for single-image textures (live A/B: 2808 B vs 2048 B real data), and the port's RC2 clip + `glGenerateMipmap`/trilinear (observed live) box-filters hard 0/F alpha into fractional mips → XLU smear. Pass 26 fix directions in entry (cheapest: no mip gen for no-chain textures). | | D237 | **QoL ask (M-106, user QA): F10 options overlay — make it scrollable with the mouse wheel, expose more settings into it (e.g. screen-flash / no-hit-flash…** — full `## D237` entry at file tail | OPEN — not designed; port-only | | D238 | **QoL ask (M-106, user QA): mouse sensitivity rework toward normal PC-shooter feel.** — full `## D238` entry at file tail | OPEN — not designed; port-only (`port/src/input.c` + `config.c`) | | D239 | **QoL ask (M-106, user QA): click-to-lock vs always-grab parity.** — full `## D239` entry at file tail | CLOSED — executed in the v0.2.0 prep pass (port-only)… | @@ -592,7 +592,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D304 | **Mouse sensitivity menu overhaul (user request, 2026-09-18): aim speed/turn speed sliders weren't "1:1", had too little range, and (follow-up) risked decoupling into a bad state; F10 menu also had a click-jump bug.** — full `## D304` entry at file tail | FIXED — see full entry for both the range/parity pass and the same-day follow-up (collapsed to a single `Input.MouseSensitivity` menu row + fixed `overlayUpdateScroll()`'s unconditional bottom-anchor, the actual cause of the reported "menu jumps to the setting below on click"). Build-verified (Windows) both passes; headless overlay smoke test clean after each. **Live F10 playtest done (2026-09-18): user confirmed the collapsed single-sensitivity menu works, the click-jump is gone, and the D251 bottom-row duplication no longer occurs.** | | D305 | **Steam Deck SIGSEGV on the v0.3.0 pre-release build (Cavern, early in the level, `Game.AllUnlocked` previously enabled on the session/save) — same exact FAULT ADDR 0x62 / `sndHandleEvent` signature as D285, reproduced AFTER D285's own lock fix was already live-verified crash-free on real hardware.** — full `## D305` entry at file tail | FIXED (defensive guard) — `sndHandleEvent`'s preempt-scan (`src/snd.c`) dereferenced its list-walk pointer before ever checking it for NULL (a do-while always runs its body once); D285's `osSetIntMask` lock only prevents another thread from emptying the list mid-scan, not a scan that starts with an already-empty list, so the crash could still happen even with that fix in place. Added the missing `iterState != NULL` guard before the loop, matching D255's established skip-instead-of-dereference precedent. Root desync mechanism (why pool-exhaustion accounting and the list can disagree) still not identified; `Game.AllUnlocked` correlation not proven, only reported. **Live-verified on the Deck (2026-09-18):** user ran two test sessions on Cavern with the updated build, plus toggled `Game.AllUnlocked` off then back on and relaunched — no crash in any of them. | | D306 | **Dam intro/truck wheels render with odd transparent-looking areas, possibly z-fighting (user QA, 2026-09-18).** — full `## D306` entry at file tail | OPEN — reported only, not investigated. Explicitly not for v0.3.0. | -| D307 | **In-level security camera entities can be flipped facing the wrong direction (backwards) on some levels, e.g. Bunker (user QA, 2026-09-18) — NOT the player's view camera.** — full `## D307` entry at file tail | OPEN — reported only, not investigated. Explicitly not for v0.3.0. | +| D307 | **In-level security camera entities can be flipped facing the wrong direction (backwards) on some levels, e.g. Bunker (user QA, 2026-09-18) — NOT the player's view camera.** — full `## D307` entry at file tail | OPEN (investigated 2026-09-21) — no port-side bug found: orientation path traced, every input verified N64-faithful vs ROM (`PcctvZ` switch data, room-scaled boundpads, config words), all math byte-identical `src/game`. Blocked on user repro detail; `GE_D307=1` diagnostic in-tree. | | D308 | **Occasional z-fighting confirmed on some levels' geometry, general/broader than the D306 truck-wheel instance (user QA, 2026-09-18).** — full `## D308` entry at file tail | OPEN — reported/confirmed by the user, not investigated. Explicitly not for v0.3.0; D306 may be one instance of this. | | D309 | **Complete non-self-recovering freeze (Windows) returning from Caverns to the intro screen — boss thread spins forever in `ai()` on `m_RunToBondPersistent` via a phantom `GotoNext(lblZero)` created by `chraiitemsize`'s `AI_PRINT` NUL-scan mis-sizing a stringless PRINT record (user report + live GDB diagnosis, 2026-09-18).** — full `## D309` entry at file tail | **FIXED via D310 — user-playtest-confirmed 2026-09-18** (Caverns→intro clean; the spin record itself observed resuming correctly in the post-fix probe log). The teardown-ordering open question (why PC presents the unreachable-Bond state) remains documented but is now harmless. | | D310 | **RULE-2-SIGNOFF: `chraiitemsize()` mis-sizes 1-byte `AI_PRINT` records (NUL-scans forward), corrupting AI execution for any list containing an active PRINT — fixes D309's freeze mechanism and the Caverns standing-still guards (`Stop`+`PRINT("wait")` mis-lands on EndList, re-executed every tick; user report 2026-09-18).** — full `## D310` entry at file tail | **FIXED — user-playtest-confirmed 2026-09-18** (v2: global-list-only sizing via `d310ItemSize`; guards resume, Trevelyan pathing intact, Caverns→intro clean; probe log verified both list classes). PD precedent: PRINT removed, slot 0xAD reassigned. | @@ -12227,7 +12227,15 @@ do { **Reported only, not investigated, explicitly not for v0.3.0.** User playtest: on some levels (Bunker named specifically), the in-level **security camera props/entities** — the gameplay objects that spot the player and alert guards, not the player's own view camera — can end up facing backwards (the wrong direction) "for some reason." No further repro detail given (trigger condition, whether it self-corrects, whether it's tied to a specific spawn/setup path). No code read done. **Correction (same day): initially misread as the player's view camera — clarified by the user to mean the in-level security camera entities specifically.** -**Status:** OPEN — logged, no investigation started. Deferred past v0.3.0 by user instruction. +**Investigation (v0.4.0 prep, 2026-09-21):** the full orientation path was traced and every input verified N64-faithful; **no port-side divergence found**. + +- Orientation is computed once at load in `setupCctv()` (`src/game/prop.c`): watchpoint = `obj->mtx · Switches[0]->Data + prop->pos`; look basis = `set_basis_and_position_target(watch − padpos, up=(0,1,0))` with row 0 scaled by model scale (0.1); yaw `unkC4 = atan2f(tx, tz)`; initial turret angle `unkCC` from record config word (N64 w51). Per-frame render: `mtxs[1] = RotY(unkC8) × unk84` (`propobj.c` ~5964). All matrix/atan2 helpers are byte-identical `src/game` code (`matrixmath.c`, `math_atan2f.c`) — none are port forks. +- Data verified against the ROM for Bunker1 (`UsetupsevbunkerZ`, level_09, 4 CCTV records): model switch offset `(0, 70.3664, 31.9837)` matches `PcctvZ` (NOT `GcameraZ` — that's the gun; PitemZ idx 24 = `assets/obseg/prop/cctv`) node sw[0] bit-for-bit; boundpad positions match the pccg sidecar exactly once the legitimate load-time room-scale is applied (`pad->pos *= get_room_data_float2()`, 1/level-scale ≈ 1.8542 for Bunker1 — initially looked like a mismatch because raw file values are pre-scale); `unkCC` raw angles match record config words; prop placement derives from pad + bbox via the standard `moveToPad` path. +- An env-gated diagnostic (`GE_D307=1`, TEMP, load-time only) dumps every input and derived value per CCTV record for offline ROM comparison — kept in-tree like D60/D88. + +**Conclusion so far:** given byte-identical code + verified-identical inputs, the PC orientation values are what the N64 computes. If the cameras genuinely differ from N64 hardware, the remaining suspect is fast3d's handling of the non-orthonormal lens matrix (`unk84` row 0 scaled 0.1×) in the per-frame render path — but that is unproven. **Blocked on user repro detail:** which camera/level, a screenshot, and ideally an N64 comparison (the symptom may be faithful N64 level-design behavior). + +**Status:** OPEN (investigated — no port-side bug found; awaiting repro detail). ## D308 — Occasional z-fighting confirmed on some levels' geometry, general/broader than the D306 truck-wheel instance (user QA, 2026-09-18). diff --git a/src/game/prop.c b/src/game/prop.c index 17378cee..84c38df1 100644 --- a/src/game/prop.c +++ b/src/game/prop.c @@ -688,6 +688,40 @@ void setupCctv(s32 arg0, CCTVRecord *arg1, s32 cmdindex) arg1->unkC8 = arg1->unkCC; arg1->unkC4 = atan2f(sp44.f[0] - sp50->pos.f[0], sp44.f[2] - sp50->pos.f[2]); arg1->timer = 0; + +#ifdef PORT + /* D307 probe: dump every input + the derived orientation so a PC run + * can be compared word-for-word against a ROM decode of the same + * record (facing-backwards CCTV report). Env-gated, load-time only. */ + if (getenv("GE_D307")) { + fprintf(stderr, + "D307: cctv cmd=%d obj=%d pad=%d bound=%d pos=(%.6g,%.6g,%.6g) " + "mtx=(%.6g,%.6g,%.6g %.6g,%.6g,%.6g %.6g,%.6g,%.6g) " + "sw=(%.6g,%.6g,%.6g) watch=(%.6g,%.6g,%.6g) padpos=(%.6g,%.6g,%.6g) " + "target=(%.6g,%.6g,%.6g) C4=%.6f CC=%.6f basis=(%.6g,%.6g,%.6g %.6g,%.6g,%.6g %.6g,%.6g,%.6g)\n", + cmdindex, arg1->obj, arg1->pad, !isNotBoundPad(arg1->pad), + arg1->prop->pos.f[0], arg1->prop->pos.f[1], arg1->prop->pos.f[2], + arg1->mtx.m[0][0], arg1->mtx.m[0][1], arg1->mtx.m[0][2], + arg1->mtx.m[1][0], arg1->mtx.m[1][1], arg1->mtx.m[1][2], + arg1->mtx.m[2][0], arg1->mtx.m[2][1], arg1->mtx.m[2][2], + temp_a2->f[0], temp_a2->f[1], temp_a2->f[2], + sp44.f[0], sp44.f[1], sp44.f[2], + sp50->pos.f[0], sp50->pos.f[1], sp50->pos.f[2], + sp44.f[0] - sp50->pos.f[0], sp44.f[1] - sp50->pos.f[1], sp44.f[2] - sp50->pos.f[2], + arg1->unkC4, arg1->unkCC, + sp3C->m[0][0], sp3C->m[0][1], sp3C->m[0][2], + sp3C->m[1][0], sp3C->m[1][1], sp3C->m[1][2], + sp3C->m[2][0], sp3C->m[2][1], sp3C->m[2][2]); + if (arg1->pad == 10018) { + for (int d307i = 16; d307i <= 24; d307i++) { + struct PadRecord *d307p = &g_CurrentSetup.boundpads[d307i]; + fprintf(stderr, "D307: bpad[%d] ptr=%p pos=(%.6g,%.6g,%.6g)\n", + d307i, (void *)d307p, + d307p->pos.f[0], d307p->pos.f[1], d307p->pos.f[2]); + } + } + } +#endif } }