Files
Sylpheed/tools/re-capture/eff_bit_census.py
sim ac0ad579fd chore: retire the last dead paths and names from the consolidation
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>
2026-09-16 22:30:28 +02:00

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)