Commit Graph
17 Commits
Author SHA1 Message Date
James Dansereau 3e5720c01b D74: rework sub-tile UV pre-wrap as opt-in Video.WrapFix (was dead + OOB)
The D74 block in gfx_sp_tri was inert (guard `cms & G_TX_WRAP` == `& 0`,
always false) AND OOB (`tex_width2[i]` indexed by the vertex i=0..2, but
the array is per-texunit [2]). SMALL-FIXES B1: the naive in-place
correction boot-crashed -level_09.

Rework per the B1 plan: precompute per-texunit `wrap_s/t[t]`,
`wrap_tw/th[t]`, `wrap_uls/ult[t]` once in the texunit setup loop (needs
the original cms/cmt before the CLAMP-clear), keyed on 'clamp bit not
set' + window smaller than the uploaded image; apply per vertex indexed
by `t`. Gated behind `Video.WrapFix` (default 0 -- new/never-run path).
Verified: WrapFix=0 unchanged; WrapFix=1 -level_09 renders clean through
frame 375, no boot crash, no visible corruption. Visual fix on wrapped
triangle textures (room panels) needs a level with them + an N64
reference -- Depot per B1.
2026-08-31 11:40:42 -04:00
James Dansereau ddef038238 fast3d RC2: clip mip-contaminated texture uploads to base height (+ GE_DTEX probe)
Reported: menu / room textures look 'interlaced' (alternating garbage
rows). Root cause (docs/TEXTURE-GLITCH-ANALYSIS.md RC2): GE's
texGetDepthAndSize() packs base level + every LOD mip into one
gDPLoadBlock, so fast3d's upload height (size_bytes / row) runs ~1.3x
taller than the base image and the mip bytes render as rows below it.
GE_DTEX=1 confirmed it: 64x64 I4 uploaded 64x87, 65x65 -> 65x95, etc.

Fix in import_texture(): when LOD is active (rdp.tex_lod) and the load
is a plain full-width block (not a windowed gfx_dp_load_tile - the D74
sub-tile / Rare-logo case, which is lod=0), clip size_bytes to the
SETTILESIZE base-tile height. GL then builds correct mips from a correct
base. New knob Video.FixMipTextures (default 1; =0 restores old upload,
verified byte-identical to golden -level_09 3/3). BUNKER1 with the fix
on: side-wall textures visibly cleaner, geometry/HUD unchanged; Silo
unregressed. -level_09/-level_20 framediff WILL flag with the fix on
(textures changed for the better) - refresh golden after the user
confirms the menu + Depot look right.

