ec82764bef
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression (272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps, and all dump-derived textures/traces) is excluded via .gitignore and stays local. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
110 lines
6.6 KiB
Python
110 lines
6.6 KiB
Python
#!/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)
|