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>
70 lines
2.9 KiB
Python
Executable File
70 lines
2.9 KiB
Python
Executable File
"""What would each rest-pose rule do to the 2 305 genuinely-ambiguous elements?
|
|
|
|
Only two of them sit on a screen with a live capture, so the rule cannot be
|
|
scored disc-wide. What CAN be quantified is the blast radius: how often the
|
|
rules disagree, and how often each returns a pose that draws nothing.
|
|
"""
|
|
import struct, zlib, glob, collections
|
|
from disc import disc_root
|
|
DECL_AT, DECL_ENTRY, KEYFRAME = 0x20, 60, 40
|
|
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
|
|
def elements(d):
|
|
if d[:4]!=b"RATC": return []
|
|
count=struct.unpack_from(">I",d,0x14)[0]
|
|
if not (0<count<4096): return []
|
|
out,pos=[],DECL_AT+count*DECL_ENTRY
|
|
while True:
|
|
if pos+8>len(d): break
|
|
idx,frames=struct.unpack_from(">II",d,pos)
|
|
if idx>=count or frames==0 or frames>4096: break
|
|
first=pos+12; end=first+frames*KEYFRAME-4
|
|
poses,times=[],[]
|
|
for k in range(frames):
|
|
b=first+k*KEYFRAME
|
|
if b+36>len(d) or b+36>end: break
|
|
poses.append(struct.unpack_from(">I",d,b)+struct.unpack_from(">II",d,b+16)+struct.unpack_from(">ii",d,b+28))
|
|
times.append(struct.unpack_from(">i",d,b+36)[0] if b+40<=end else None)
|
|
if poses: out.append((times,poses))
|
|
pos=end
|
|
return out
|
|
def dwell(t,p):
|
|
best=(0,-1)
|
|
for k in range(len(t)-1):
|
|
if t[k] is None or t[k+1] is None: continue
|
|
d=t[k+1]-t[k]
|
|
if d>=best[1]: best=(k,d)
|
|
return best[0]
|
|
alpha=lambda q:(q[0]>>24)&0xff
|
|
n=agree=0
|
|
inv=collections.Counter(); deg=collections.Counter()
|
|
for pak in sorted(glob.glob(disc_root() + "/dat/GP_*.pak")):
|
|
for d in entries(pak[:-4]):
|
|
for t,p in elements(d):
|
|
if len(p)==1: continue
|
|
if any(p[i]==p[i+1] for i in range(len(p)-1)): continue
|
|
n+=1
|
|
rd=p[dwell(t,p)]; rl=p[-1]
|
|
agree += rd==rl
|
|
for tag,r in (("dwell",rd),("last",rl)):
|
|
if alpha(r)==0: inv[tag]+=1
|
|
if r[1]==0 or r[2]==0: deg[tag]+=1
|
|
print(f"genuinely ambiguous elements (plateau-less, 2+ keyframes): {n}")
|
|
print(f" the two rules AGREE on : {agree} ({100*agree/n:.1f} %)")
|
|
print(f" they DIFFER on : {n-agree} ({100*(n-agree)/n:.1f} %)")
|
|
print()
|
|
for tag in ("dwell","last"):
|
|
print(f" rule {tag:6}: returns an INVISIBLE pose {inv[tag]:5d} ({100*inv[tag]/n:5.1f} %),"
|
|
f" zero-scale {deg[tag]:4d} ({100*deg[tag]/n:4.1f} %)")
|