New GE_DTEX=1 env probe logs load params for the first 64 textures.
2026-08-31 08:25:34 -04:00
James Dansereau 7d31654f2e D2/D146: strip unconditional D63 debug trail from gfx_pc.cpp
The D63 branch-trail dump fired ~10 sysLogPrintf lines + a full trail
walk on EVERY unknown GBI opcode, unconditionally. On the front-end
menu (D149 corrupt model DL) this ran per-frame and collapsed the menu
to ~5fps with input starved. Removed the trail statics, the per-G_DL
recording, and the default-case dump; the rate-limited D146 'ending DL'
log stays. -level_09 3/3 framediff, -level_20 91.6% unregressed.
2026-08-31 07:56:35 -04:00
James Dansereau 609bb732b9 M-29: aim sensitivity fix (BACKLOG B3 / SMALL-FIXES B4)
port/src/input.c: RMB manual-aim mouse sensitivity overshot badly at the
old MouseAimSpeed=50 default (25 px/poll saturated the aim band, so a
flick ran GE's (stick-60)/10 curve at full rate for many polls). Lower
the default to 25 and expose the band itself as Input.AimBand (int 5..40,
default 20 -> unchanged behaviour at the new speed). Port-only, config-
backed, fully reversible via ge007.ini.

port/fast3d/gfx_pc.cpp: annotate the dead D74 texture-wrap block
(SMALL-FIXES B1). The in-place correction (G_TX_CLAMP test + per-texunit
index) activates never-run code and crashes -level_09 at boot; it needs
the hoist-out-of-vertex-loop rework + per-level visual verification, not
a one-liner. Left inert with a NOTE; no behaviour change.

Verified: build green (ntsc-final); -level_09 + -level_20 crash-free for
a full ~75s capture window on a settled machine (D153: earlier back-to-
back runs crashed under build/run thrash, not a regression).
2026-08-30 20:49:50 -04:00
James Dansereau a82e90d457 M-27: D146 hardening - guard gfx_sp_vertex/movemem/set_vertex_colors ptrs
A desynced/corrupt front-end display list (D75 family - the compiled
wallet/dossier/logo sub-DLs at seg5+offset) feeds wild pointers to the RSP
command handlers before the walk reaches an unknown opcode. Added a shared
fast3d_ptr_ok() mapped-range check; gfx_sp_vertex, gfx_sp_movemem and
gfx_sp_set_vertex_colors now bail on a wild pointer instead of faulting.
Pairs with the D146 unknown-opcode -> end-DL change.

Verified: -level_09 unregressed.
2026-08-30 15:30:16 -04:00
James Dansereau cea9ce0df9 M-27: D146 unknown-GBI-opcode -> end DL instead of abort(); tools_pc/debug.ps1
D146 (port/fast3d/gfx_pc.cpp): an unrecognised opcode in the DL walk called
sysFatalError() -> abort() -> whole process dies with no ge007.crash.log
(the crash handler does not cover abort). Seen on the real post-Dam
MISSION COMPLETE screen: "[FATAL] Unknown GBI opcode 0x40c03 at
0x70161a8c" - a front-end 3D model (dossier, D75/D144 family) whose display
list was never built, sitting at a valid DRAM address full of junk. D144
guarded the bad matrix pointer; the walk then hit more garbage. Now: log
loudly (rate-limited x20) and `return` to end the (sub-)DL so the frame
finishes. A real level-render desync is still visible in the log.

tools_pc/debug.ps1: launch the port under gdb from the repo root so a
playtest crash always leaves a backtrace (abort/fatal or silent exit).
-Menu <id> sets GE_STARTMENU; passes through -level_XX etc. Prints the
FATAL line + backtrace + crash log when the game exits.

Verified: GE_STARTMENU=13 mission-complete -> Next chain renders, no abort,
no crash (D75 model cosmetics aside).
2026-08-30 15:27:23 -04:00
James Dansereau 70793eba4f M-27: D143 null-string text guard + D144 bad-matrix-ptr fast3d guard
D143 (src/game/textrelated.c): langGet() returns NULL for a string slot the
current flow has not loaded (non-DEBUG builds; DEBUG substitutes a
placeholder). The N64 retail menu flow guarantees every front-end string is
resident before it is drawn; the PC flow does not yet, so a missing
briefing/objective/mode-select string reached textRender/textMeasure/
textWrap as NULL and faulted in the `*text` scan. Added #ifdef PORT NULL
guards at all four entry points -> degrade to "draw nothing" (blank text)
instead of crashing. User verified: front-end now navigable folder -> mode
-> difficulty -> level -> in-game.

D144 (port/fast3d/gfx_pc.cpp gfx_sp_matrix): a front-end 3D model
(MISSION COMPLETE dossier / mode-select wallets - D75 family) emits a
gSPMatrix whose pointer field was never resolved; w1 arrives as
0xFFFFFFFFFFFFFFFF, seg_addr() passes it through, and gfx_sp_matrix faults
reading addr[]. Guard: an unmapped/non-canonical addr -> substitute identity
(model draws with wrong transform, already a parked D75 cosmetic) so the
screen and its "Next" -> menu transition survive.

Verified: -level_09 unregressed (framediff 2/2 ok w/ HUD mask, no crash).
2026-08-30 14:47:37 -04:00
James Dansereau bb14baf3bf @
D131: restore truncated module ptr in seg_addr (Jungle -level_37)

explosionRenderPropSmoke passes a compiled .bss matrix symbol
(&dword_CODE_bss_8007A100) through osVirtualToPhysical() -- a u32-returning
shim -- into gSPMatrix, truncating the 0x1_00000000 module high word. w1
lands as 0x40xxxxxx and seg_addr() returns it raw -> wild deref in
gfx_sp_matrix when the first explosion draws (~frame 300).

Fix: seg_addr() restores the module high word for any fallthrough w1 in
[0x40000000, 0x70000000) (module fixed-based at 0x140000000, no ASLR;
DRAM/KSEG0/segmented/phys are all handled in earlier branches). Covers the
~30 latent osVirtualToPhysical(<compiled matrix/vtx symbol>) sites in
explosion/glass/blood/bondview2.

Verified: Jungle renders to frame 1500-2400+ crash-free at 91.7% content;
-level_20/-level_24 unregressed. Sweep 18->19/21 (note: -level_09 has a
separate pre-existing boot crash regressed since M-18 -- see HANDOFF).
@
2026-08-29 22:13:08 -04:00
James Dansereau 93b31c8c1d D116 part 3: buf_vbo + texture-upload runtime-verified non-mirrored; contradiction documented, deprioritised (cosmetic). GE_D116/vbo probe kept 2026-08-29 11:03:13 -04:00
James Dansereau 8fdf473dc8 D116: probe finds HUD mirror is per-quad texture-U flip, not proportional-font-specific; ammo digits also mirrored; re-opens D114 2026-08-29 10:31:07 -04:00
James Dansereau 1fe85339f2 D107: fast3d sampled unloaded mip tile for GE's LOD textures (blurry ceilings)
GE's room GDLs emit G_TL_LOD + G_TD_DETAIL for mip-mapped textures, but
gfx_detail_textures_enabled is false for this port. gfx_lod_tile_offset's
'rdp.tex_lod ? rdp.tex_detail : i' then returned tex_detail (1) for every
texel, so fast3d sampled GE's first mip (render tile 1). GE loads the
whole mip chain with one G_LOADBLOCK to TMEM 0 and just points tiles 1..5
at offsets inside it; fast3d keys loaded_texture[] by TMEM addr and has
no entry for tile 1's offset, so import_texture fabricated a 16x16 from
the top-left 256 bytes of the base image and magnified it across the
polygon -- the blurry brown/grey ceilings and wall panels in BUNKER1.

Fix: when detail textures are disabled, gfx_lod_tile_offset always
returns 0 (the base render tile, the only correctly-loaded level). Sharp
near surfaces, matching what the N64 shows at close range.

Verified -level_09: BUNKER1 ventilation room ceilings/walls/light panels
now render crisp instead of brown mush. Single-level textures (racks,
room 1) were already fine and are unaffected.
2026-08-28 22:02:25 -04:00
James Dansereau c09bf37c66 D63: gate dram-branch G_DL trace behind GE_D63 (was flooding -level_09) 2026-08-28 09:04:46 -04:00
James Dansereau 978306d14f D73+D74: intro logo fixed (sinf/cosf endianness + texture import/sub-tile wrap)
D73 (root cause of old D72.3): GE's sinf/cosf build their double
constants through the du bit-union with big-endian word pairs {hi,lo};
on LE PC the .d read has the words swapped (rpi.d ~ 2^733), range
reduction overflows, and FTOFIX32 saturates to -32768 for every
sin/cos entry of guMtxF2L — so the Rareware logo's rotation matrix was
garbage and all its triangles projected off-screen. Fix: DVAL() macro
in src/libultra/gu/guint.h (PORT-only; #else branch is the original
expression) re-packing hi/lo into the correct LE double; sinf.c/cosf.c
.d reads replaced. N64 build untouched.

D74 (texture side, three port-layer bugs in fast3d):
- import_texture's fallback condition (rdp.tex_lod && tile >=
  first+detail) overwrote valid gDPLoadBlock tmem data with
  line*tile.height: letter mip chains 2744B -> 2048B (mips 1-5 dropped),
  D_02005FF0 32x32 -> 32x3 (stale tile.height from gsDPSetTileSize).
  Now falls back only when the tmem slot was never written.
