#!/usr/bin/env python3 """retroDE_ps2 — Ch347 authentic asset extractor: real Silent Hill 3 PSMT8 texture + real CLUT. Codex target A: "authentic SH3 palettized texture + real palette rendered through chosen sprite geometry" — NOT a faithful SH3 draw. We pick a CLEAN candidate whose texture is a NATIVE PSMT8 upload (linear payload, no GS-memory model needed) and whose CLUT is a clean 256-entry PSMCT32 upload, both validated by rendering to a coherent SH3 surface (see captures/gs/silenthill3/extracted/*.png). Default candidate (dump 224139): PSMT8 tex tbp=13824 (128x128, native upload, 99 distinct indices) + CLUT cbp=14282 (16x16=256 PSMCT32, read LINEARLY — csm=0 but linear order renders coherent; CSM1 bit-swap scrambles). Primary render = DECAL (opaque): the authentic CLUT RGB is kept BYTE-FOR-BYTE; alpha (SH3's real ~0x04) is IGNORED by the render mode, never rewritten (Codex guardrail). Emits the index texture, the CLUT, and a software DECAL reference FB for the pre-fit pixel-diff. Outputs (LOCAL, dump-derived -> gitignored) into sim/data/top_psmct32_raster_demo/: sh3_tex_idx.mem 128x128 PSMT8 indices, 4 per 32-bit word (low->high byte), $readmemh sh3_clut.mem 256 PSMCT32 ABGR CLUT entries (one per line) — authentic RGB, authentic alpha sh3_ref.mem software DECAL reference FB 128x128 PSMCT32 (CLUT[idx] RGB, opaque) for pixel-diff sh3_authentic.png visual confirmation (DECAL) """ import sys, os, glob, struct HERE=os.path.dirname(os.path.abspath(__file__)); ROOT=os.path.normpath(os.path.join(HERE,"..")) sys.path.insert(0, os.path.join(ROOT,"tools")); import gs_texture_residency as R DATA=os.path.join(ROOT,"sim","data","top_psmct32_raster_demo") EXTR=os.path.join(ROOT,"captures","gs","silenthill3","extracted") def main(argv): dump = argv[1] if len(argv)>1 else None if not dump: cands = glob.glob(os.path.join(ROOT,"captures","gs","silenthill3","*224139*.gs.zst")) if not cands: sys.exit("no SH3 dump found; pass the .gs.zst path") dump = cands[0] TBP = int(argv[2]) if len(argv)>2 else 13824 # native PSMT8 texture base CBP = int(argv[3]) if len(argv)>3 else 14282 # CLUT base W = H = 128 d,h,events,uploads,runs,vram = R.collect(dump,0) # texture: latest NATIVE PSMT8 upload to TBP (linear row-major payload) tu = [u for u in uploads if u["dbp"]==TBP and u["dpsm"]==0x13] if not tu: sys.exit(f"no native PSMT8 (dpsm=0x13) upload to tbp={TBP} — not a clean candidate") tup = max(tu, key=lambda u:u["idx"]); tex = d[tup["blob_range"][0]:tup["blob_range"][1]] if len(tex) < W*H: sys.exit(f"texture payload {len(tex)}B < {W*H}") idx = [tex[y*W+x] for y in range(H) for x in range(W)] # CLUT: latest PSMCT32 256-entry upload to CBP, read LINEARLY cu = [u for u in uploads if u["dbp"]==CBP and u["dpsm"]==0x00] if not cu: sys.exit(f"no PSMCT32 CLUT upload to cbp={CBP}") cup = max(cu, key=lambda u:u["idx"]); cb = d[cup["blob_range"][0]:cup["blob_range"][1]] pal = [int.from_bytes(cb[i*4:i*4+4],"little") for i in range(256)] # software DECAL reference = EXACTLY what the hardware DECAL emit stores: the texel = CLUT[idx], byte-for- # byte authentic (RGB AND the real ~0x04 alpha — nothing rewritten). The pre-fit TB pixel-diffs RGB (the # authentic-art claim); the A byte is preserved here but not the focus. The PNG forces opaque for display. ref = [pal[i] for i in idx] os.makedirs(DATA, exist_ok=True); os.makedirs(EXTR, exist_ok=True) # PSMT8 indices packed 4/word (byte0=lowest x), $readmemh as 32-bit with open(os.path.join(DATA,"sh3_tex_idx.mem"),"w") as f: f.write(f"// Ch347 LOCAL authentic SH3 PSMT8 indices {W}x{H} (native upload tbp={TBP}). gitignored.\n") for w in range(0, W*H, 4): word = idx[w] | (idx[w+1]<<8) | (idx[w+2]<<16) | (idx[w+3]<<24) f.write(f"{word:08x}\n") with open(os.path.join(DATA,"sh3_clut.mem"),"w") as f: f.write(f"// Ch347 LOCAL authentic SH3 CLUT 256xPSMCT32 (cbp={CBP}, linear). RGB+alpha authentic. gitignored.\n") for p in pal: f.write(f"{p & 0xFFFFFFFF:08x}\n") with open(os.path.join(DATA,"sh3_ref.mem"),"w") as f: f.write(f"// Ch347 LOCAL SW DECAL reference FB {W}x{H} (CLUT[idx] RGB, opaque display). gitignored.\n") for p in ref: f.write(f"{p & 0xFFFFFFFF:08x}\n") try: from PIL import Image img=Image.new('RGBA',(W,H)); img.putdata([(p&0xFF,(p>>8)&0xFF,(p>>16)&0xFF,0xFF) for p in ref]) img.save(os.path.join(EXTR,"sh3_authentic.png")) except Exception as e: print("(PIL unavailable, skipped PNG:", e, ")") # --- Ch347 (ii): a DETERMINISTIC 64x64 authentic CROP (Codex first-silicon target). --- # The crop is a DIRECT SUBSET of the extracted indices (no resample/transform); the CLUT is unchanged. # Deterministic rule: the 64x64 window (stride 8) with the MOST distinct indices, tie-break smallest (cy,cx) # — guarantees real content + a reproducible, reported origin. CW = CH = 64 best = (-1, 0, 0) for cy in range(0, H-CH+1, 8): for cx in range(0, W-CW+1, 8): s = set(idx[(cy+ly)*W + (cx+lx)] for ly in range(CH) for lx in range(CW)) if len(s) > best[0]: best = (len(s), cx, cy) _, CX, CY = best cidx = [idx[(CY+ly)*W + (CX+lx)] for ly in range(CH) for lx in range(CW)] cref = [pal[i] for i in cidx] with open(os.path.join(DATA,"sh3_tex_idx64.mem"),"w") as f: f.write(f"// Ch347 LOCAL authentic SH3 PSMT8 64x64 CROP @({CX},{CY}) of tbp={TBP} 128x128 (direct subset). gitignored.\n") for w in range(0, CW*CH, 4): f.write(f"{cidx[w] | (cidx[w+1]<<8) | (cidx[w+2]<<16) | (cidx[w+3]<<24):08x}\n") with open(os.path.join(DATA,"sh3_ref64.mem"),"w") as f: f.write(f"// Ch347 LOCAL SW DECAL reference 64x64 CROP @({CX},{CY}) (CLUT[idx] RGB). gitignored.\n") for p in cref: f.write(f"{p & 0xFFFFFFFF:08x}\n") try: from PIL import Image ci=Image.new('RGBA',(CW,CH)); ci.putdata([(p&0xFF,(p>>8)&0xFF,(p>>16)&0xFF,0xFF) for p in cref]) ci.save(os.path.join(EXTR,"sh3_authentic64.png")) except Exception: pass print(f"[Ch347] SH3 authentic asset: tex tbp={TBP} {W}x{H} ({len(set(idx))} distinct indices), CLUT cbp={CBP} " f"({len(set(pal))} colors). DECAL reference emitted.") print(f"[Ch347] 64x64 CROP @({CX},{CY}) [deterministic max-distinct window]: {len(set(cidx))} distinct indices, " f"{len(set(cref))} colors -> sh3_tex_idx64.mem, sh3_ref64.mem") print(f"[Ch347] -> {DATA}/sh3_tex_idx*.mem, sh3_clut.mem, sh3_ref*.mem (+ {EXTR}/sh3_authentic*.png) — all LOCAL") if __name__ == "__main__": main(sys.argv)