diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 3e192a3..f9f2623 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -827,6 +827,31 @@ unknown, what evidence exists, and what the first step would be. Move an item in field; the stat keys in records not opened here (`MegaTons`, `BulletLimit`, `CrewCount`, `HP_CLASS`); why the roster lists 59 of 131. +* ✅ **(2026-08-27) WHO REFERENCES A `.prt` DECIDES WHETHER IT SHIPS — menu parts + are named and shipped, in-game parts are embedded. 0 counterexamples in 376.** + [structures/mission-script-manifest](structures/mission-script-manifest.md), + artefact `data/prt-parts.txt` (+44 lines, 0 deletions — purely additive). + Censused the whole 135 unresolved set instead of chasing the five. Cross-tab + over all 376 `.prt` names: **referenced by `tables.pak` AND resolves 241; + neither 100; referenced but absent 35; resolves WITHOUT being referenced by + `tables.pak` — ZERO.** + 🔑 **Resolving implies a `tables.pak` reference, 0 counterexamples of 376.** + `tables.pak` holds the menu screen configs and every part it names ships as + `\.prt`. The **100** names that appear only inside the + `GP_MAIN_GAME_*2D.pak` bundles never ship as entries — the in-flight HUD is + authored into the bundles, not loaded by name. So the five `pgmsg_*.prt` were + never a special case; the real split is **menu = named + shipped, in-game = + embedded**. + ❌ **It is NOT a prefix rule** — 6 of the 135 `pg*` names DO resolve + (`pgloading`, `pgloading2`, `pgmsg_scr`, `pgpause`, `pgpause_ttrl`, `pgpbase`), + precisely because `tables.pak` names them. + 🟡 The 35-name residual is one coherent family: `tables.pak`-referenced yet + absent — `pgmenu_btn*`/`pgmenu_item*`/`pgmenu_pad`, `pgfacewin*`, `pgtextwin`, + `phinfo1-3`, `02d`, `02d_scr`, `psview_release`: the **in-game pause menu and + squadron-order overlay**, named by a menu config but drawn from the in-game + bundles. Consistent with the split, not separately proved. + 🟡 Still open: where an embedded part sits INSIDE a bundle. + * 🟡 **(2026-08-27) WHERE `.prt` SCREEN PARTS LIVE — and the five mission banners are a real exception.** [structures/mission-script-manifest](structures/mission-script-manifest.md), diff --git a/docs/re/data/prt-parts.txt b/docs/re/data/prt-parts.txt index 8f9fe3f..9a66119 100644 --- a/docs/re/data/prt-parts.txt +++ b/docs/re/data/prt-parts.txt @@ -22,6 +22,50 @@ prbase.prt resolves under: ['eng\\', 'jpn\\', 'fra\\', 'deu\\', 'ita\\', 'esp\\'] palogo1.prt resolves under: ['eng\\', 'jpn\\'] +## WHO REFERENCES a part decides whether it ships as an entry + cross-tab (referenced by tables.pak?, resolves as an entry?): + tables=True resolves=True 241 + tables=False resolves=False 100 + tables=True resolves=False 35 + => resolving IMPLIES referenced by tables.pak: 0 counterexamples of 376 + the 35 referenced-but-absent parts: + 02d.prt + 02d_scr.prt + pgfacewin.prt + pgfacewin_base.prt + pgfacewin_dead.prt + pgfacewin_em.prt + pgfacewin_tr.prt + pgmenu_btn00.prt + pgmenu_btn00f.prt + pgmenu_btn01.prt + pgmenu_btn01f.prt + pgmenu_btn10.prt + pgmenu_btn10f.prt + pgmenu_btn11.prt + pgmenu_btn11f.prt + pgmenu_btn12.prt + pgmenu_btn12f.prt + pgmenu_btn20.prt + pgmenu_btn20f.prt + pgmenu_btn21.prt + pgmenu_btn21f.prt + pgmenu_btn22.prt + pgmenu_btn22f.prt + pgmenu_btn30.prt + pgmenu_btn30f.prt + pgmenu_item0.prt + pgmenu_item1.prt + pgmenu_item2.prt + pgmenu_item3.prt + pgmenu_pad.prt + pgtextwin.prt + phinfo1.prt + phinfo2.prt + phinfo3.prt + psview_release.prt + pg* parts: 135, of which 6 resolve: ['pgloading.prt', 'pgloading2.prt', 'pgmsg_scr.prt', 'pgpause.prt', 'pgpause_ttrl.prt', 'pgpbase.prt'] + ## `_sub.rat` sub-bundles inside RATC distinct _sub.rat names: 11 pgmsg_end_sub.rat in 6 archives ['GP_MAIN_GAME_D2D.pak', 'GP_MAIN_GAME_E2D.pak'] diff --git a/docs/re/structures/mission-script-manifest.md b/docs/re/structures/mission-script-manifest.md index 36a293f..d24f9b5 100644 --- a/docs/re/structures/mission-script-manifest.md +++ b/docs/re/structures/mission-script-manifest.md @@ -126,7 +126,37 @@ but the control kills the generalisation: **only 4 of 376 `.prt` stems have a `_sub.rat`. So `_sub.rat` is not the container convention; it is something these four happen to have. -**Still not settled**: where the five mission-banner parts actually are. What is +### ✅ Who REFERENCES a part decides whether it ships as an entry + +Censused the whole 135, not the five. The cross-tab over all **376** names: + +| referenced by `tables.pak` | resolves as an entry | count | +|---|---|---:| +| yes | yes | **241** | +| no | no | **100** | +| yes | no | 35 | +| **no** | **yes** | **0** | + +**Resolving implies being referenced by `tables.pak` — 0 counterexamples in +376.** `tables.pak` holds the menu screen configs; every part it names ships as +`\.prt`. The **100** names that appear only inside the +`GP_MAIN_GAME_*2D.pak` bundles never ship as entries at all — they are the +in-flight HUD, authored into the bundles rather than loaded by name. + +🔑 So the five `pgmsg_*.prt` are not a special case of their own: they belong to +the 135, and the real split is **menu parts (named, shipped) vs in-game parts +(embedded)**. It is not a prefix rule — 6 of the 135 `pg*` names *do* resolve +(`pgloading`, `pgloading2`, `pgmsg_scr`, `pgpause`, `pgpause_ttrl`, `pgpbase`), +because `tables.pak` names them. + +🟡 **The 35-name residual is one coherent family**: referenced by `tables.pak` +yet absent — `pgmenu_btn*` / `pgmenu_item*` / `pgmenu_pad`, `pgfacewin*`, +`pgtextwin`, `phinfo1-3`, `02d`, `02d_scr`, `psview_release`. That is the +**in-game pause menu and squadron-order overlay**: named by a menu config but +drawn from the in-game bundles. Consistent with the split above; not separately +proved. + +**Still not settled**: where the embedded parts sit *inside* a bundle. What is now settled is that they are **not** pak entries while 241 of their siblings are, and that `pgmsg_update` is the odd one out even among them. diff --git a/tools/re-capture/prt_parts.py b/tools/re-capture/prt_parts.py index 882f3c2..feee77f 100644 --- a/tools/re-capture/prt_parts.py +++ b/tools/re-capture/prt_parts.py @@ -18,13 +18,14 @@ PRE = ['', 'Stage\\', 'stage\\', 'message\\', 'language\\', '2d\\', 'ui\\', 'prt def main(): H = collections.defaultdict(set) names = set() + ref = collections.defaultdict(set) rat = collections.defaultdict(set) for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)): base = os.path.basename(pk) for h, b in pak_entries(pk): H[h].add(base) for m in re.finditer(rb'[A-Za-z0-9_]{3,40}\.prt', b): - names.add(m.group(0).decode()) + names.add(m.group(0).decode()); ref[m.group(0).decode()].add(base) if b[:4] == b'RATC': for m in re.finditer(rb'[A-Za-z0-9_]{3,40}_sub\.rat', b): rat[m.group(0).decode()].add(base) @@ -50,6 +51,23 @@ def main(): ok = [p or '' for p in PRE if U.name_hash(p + n) in H] print(" %-20s resolves under: %s" % (n, ok if ok else 'NOTHING')) + res = {n: any(U.name_hash(p + n) in H for p in PRE) for n in names} + tab = lambda n: 'tables.pak' in ref[n] + ct = collections.Counter((tab(n), res[n]) for n in names) + print("\n## WHO REFERENCES a part decides whether it ships as an entry") + print(" cross-tab (referenced by tables.pak?, resolves as an entry?):") + for k in sorted(ct, key=lambda x: -ct[x]): + print(" tables=%-5s resolves=%-5s %3d" % (k[0], k[1], ct[k])) + print(" => resolving IMPLIES referenced by tables.pak: %d counterexamples of %d" + % (ct[(False, True)], len(names))) + bad = sorted(n for n in names if tab(n) and not res[n]) + print(" the %d referenced-but-absent parts:" % len(bad)) + for n in bad: + print(" %s" % n) + pg = [n for n in names if n.startswith('pg')] + print(" pg* parts: %d, of which %d resolve: %s" + % (len(pg), sum(1 for n in pg if res[n]), sorted(n for n in pg if res[n]))) + print("\n## `_sub.rat` sub-bundles inside RATC") print(" distinct _sub.rat names: %d" % len(rat)) for k in sorted(rat):