- TextureCacheKey gains size_bytes so a truncated first import cannot
  poison later full imports of the same address.
- VBO UV path now wraps by TILE size for WRAP sub-tiles (N64 semantics);
  the logo's 20x3 tile at offset (11.5,29) was sampling row 0 instead
  of rows 29-31. Per-vertex pre-wrap; known limits: half-texel edge
  bleed, mirror sub-tiles unhandled.

Verified: clean runs reach frame ~2100 then hit the D69 load_bg_file
fault (unchanged — still the milestone blocker); PPM frame ~555 shows
the four gold RARE letters on the dark-blue plate (final pixel-perfect
check vs N64 footage left as a low-priority task). All env-gated probes
from this session (GE_DBGUV/DBGTRI/DBGTALL/DBGMAT/D74IMP/D74DUMP/
GE_DBGLOAD) stripped; previously committed TEMP D63 blocks + GE_D71LOG
remain on the HANDOFF strip list.

Docs: PCPortResearch.md §F D73/D74 entries, §G/§H status updated;
HANDOFF.md rewritten for Session I (D69 primary); AGENTS.md phase line.
2026-08-24 22:32:27 -04:00
James Dansereau 8ed411d3f1 D70-D72: PPM capture tool, C-array texture byte-order fix, tc[] UV restore
- D70: GE_PCDUMP env-gated PPM frame dump (video.c + gfx_opengl.cpp); ppm/ gitignored
- D71: per-source bswap of C-array texture sources in import_texture (the four
  rarewarelogo.c RGBA16 images are LE u32 arrays on PC -> pink/green logo)
