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>
45 lines
2.3 KiB
Python
45 lines
2.3 KiB
Python
import os, sys, glob, collections
|
|
from disc import disc_root
|
|
_SD = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, _SD)
|
|
exec(open(os.path.join(_SD, "regn_decode.py")).read().split("# ── the object")[0])
|
|
import unitgroup as U
|
|
print("# `Generic` is the per-FILE header record, not a table")
|
|
print("#")
|
|
print("# One `Generic` per IDXD pak entry; its schema is set by the file's TYPE.")
|
|
print("# Regenerate: see docs/re/structures/unit-datasheet-static.md")
|
|
for pk in sorted(glob.glob(disc_root() + "/**/GP_MAIN_GAME_*.pak",recursive=True)):
|
|
E=pak_entries(pk); items=E.items() if isinstance(E,dict) else E
|
|
kinds=collections.Counter(); ids=set(); types=collections.Counter()
|
|
nidxd=0; man=eff=0; ctl_ok=ctl_bad=0; one_per=True
|
|
for h,b in items:
|
|
if bytes(b[:4])!=b'IDXD': continue
|
|
try: recs=U.parse(bytes(b))
|
|
except Exception: continue
|
|
nidxd+=1
|
|
names=[r['squadron'] for r in recs]
|
|
if names.count('Generic')>1: one_per=False
|
|
man+= 'Maneuver' in names; eff+= 'Effect' in names
|
|
for r in recs:
|
|
if r['squadron']!='Generic': continue
|
|
n=U.named(r)
|
|
if 'HP' in n:
|
|
kinds['unit datasheet']+=1; types[n.get('Type','<none>')]+=1
|
|
elif set(n)=={'Count'}:
|
|
kinds['message-file header']+=1
|
|
nmsg=sum(1 for x in names if x.startswith('Message_'))
|
|
ctl_ok+= str(nmsg)==n['Count'].strip(); ctl_bad+= str(nmsg)!=n['Count'].strip()
|
|
elif 'SideID' in n: kinds['character']+=1
|
|
elif 'EnumAsteroidGroup' in n: kinds['asteroid group']+=1
|
|
else: kinds['other (%d fields)'%len(n)]+=1
|
|
if 'ID' in n: ids.add(n['ID'])
|
|
print()
|
|
print("== %s (%d IDXD entries)" % (pk.split('/')[-1], nidxd))
|
|
for k,v in kinds.most_common(): print(" %-22s %4d" % (k,v))
|
|
print(" %-22s %4d" % ("TOTAL Generic", sum(kinds.values())))
|
|
print(" distinct Generic.ID %4d (= unit %d + character %d)"
|
|
% (len(ids), kinds['unit datasheet'], kinds['character']))
|
|
print(" unit Generic.Type %s" % dict(types))
|
|
print(" entries w/ Maneuver %d w/ Effect %d" % (man,eff))
|
|
print(" CONTROL message-header Count == #Message_NNN siblings: %d ok / %d bad" % (ctl_ok,ctl_bad))
|