"""M3 texel store: parse the game wire's action-0x1a texture uploads into a handle -> (u_size, v_size, mode, RGB array) dict. Format: header[32] = {handle, byte_size, u, v, mode, ...}, then v rows of 256 bytes, then n=u*v texels. TEXEL WORD = [pad, B, G, R] bytes -- "SVT xbgr" (ground truth: dpl3-revive patha/vrboard.py:448 `do_texels` + stage_assets.py:43, the actual renderer's own decode). Byte 0 is a PAD (verified: 0 in every texel across the capture); the colour is bytes 1,2,3 = B,G,R, so RGB = bytes (3,2,1). There is NO alpha byte -- cutout ("dpl_tex_value_cut") is done by NEAR-BLACK keying at draw time, not a stored channel (patha/vrview_gl.py). `mode` 1 is the board's internal 8-bit storage path; the WIRE upload is always 4 bytes/texel regardless of mode. Reading RGB as bytes (0,1,2) = (pad,B,G) -- zero red -- was the all-CYAN bug.""" import struct import numpy as np def build_texstore(recs): store = {} i = 0 while i < len(recs): a, p = recs[i] if a == 0x1a and len(p) == 32: h = [struct.unpack_from('= n * 4: arr = np.frombuffer(rb[:n * 4], dtype=np.uint8).reshape(v, u, 4) # texel = [pad,B,G,R] -> RGB = bytes (3,2,1); v-flip (rows arrive # bottom-up: 'PLAYER' reads mirrored otherwise) rgb = arr[::-1, :, 3:0:-1].copy() # (R,G,B) from (3,2,1) store[handle] = (u, v, mode, rgb) i = j else: i += 1 return store