Nothing here changes what a tool computes; it changes where tools look. - tools/re-capture: 33 censuses globbed /work/sylph_extract, a path that has existed nowhere since /work became a clone, so they matched nothing and printed empty results. They now resolve the disc through a new disc.py from $SYLPHEED_DISC and exit loudly without it (the #44 fix, generalised). Nine scripts that imported siblings from the retired Reborn checkout or an old session scratchpad now import from their own directory. unitgroup.py only needs the variable when --pak is not given. - sylpheed-xex: the loader only ever uses the XEX2 retail key. The dead devkit key and a doc comment claiming a devkit fallback that does not exist are gone; Project Sylpheed is a retail XEX2, so no XEX1 key either. - sylpheed-viewer: real_font_rasterizes looked for /tmp/sylph_extract and so always skipped. It reads $SYLPHEED_DISC now, and passes against the disc. - Comments and docs that named xenia-rs, the Reborn repository or /work/*.pe as places to look now name sylpheed.db, Canary's ppc_context.h and the flat .pe; docs/re/README.md no longer says the native Canary build does not run. Historical records keep their original paths: findings that were measured against /work/xenia-rs/sylpheed.db still say so. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
62 lines
2.7 KiB
Python
Executable File
62 lines
2.7 KiB
Python
Executable File
"""Disc-wide: does T8aD flag bit 0x02 imply an `eff` name?
|
|
|
|
Names come from the NAME IMMEDIATELY PRECEDING each T8aD chunk -- validated
|
|
17/18 on GP_TITLE build 4 against the known RATC child order. The single
|
|
mismatch is the documented `pteff04.t32` -> registered as `8AX` case, i.e. the
|
|
preceding name is the ELEMENT's (opt) name and the child list is the SPRITE's.
|
|
This test uses the element name and says so.
|
|
"""
|
|
import struct, zlib, glob, os, re, collections
|
|
from disc import disc_root
|
|
NAME = re.compile(rb'[A-Za-z0-9_.]{2,31}\x00')
|
|
|
|
def entries(base):
|
|
stub = open(base + ".pak", "rb").read()
|
|
if stub[:4] != b"IPFB": return
|
|
n = struct.unpack_from(">I", stub, 4)[0]
|
|
segs = sorted(glob.glob(base + ".p[0-9][0-9]"))
|
|
if not segs: return
|
|
blob = b"".join(open(s, "rb").read() for s in segs)
|
|
for i in range(n):
|
|
h, off, sz = struct.unpack_from(">III", stub, 0x10 + 12 * i)
|
|
st = blob[off:off + sz]
|
|
if len(st) < 10: continue
|
|
try: yield (zlib.decompress(st[10:]) if st[:2] == b"Z1" else st)
|
|
except Exception: continue
|
|
|
|
set_eff = set_noneff = clear_eff = clear_noneff = 0
|
|
examples = []
|
|
for pak in sorted(glob.glob(disc_root() + "/dat/GP_*.pak")):
|
|
for d in entries(pak[:-4]):
|
|
for m in re.finditer(b"T8aD", d):
|
|
o = m.start()
|
|
try:
|
|
fl = struct.unpack_from(">I", d, o + 4)[0]
|
|
w = struct.unpack_from(">I", d, o + 0x14)[0]
|
|
h = struct.unpack_from(">I", d, o + 0x18)[0]
|
|
except Exception: continue
|
|
if not (0 < w <= 4096 and 0 < h <= 4096): continue
|
|
ms = list(NAME.finditer(d[max(0, o - 64):o]))
|
|
if not ms: continue
|
|
nm = ms[-1].group()[:-1].decode("latin1")
|
|
eff = "eff" in nm.lower()
|
|
if fl & 2:
|
|
if eff: set_eff += 1
|
|
else:
|
|
set_noneff += 1
|
|
if len(examples) < 12: examples.append((os.path.basename(pak), nm, f"{fl:08x}", f"{w}x{h}"))
|
|
else:
|
|
clear_eff += 1 if eff else 0
|
|
clear_noneff += 0 if eff else 1
|
|
tot = set_eff + set_noneff + clear_eff + clear_noneff
|
|
print(f"sprites with a resolvable preceding name: {tot}")
|
|
print(f" bit SET & name has 'eff' : {set_eff}")
|
|
print(f" bit SET & name lacks 'eff': {set_noneff} <-- counterexamples to 'set => eff'")
|
|
print(f" bit clear & name has 'eff' : {clear_eff}")
|
|
print(f" bit clear & name lacks 'eff': {clear_noneff}")
|
|
if tot: print(f"\n P(name has 'eff' | bit set) = {set_eff/max(set_eff+set_noneff,1):.3f}")
|
|
if tot: print(f" P(name has 'eff' | bit clear) = {clear_eff/max(clear_eff+clear_noneff,1):.3f}")
|
|
if examples:
|
|
print("\n counterexamples (bit set, no 'eff'):")
|
|
for e in examples: print(" ", e)
|