forked from Ports/goldeneye-pc-port
Live-tested against 4 real user playtests (not scripted) tonight, each confirming purple/blue explosions still reproduce on current main: 1. G_LIGHTING hypothesis (previous session's lead) RULED OUT: extended the D172 probe to flag lit particle draws uncapped (the original 40-hit cap, sized for a short scripted repro, silently went dark after ~40 ordinary bullet-impact particles well before any real explosion -- masking the result, not confirming it). Fixed with an always-on periodic counter. Result: 5000+ particle draws logged across a real playtest with explosions triggered, 100% LIGHTING=off, 0 suspect hits. 2. Texture-cache-collision hypothesis RULED OUT: extended the same probe to log the actual bound TEXEL1 address/GL-texture-id/byte-count. 7 distinct, internally-consistent 448-byte (16x14 RGBA16) textures observed -- no aliasing, no stale-slot signature. 3. ROOT CAUSE: used the existing GE_TEXDUMP feature (extended to exempt 16x14 RGBA images from its own 400-image cap, same masking problem as above) to dump all 15 IMAGE_FIRE_0..14 particle textures as PPM during a live playtest with confirmed-purple explosions. Every single one decodes blue/purple-dominant; swapping the decoded R and B channels turns all 15 into a plausible warm fire palette, consistently. This is NOT a bug in the shared RGBA16 decoder (port/fast3d/gfx_pc.cpp import_texture_rgba16) -- that function is proven correct by every other RGBA16 texture in the game rendering fine, and the general C-array byte-order normalization already runs upstream of it. The wrongness is in the decoded bytes themselves: either the Globalimagetable entries for these 15 image ids resolve to the wrong ROM offset, or these assets use a sub-format the shared decoder doesn't special-case. Do NOT blanket-swap R/B in the shared decoder -- it would break every correctly-rendering RGBA16 texture in the game. Also fixes a mistake made mid-session: a follow-up raw-byte-dump probe (GE_TEXRAW) initially keyed its own cap-exemption on the wrong field (gsDPSetTileSize's logical 56x56 wrap size instead of the real 16x14 pixel content), so it never matched and burned a live-playtest cycle for nothing. Fixed to key on the real signature: a 448-byte load (16*14*2, RGBA16). Diagnostic infra left in tree, env-gated and inert by default: GE_D172=1 (uncapped lit-case + periodic counter in gfx_sp_tri1), GE_TEXDUMP=1 (16x14-exempt-from-cap fire dumper in gfx_opengl_upload_texture), GE_TEXRAW=1 (fixed 448-byte-exempt raw dump in the texture import path).