From 1132d9522708e157aefc15bfd8a8f1242d0b6c24 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Thu, 27 Aug 2026 14:27:38 +0000 Subject: [PATCH] re: an embedded .prt part is a top-level RATC bundle, addressed by its element prefix Read first: ui-rat-layout.md OWNS the RATC stack and already documents the 60-byte element declaration table and 'one bundle = one (context x language) build of that screen'. What it does not say is how a part is addressed when it is NOT a pak entry. The elements of one bundle share a common name prefix, and that prefix is the part name. GP_MAIN_GAME_E2D.pak has 130 bundles with 114 distinct element prefixes -- pgmenu_btn00, pghud_wing, pgface, pghud_range, pgmanuva_eff0 -- exactly the in-game part names. The 100 in-game-only .prt names match a 2D bundle prefix 68 times; the control, the 241 shipped parts, matches ZERO. The two families are disjoint on the test. 13 shipped parts match a bundle prefix in their own screen pak, which is the expected shape. Four of the five mission banners land here: pgmsg_start.prt is E2D bundle 0x89fac252, a one-element bundle declaring pgmsg_start_sub.rat; likewise _end_, _failed_, _restart_. pgmsg_update has no bundle at all, consistent with having no _sub.rat. 32 in-game names still have no 2D bundle. They cluster into families whose base name is a bundle, reading as a variant declared inside a parent bundle -- a reading, not adopted. Artefact +38 lines / 0 deletions; the other six regenerate byte-identical. --- docs/re/BACKLOG.md | 27 +++++++++++++ docs/re/data/prt-parts.txt | 38 +++++++++++++++++++ docs/re/structures/mission-script-manifest.md | 38 ++++++++++++++++++- tools/re-capture/prt_parts.py | 34 ++++++++++++++++- 4 files changed, 135 insertions(+), 2 deletions(-) diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index f9f2623..0ab9268 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -827,6 +827,33 @@ 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) AN EMBEDDED PART IS A TOP-LEVEL RATC BUNDLE, ADDRESSED BY ITS + ELEMENT PREFIX โ€” 68 of 100, against 0 of 241 for the shipped parts.** + [structures/mission-script-manifest](structures/mission-script-manifest.md), + artefact `data/prt-parts.txt` (+38 lines, 0 deletions โ€” purely additive). + Read first: `ui-rat-layout.md` OWNS the RATC stack and already documents the + 60-byte element declaration table at `0x20` and "one bundle = one + (context x language) build of that screen". What it does not say is how a part + is addressed when it is NOT a pak entry. + ๐Ÿ”‘ **The elements of one bundle share a common name prefix, and that prefix is + the part name.** `GP_MAIN_GAME_E2D.pak` = **130 bundles, 114 distinct element + prefixes** โ€” `pgmenu_btn00`, `pghud_wing`, `pgface`, `pghud_range`, + `pgmanuva_eff0`, i.e. exactly the in-game part names. + ๐Ÿงช **The 100 in-game-only `.prt` names match a 2D bundle prefix 68 times; the + CONTROL โ€” the 241 shipped parts โ€” matches ZERO.** The two families are disjoint + on the test. (13 shipped parts match a bundle prefix in their OWN screen pak, + which is the expected shape.) + Four of the five mission banners land here: `pgmsg_start.prt` is E2D bundle + `0x89fac252`, a ONE-element bundle declaring `pgmsg_start_sub.rat`; likewise + `_end_` `0x92239624`, `_failed_` `0xc01017fc`, `_restart_` `0x606fef65`. + **`pgmsg_update` has no bundle at all**, consistent with having no `_sub.rat`. + ๐ŸŸก **32 in-game names still have no 2D bundle.** They cluster into families + whose BASE name is a bundle โ€” `pghud_wing_act_*` beside `pghud_wing`, + `pghud_range_*` beside `pghud_range`, `pgmanuva_eff1..3` beside + `pgmanuva_eff0`, `pgtarget_*` โ€” reading as "a variant declared inside a parent + bundle". **A reading, not adopted**; nothing here shows the variant mechanism. + ๐ŸŸก Still open: those 32, and `pgmsg_update`, absent from every route tried. + * โœ… **(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), diff --git a/docs/re/data/prt-parts.txt b/docs/re/data/prt-parts.txt index 9a66119..cf15bc5 100644 --- a/docs/re/data/prt-parts.txt +++ b/docs/re/data/prt-parts.txt @@ -66,6 +66,44 @@ psview_release.prt pg* parts: 135, of which 6 resolve: ['pgloading.prt', 'pgloading2.prt', 'pgmsg_scr.prt', 'pgpause.prt', 'pgpause_ttrl.prt', 'pgpbase.prt'] +## AN EMBEDDED PART IS A TOP-LEVEL RATC BUNDLE, named by its element prefix + RATC bundle element-prefixes: 363 distinct (2D paks: 114) + in-game-only .prt (100): match a 2D bundle prefix 68 ; any bundle prefix 68 + CONTROL shipped .prt (241): match a 2D bundle prefix 0 ; any bundle prefix 13 + in-game .prt with NO 2D bundle (32): + pggauge_armor_eff1.prt + pggauge_shield_eff1.prt + pghud_GAremainder.prt + pghud_OBremainder.prt + pghud_arms_active1em.prt + pghud_arms_active1oh.prt + pghud_arms_active2em.prt + pghud_arms_active2oh.prt + pghud_arms_active3em.prt + pghud_arms_active3oh.prt + pghud_range_bar_em.prt + pghud_range_main.prt + pghud_range_nose.prt + pghud_subtarget.prt + pghud_target_locator_easy.prt + pghud_wing_act_attack.prt + pghud_wing_act_dispersion.prt + pghud_wing_act_formation.prt + pghud_wing_act_guard.prt + pghud_wing_alert.prt + pghud_wing_damage.prt + pgmanuva_eff1.prt + pgmanuva_eff2.prt + pgmanuva_eff3.prt + pgmark_em_ob_s.prt + pgmsg_update.prt + pgremaining.prt + pgtarget_G_missile.prt + pgtarget_cannon.prt + pgtarget_gun.prt + pgtarget_laser.prt + pgtimer.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 d24f9b5..41cc9f9 100644 --- a/docs/re/structures/mission-script-manifest.md +++ b/docs/re/structures/mission-script-manifest.md @@ -156,7 +156,43 @@ yet absent โ€” `pgmenu_btn*` / `pgmenu_item*` / `pgmenu_pad`, `pgfacewin*`, 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 +### โœ… An embedded part IS a top-level RATC bundle, named by its element prefix + +[ui-rat-layout](ui-rat-layout.md) owns this stack and already says a top-level +RATC bundle is *"one (context ร— language) build of that screen"* with a 60-byte +element declaration table at `0x20`. What it does not say is **how a part is +addressed when it is not a pak entry**. It is addressed by the bundle itself: +the elements of one bundle share a common name prefix, and **that prefix is the +part name**. + +`GP_MAIN_GAME_E2D.pak` holds **130** bundles with **114** distinct element +prefixes โ€” `pgmenu_btn00`, `pghud_wing`, `pgface`, `pghud_range`, +`pgmanuva_eff0`, โ€ฆ i.e. exactly the in-game part names. + +| | matches a **2D** bundle prefix | matches any bundle prefix | +|---|---:|---:| +| the 100 in-game-only `.prt` | **68** | 68 | +| **CONTROL** the 241 shipped `.prt` | **0** | 13 | + +**0 of 241** is what makes this a result rather than a coincidence: the two +families are disjoint on the test. (The 13 are shipped parts matching a bundle +in their *own* screen pak, which is expected.) + +Four of the five mission banners land here: `pgmsg_start.prt` is the E2D bundle +`0x89fac252`, a one-element bundle declaring `pgmsg_start_sub.rat`; likewise +`_end_` `0x92239624`, `_failed_` `0xc01017fc`, `_restart_` `0x606fef65`. +**`pgmsg_update` has no bundle at all** โ€” consistent with it having no +`_sub.rat` either. + +๐ŸŸก **32 in-game names still have no 2D bundle** (listed in the artefact). They +visibly cluster into families whose *base* name is a bundle โ€” `pghud_wing_act_*` +beside `pghud_wing`, `pghud_range_*` beside `pghud_range`, `pgmanuva_eff1..3` +beside `pgmanuva_eff0`, `pgtarget_*` โ€” which reads as "a variant declared inside +a parent bundle rather than a bundle of its own". **A reading, not adopted**; +nothing here shows the variant mechanism. + +**Still not settled**: those 32, and `pgmsg_update`, which is absent from every +route tried. 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 feee77f..5de2ac0 100644 --- a/tools/re-capture/prt_parts.py +++ b/tools/re-capture/prt_parts.py @@ -3,7 +3,7 @@ Regenerates docs/re/data/prt-parts.txt. """ -import sys, os, glob, re, collections +import sys, os, glob, re, struct, collections HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) @@ -15,10 +15,21 @@ FIVE = ['pgmsg_start.prt', 'pgmsg_end.prt', 'pgmsg_update.prt', PRE = ['', 'Stage\\', 'stage\\', 'message\\', 'language\\', '2d\\', 'ui\\', 'prt\\', 'view\\'] + [l + '\\' for l in ('eng', 'jpn', 'fra', 'deu', 'ita', 'esp')] +def _elements(b): + n = struct.unpack_from('>I', b, 0x14)[0] + if not (0 < n < 4000) or 0x20 + n * 60 > len(b): + return [] + return [b[0x20 + i * 60:0x20 + i * 60 + 28].split(b'\x00')[0].decode('latin-1', 'replace') + for i in range(n)] + +def _stem(e): + return re.sub(r'\.(t32|rat|prm)$', '', e) + def main(): H = collections.defaultdict(set) names = set() ref = collections.defaultdict(set) + pref2d, prefall = set(), set() rat = collections.defaultdict(set) for pk in sorted(glob.glob('/work/sylph_extract/**/*.pak', recursive=True)): base = os.path.basename(pk) @@ -29,6 +40,13 @@ def main(): 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) + e = _elements(b) + if e: + pfx = os.path.commonprefix([_stem(x) for x in e]).rstrip('_') + if pfx: + prefall.add(pfx) + if '2D.pak' in base: + pref2d.add(pfx) print("# Where `.prt` screen parts live") print("# Regenerate: python3 tools/re-capture/prt_parts.py") @@ -68,6 +86,20 @@ def main(): 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]))) + ing = [n for n in names if not res[n] and 'tables.pak' not in ref[n]] + sh = [n for n in names if res[n]] + match = lambda ns, S: sum(1 for n in ns if n[:-4] in S or n[:-4] + '_sub' in S) + print("\n## AN EMBEDDED PART IS A TOP-LEVEL RATC BUNDLE, named by its element prefix") + print(" RATC bundle element-prefixes: %d distinct (2D paks: %d)" % (len(prefall), len(pref2d))) + print(" in-game-only .prt (%d): match a 2D bundle prefix %d ; any bundle prefix %d" + % (len(ing), match(ing, pref2d), match(ing, prefall))) + print(" CONTROL shipped .prt (%d): match a 2D bundle prefix %d ; any bundle prefix %d" + % (len(sh), match(sh, pref2d), match(sh, prefall))) + resid = sorted(n for n in ing if n[:-4] not in pref2d and n[:-4] + '_sub' not in pref2d) + print(" in-game .prt with NO 2D bundle (%d):" % len(resid)) + for n in resid: + print(" %s" % n) + print("\n## `_sub.rat` sub-bundles inside RATC") print(" distinct _sub.rat names: %d" % len(rat)) for k in sorted(rat):