u32 count at 0x14, then fixed 60-byte entries of [name | 4 flag words | pivotX | pivotY | 0]. The table lists both .t32 sprites and .rat records, so it is the screen's element list. Verified: for all 7 .t32 entries in the tutorial pause bundle the declared pivot is exactly half the decoded texture's dimensions, 7/7 with no mismatch. That also settles what the pair at 0x50/0x54 of a .rat record is — a .rat simply opens with one of these declarations. The language split is visible inside one bundle: the English build's .t32 declarations carry correct English pivots while its .rat declarations carry Japanese-derived ones, so the sprite table is regenerated per language and the layout layer is inherited from the Japanese master. Adds tools/re-capture/ratc_decls.py (parse the table, check pivots against the decoded textures). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
42 lines
1.6 KiB
Python
42 lines
1.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Parse a RATC bundle's header SPRITE DECLARATION TABLE: u32 count at 0x14, then
|
|
fixed 60-byte entries of [name, NUL-padded | 4 u32 flags | pivotX | pivotY | 0].
|
|
Check each pivot against half the decoded texture's real dimensions."""
|
|
import struct, sys, glob, re, os
|
|
|
|
def decls(d):
|
|
n = struct.unpack_from(">I", d, 0x14)[0]
|
|
out = []
|
|
for i in range(n):
|
|
o = 0x20 + i * 60
|
|
if o + 60 > len(d): break
|
|
name = d[o:o+28].split(b"\0")[0].decode("ascii", "replace")
|
|
px, py = struct.unpack_from(">II", d, o + 48)
|
|
out.append((name, px, py))
|
|
return n, out
|
|
|
|
def texmap(prefix):
|
|
t = {}
|
|
for f in glob.glob(f"pause-tex/{prefix}_*.png"):
|
|
m = re.match(rf".*/{prefix}_(.+)\.t32_(\d+)x(\d+)\.png", f)
|
|
if m: t[m.group(1) + ".t32"] = (int(m.group(2)), int(m.group(3)))
|
|
return t
|
|
|
|
for path, prefix in [(sys.argv[1], sys.argv[2])]:
|
|
d = open(path, "rb").read()
|
|
n, ds = decls(d)
|
|
tm = texmap(prefix)
|
|
print(f"{os.path.basename(path)}: count={n}, parsed={len(ds)}")
|
|
ok = bad = miss = 0
|
|
for name, px, py in ds:
|
|
if name in tm:
|
|
w, h = tm[name]
|
|
hit = (px == w // 2 and py == h // 2)
|
|
ok, bad = ok + hit, bad + (not hit)
|
|
flag = "OK " if hit else "MISMATCH"
|
|
print(f" {flag} {name:28s} tex {w:4d}x{h:<4d} half {w//2:4d},{h//2:<4d} decl {px:4d},{py:<4d}")
|
|
else:
|
|
miss += 1
|
|
print(f" ? {name:28s} (no decoded texture) decl {px:4d},{py:<4d}")
|
|
print(f" => pivot == half(texture): {ok} ok, {bad} mismatch, {miss} unchecked")
|