#!/usr/bin/env python3 """How much of each pak's TOC can be named from the asset strings on the disc. Harvests every plausible asset-name string from every archive, hashes each under the known path prefixes, and reports per archive how many of its entries are explained. Regenerates docs/re/data/archive-naming.txt. """ import sys, os, glob, re, collections HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) import unitgroup as U from unit_substructures import pak_entries EXT = r'(prt|t32|rat|tbl|xpr|ttc|ttf|TTC|TTF|bin|col|rgn|prm)' PRE = ['', '2d\\', 'ui\\', 'hud\\', 'Stage\\', 'stage\\', 'message\\', 'language\\', 'prt\\', 'view\\'] + [l + '\\' for l in ('eng', 'jpn', 'fra', 'deu', 'ita', 'esp')] def main(): paks = sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)) names = set() toc = {} for pk in paks: ents = pak_entries(pk) toc[os.path.basename(pk)] = [h for h, b in ents] for h, b in ents: for m in re.finditer((r'[A-Za-z0-9_\\.\-]{4,60}\.' + EXT).encode(), b): names.add(m.group(0).decode('latin-1')) H = set() for n in names: for p in PRE: H.add(U.name_hash(p + n)) print("# How much of each pak's TOC can be named from the disc's own strings") print("# Regenerate: python3 tools/re-capture/archive_naming.py") print("# See docs/re/structures/archive-naming.md") print("\n candidate asset-name strings harvested: %d" % len(names)) print(" path prefixes tried: %d" % len(PRE)) print("\n archive entries named pct") rows = sorted(((k, v) for k, v in toc.items()), key=lambda r: -len(r[1])) for nm, ent in rows: nd = sum(1 for h in ent if h in H) print(" %-30s %6d %6d %5.1f%%" % (nm, len(ent), nd, 100.0 * nd / len(ent) if ent else 0)) def _ix(t): return U.ixud_hash([ord(c) for c in t]) FN = [('name_hash', U.name_hash), ('tag_hash', U.tag_hash), ('ixud_hash', _ix)] SUB = ['', '2d\\', 'eng\\', 'Data\\', 'ArmsSt\\', 'Marker\\'] print("\n## CONTROL is the unnameable pair keyed by a DIFFERENT hash?") print(" (6 prefixes x 3 hash families; the menu paks are the positive control)") for nm in ['GP_MAIN_GAME_E2D.pak', 'GP_READY_ROOM.pak', 'GP_TITLE.pak', 'GP_PAUSE_MENU.pak']: S = set(toc[nm]) for fname, f in FN: best = (0, '') for p in SUB: n = sum(1 for v in names if f(p + v) in S) if n > best[0]: best = (n, p or '') print(" %-22s %-10s %4d / %-5d %s" % (nm, fname, best[0], len(S), best[1])) print("\n## CONTROL the executable holds almost no asset names") print(" sylpheed.db `strings`: 7140 rows, of which 2 look like asset paths") print(" Data\\gmicon002_2.t32 and Data\\gmicon006_2.t32") for n in ['Data\\gmicon002_2.t32', 'Data\\gmicon006_2.t32', 'gmicon002_2.t32']: print(" %-28s resolves anywhere: %s" % (n, U.name_hash(n) in H)) if __name__ == "__main__": main()