#!/usr/bin/env python3 """Every `opt ` name in every RATC bundle on the disc, by extension -- and the `.tan` frame sequences among them. Written to close the reach caveat in docs/re/structures/ratc-child-names.md: 60 of 18 002 RATC children carry no `opt ` block of their own. They are not children. They are the ten frames of the disc's only `.tan` resource, and one `opt ` block names the whole run. python3 tools/re-capture/ratc_opt_name_census.py Reads $SYLPHEED_DISC/dat/*.pak directly (IPFB TOC + Z1/zlib entries), so it does not depend on the Rust parser it is checking. Takes a few minutes. """ import struct, zlib, glob, os, collections, bisect from disc import disc_root MAG = (b"T8aD", b"RATC", b"ttcf", b"\x89PNG") ext = collections.Counter() tan_sites = [] opt_total = 0 DISC = disc_root() for pakpath in sorted(glob.glob(f"{DISC}/dat/*.pak")): base = pakpath[:-4] pak = open(pakpath, "rb").read() if pak[:4] != b"IPFB": continue n = struct.unpack_from(">I", pak, 4)[0] toc = [struct.unpack_from(">III", pak, 0x10 + 12*i) for i in range(n)] segs = sorted(glob.glob(base + ".p[0-9][0-9]")) if not segs: continue data = b"".join(open(s, "rb").read() for s in segs) for ei, (h, off, cs) in enumerate(toc): raw = data[off:off+cs] try: b = zlib.decompress(raw[10:]) if raw[:2] == b"Z1" else raw except Exception: continue if b[:4] != b"RATC": continue names = [] p = b.find(b"opt ") while p >= 0: ln = struct.unpack_from(">I", b, p+4)[0] if p+8 <= len(b) else 0 if 0 < ln <= 64 and p+8+ln <= len(b): nm = b[p+8:p+8+ln].decode('latin1', 'replace') if nm and all(32 < ord(c) < 127 for c in nm): names.append((p, nm)); opt_total += 1 ext[os.path.splitext(nm)[1].lower()] += 1 p = b.find(b"opt ", p+4) offs, i = [], 4 while i + 4 <= len(b): if b[i:i+4] in MAG: offs.append(i); i += 4 else: i += 1 if not names or not offs: continue npos = [p for p, _ in names] # each child -> index of the nearest preceding opt owner = collections.defaultdict(list) for k, o in enumerate(offs): j = bisect.bisect_left(npos, o) - 1 if j >= 0: owner[j].append(k) for j, (p, nm) in enumerate(names): if not nm.lower().endswith(".tan"): continue ks = owner.get(j, []) if not ks: continue sizes = sorted({(offs[k+1] if k+1 < len(offs) else len(b)) - offs[k] for k in ks}) tan_sites.append((os.path.basename(pakpath), ei, nm, len(ks), sizes)) print(f"`opt ` blocks disc-wide: {opt_total}") print("\nby extension:") for e, c in ext.most_common(25): print(f" {e or '(none)':10} x{c}") print(f"\n.tan resources with >=1 child: {len(tan_sites)}") seen = collections.Counter() for t in tan_sites: seen[(t[0], t[2], t[3], tuple(t[4]))] += 1 for (pk, nm, fr, sz), c in sorted(seen.items()): print(f" {pk:24} {nm:30} frames={fr:3} sizes={list(sz)} x{c} bundles")