- D72: removed PD-inherited normal/lookat UV override; lookat_enabled defaults
  false (GE always uses authored tc[] UVs; N64 boots with RSP mem zeroed)
- D72.3 OPEN: logo triangles still project off-screen; a flat dark-blue
  checkerboard triangle is rasterized instead - identity pending
  (GE_DBGUV/GE_DBGTALL env-gated traces added)
- Docs: PCPortResearch.md F/D70-D72, HANDOFF refreshed, D71 log gated
2026-08-24 19:12:40 -04:00
James Dansereau 8f631462f3 PC port: phase 2 — D59-D68 milestone: full intro renders; blocker is D69 (BG-file BE headers)
D59/D64: the 'external GL DLL' crash was an unbounded blood-RLE write
(sentinel die_blood_image_end lost its N64 section adjacency on PC)
clobbering the gun-barrel sub-DL at ptr_logo_and_walletbond_DL+0x200.
D60: osPiStartDma DMA-target validation (VirtualQuery-based) in libultra.c.
D61/D63: TEMP env-gated ROM-read / VTX-pool / menu probes (strip list in
HANDOFF Task 3). D62: OSMesgQueue/OSScMsg PC layout verification.
D65/D65b: enum HEADS/BODIES signed sentinels under PORT (PC GCC picks
unsigned underlying types; N64 semantics were signed).
D66: romCopyAligned + ramrom replay 64-bit pointer width (s32 truncation
of .bss targets > 4 GiB). D67: struct image_entry N64 layout
reconstruction (dataoffset:24 first, all-u32 bitfields, sizeof==8).
D68: Globalimagetable BE->LE fixup (port/src/gimgfixup.c): bswap the
IMAGESEG-marked Gfx w1 words in the 17 ROM-copied DLs + sImageTableEntry.
index in the 32 tables after texReset's romCopy; marker scan moved to
bytes 6..7 under PORT; compiled globalDL shadows synced post-texLoad for
explosion.c's g_ExplosionDisplayLists.

Result: game boots, plays intro music, and renders the entire ~3.5-minute
intro (logo -> gun barrel with Brosnan -> cast screen) at ~59 fps. New
blocker D69: load_bg_file faults on first stage load - BG-file headers are
N64 big-endian segment-0x0F offsets read LE on PC. Full analysis + strategy
in PCPortResearch.md §F/D69; HANDOFF rewritten for the D69 session.
2026-08-24 03:27:29 -04:00
James Dansereau 181f6db07d PC port: phase 2 — D43 mechanical ABI edits + D46 buffer sizing + fast3d seg-5 case; converter spec validated (D47)
- bondtypes.h/model.c/chr.c/objecthandler_2.c/tex.c: PORT-guarded pointer-width
  and Gfx-size fixes (D43 step 2, uncommitted since prior session)
- front.c cast-screen bufferRemaining 0x1C000->0x25000, initmenus.c logo
  buffer 0x7C000->0x85000 (D46 strict bound)
- gfx_pc.cpp seg_addr: resolve raw 0x05xxxxxx via segmentPointers[5] + low24
  (unmarked segmented addresses, ROM convention)
- docs: D47 finding — converter contract finalized (compaction delta=R-P,
  accessors at 0x80090000, G_VTX GBI1 encoding, opcode-aware w1 remap rule,
  vertex formats, no embedded blobs, zero-vtx-array rule, staging contract,
  reload-hazard poolRemaining reset); HANDOFF rewritten around the remaining
  C implementation
- build-pc/d43_convert.py (gitignored, local): reference converter + validator —
  full N64->PC conversion spec run on all 512 ROM files, ALL CLEAN (max D_PC=0xB7E0)
2026-08-22 18:24:31 -04:00
James Dansereau 13ca03a6db PC port: Phase 2 — real threads, dual-mapped DRAM, fast3d integration
- Replace setjmp/longjmp green threads with real pthreads (8MB stacks);
  PortQueue side table (mutex+condvar) for OSMesgQueue; dedicated tick
  thread posts VI retrace + services timers (D24).
- Dual-mapped N64 DRAM: one 8MB store at V1 0x70000000 (s32-safe, all game
  RAM symbols) and V2 0x80000000 (KSEG0 mirror for offset|0x80000000
  rebuilds). Windows: CreateFileMappingW + MapViewOfFileEx; POSIX: memfd
  + MAP_FIXED. dram_syms.s pins cfb_16/_bssSegmentEnd/tlb block (D25).
- Address-width shims: PHYS_TO_K0 identity, OS_K0_TO_PHYSICAL = offset from
  V1 base, OS_PHYSICAL_TO_K0 identity; fast3d seg_addr() remaps small
  offsets into V2 (D26).
- ASLR off (-Wl,--disable-dynamicbase): absolute symbols referenced through
  pointer-typed externs (.refptr + BASE reloc) must not rebase; startup
  sanity check on image base 0x140000000 (D27).
- osPiStartDma posts its completion message on every path (fixes
  romReceiveMesg deadlock) (D29).
- SEH-free crash handler: raw fault log first, validated manual EBP-chain
  walk second (-fno-omit-frame-pointer), TerminateProcess; crashDumpThreads
  via Toolhelp32 for heartbeat hang dumps (D30).
- PD fast3d integrated (port/fast3d/), replaces gfxstub.c; real src/sched.c
  compiled; mainproc runs as a real pthread, host thread pumps SDL events.
- Docs: findings D24-D31 + Phase 2 status (section F/G).

Current state: boots through bossInitMainthreadData to langInit(), then
SIGSEGVs on the first file load — stack frame clobbered by a table of
truncated s32 pointers (D31, open). Next: attribute the write with
'set scheduler-locking on' + hardware watchpoint.
2026-08-21 14:42:19 -04:00