re: who selects a loadout -- the pak entry does; the Arsenal pak is stage-scoped

Settled by elimination with two controls.  The 15 loadout names appear
as a field VALUE nowhere on the disc -- 0 occurrences across every pak
-- while the control Arbalest_155KG, which is referenced, appears 150
times as a value in the same pak.  They occur only as the field names of
the UNITS record.  They are not in the executable either; control:
WEAPONS (a section key) is, Arbalest_155KG is not.

So nothing references a loadout by name.  The selection is which pak
entry the Hangar loads.  GP_HANGAR_ARSENAL.pak has 180 IDXD entries in
10 shapes; 168 carry a UNITS roster = 28 stages x 6 languages, and the
other 12 = 2 x 6.  Each entry is one stage's whole Hangar config, and
its UNITS roster is the flight for that stage -- 5 distinct rosters over
15 / 5 / 5 / 2 / 1 stages, including one where Rhino2-Katana flies
alone.  Every count is a multiple of 6 and they sum to 28, the disc's
stage count by a fourth independent route.

Corrects the previous commit: it read the pak with setdefault, so
'15 loadouts / 24 allow-lists / STANDARD_ARM1 has 11 entries' is the
union of first-seen records, not one table.  Contents vary per entry --
PlayerSET_ARM1 has 5 distinct contents, STANDARD_ARM1 3, ExSET_NOSE 4.
The chain and both controls (60/60, 70/88) are per-record and
unaffected; only the per-list sizes were over-generalised.

Not settled: the entry filenames.  name_hash probing over 8 templates x
40 indices x 6 languages resolved 0, while the same probe's controls
<lang>\weapon.tbl and <lang>\strings.tbl resolved 12/12.

Artefact +40 lines / 1 changed heading; the other four regenerate
byte-identical.
This commit is contained in:
Sylpheed RE agent
2026-08-27 13:09:07 +00:00
parent 01a5ed0e29
commit 0ea52c0f48
4 changed files with 156 additions and 3 deletions

View File

@@ -827,6 +827,41 @@ 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 SELECTS A LOADOUT: THE PAK ENTRY DOES — the Arsenal pak
is STAGE-SCOPED, 168 entries = 28 stages x 6 languages.** Also a **correction
to the entry below**.
[structures/hangar-loadout-system](structures/hangar-loadout-system.md),
artefact `data/hangar-loadouts.txt` (+40 lines, 1 changed heading).
🧪 Settled by elimination with two controls. The 15 loadout names appear as a
field **VALUE nowhere on the disc — 0 occurrences across every pak** — while
the control `Arbalest_155KG`, which *is* referenced, appears **150** times as a
value in the same pak. They occur only as the **field names of the `UNITS`
record**. They are **not in the executable** either; control: `WEAPONS` (a
section key) is, `Arbalest_155KG` is not.
So nothing references a loadout by name — the selection is **which pak entry
the Hangar loads**. `GP_HANGAR_ARSENAL.pak` has 180 IDXD entries in 10 shapes;
**168 carry a `UNITS` roster = 28 x 6**, and the other 12 = 2 x 6 (the item
table and a sibling). Each entry is one stage's whole Hangar config.
🔑 The `UNITS` roster is **the flight for that stage**, and it changes across
the campaign as the story does — 5 distinct rosters over 15 / 5 / 5 / 2 / 1
stages: Bird 1-4 + `Rhino1-Katana`/`Rhino2-Ellen`/`Rhino3-Gene`/`Rhino4-Yoji`;
a Raymond/Gene/Katana/Ellen variant; **`Rhino2-Katana` ALONE** (5 stages);
a 7-pilot variant; and one with `Rhino4-Brandon`. **Every count is a multiple
of 6 and they sum to 28** — the disc's stage count by a fourth independent
route.
⚠️ **CORRECTION to the entry below**: it read the pak with `setdefault`, so
"15 loadouts / 24 allow-lists / `STANDARD_ARM1` has 11 entries" is the union of
**first-seen** records, not one table. The contents genuinely vary per entry —
`PlayerSET_ARM1` has **5** distinct contents, `STANDARD_ARM1` **3**,
`ExSET_NOSE` **4**; the `SNNSET_*`/`NULL_*` sets have exactly 1 each. The chain
and both controls (60/60, 70/88) are per-record and unaffected; only the
per-list sizes were over-generalised.
🟡 Not settled: the entry FILENAMES. `name_hash` probing over 8 templates x 40
indices x 6 languages resolved **0**, while the same probe's controls
`<lang>\weapon.tbl` and `<lang>\strings.tbl` resolved **12/12** — the method
works, the convention is not one of the eight guessed. So which entry is which
stage is still unknown.
***(2026-08-27) THE HANGAR LOADOUT SYSTEM — the layer above the item chain.**
[structures/hangar-loadout-system](structures/hangar-loadout-system.md),
artefact `data/hangar-loadouts.txt`, regenerator

View File

@@ -2,9 +2,48 @@
# Regenerate: python3 tools/re-capture/hangar_loadouts.py
# See docs/re/structures/hangar-loadout-system.md
## counts
## the pak is STAGE-SCOPED -- one config entry per stage per language
total pak entries 1538 IDXD entries 180
entries carrying a UNITS roster 168 = 28 stages x 6 languages
entries without one 12 = 2 x 6 (the item table + siblings)
every roster count is a multiple of 6: True ; they sum to 28 stages
## the 5 distinct UNITS rosters -- who is in the flight, per stage
15 stage(s) 8 pilots: Bird1-Sandra, Bird2-Billy, Bird3-Antonius, Bird4-Carl, Rhino1-Katana, Rhino2-Ellen, Rhino3-Gene, Rhino4-Yoji
5 stage(s) 8 pilots: Bird1-Sandra, Bird2-Billy, Bird3-Antonius, Bird4-Carl, Rhino1-Raymond, Rhino2-Gene, Rhino3-Katana, Rhino4-Ellen
5 stage(s) 1 pilots: Rhino2-Katana
2 stage(s) 7 pilots: Bird1-Sandra, Bird2-Billy, Bird3-Antonius, Bird4-Carl, Rhino1-Raymond, Rhino2-Katana, Rhino3-Ellen
1 stage(s) 4 pilots: Rhino1-Katana, Rhino2-Ellen, Rhino3-Gene, Rhino4-Brandon
## counts, UNION over all entries (each name first-seen)
arsenal items 59 loadout records 15 per-slot allow-lists 24
## allow-list CONTENT VARIES BY ENTRY -- distinct contents per name
ExSET_ARM1 3
ExSET_ARM2 3
ExSET_ARM3 3
ExSET_NOSE 4
NULL_ARM1 1
NULL_ARM2 1
NULL_ARM3 1
NULL_NOSE 1
PlayerSET_ARM1 5
PlayerSET_ARM2 5
PlayerSET_ARM3 4
PlayerSET_NOSE 4
S01SET_ARM1 1
S01SET_ARM2 1
S01SET_ARM3 1
S01SET_NOSE 1
S02SET_ARM1 1
S02SET_ARM2 1
S02SET_ARM3 1
S02SET_NOSE 1
STANDARD_ARM1 3
STANDARD_ARM2 3
STANDARD_ARM3 3
STANDARD_NOSE 2
## loadout records
Bird1-Sandra S01SET_ARM1 S01SET_ARM2 S01SET_ARM3 S01SET_NOSE UnitID=SANDRA (character)
Bird2-Billy S01SET_ARM1 S01SET_ARM2 S01SET_ARM3 S01SET_NOSE UnitID=BILLY (character)

View File

@@ -7,6 +7,23 @@ with what**, and **what the Hangar is allowed to offer for each slot**.
Artefact `../data/hangar-loadouts.txt`, regenerator
`tools/re-capture/hangar_loadouts.py`.
> ## ⚠️ CORRECTION (2026-08-27, same day) — the pak is STAGE-SCOPED, and the
> ## counts below are a UNION, not one table
>
> `GP_HANGAR_ARSENAL.pak` has **180 IDXD entries in 10 distinct shapes**, and
> **168 of them = 28 stages × 6 languages** carry a `UNITS` roster. Each entry is
> **one stage's Hangar configuration**: its own `UNITS` roster, its own loadout
> records, its own allow-lists. The first pass read the pak with `setdefault`,
> keeping only the **first-seen** copy of each record name — so "15 loadouts / 24
> allow-lists / `STANDARD_ARM1` has 11 entries" is the union of first-seen, not a
> single table. The allow-list contents genuinely vary: `PlayerSET_ARM1` has **5**
> distinct contents across entries, `STANDARD_ARM1` **3**, `ExSET_NOSE` **4**;
> the `SNNSET_*` and `NULL_*` sets have exactly 1 each.
>
> **The structure of the chain below is unaffected** — the 60/60 and 70/88
> controls are per-record and still hold on the first-seen slice — but treat the
> per-list sizes as "one stage's", not "the game's".
`GP_HANGAR_ARSENAL.pak` carries **15 loadout records**`Bird1-Sandra`,
`Bird2-Billy`, `Bird3-Antonius`, `Bird4-Carl`, `Rhino1-Katana`,
`Rhino1-Raymond`, `Rhino2-Ellen`, `Rhino2-Gene`, `Rhino2-Katana`,
@@ -79,9 +96,43 @@ two Arm1 items, `STANDARD_ARM1` offers eleven.
Both are single rows against 13 that resolve, so they are exceptions in the
data, not a wrong reading of the field.
## ✅ Who selects a loadout — the pak ENTRY does
Settled the same day, by elimination with two controls:
* The 15 loadout names appear as a field **value nowhere on the disc** — 0
occurrences across every pak. **Positive control in the same loop:** the item
name `Arbalest_155KG`, which *is* referenced, appears **150** times as a value
in the same pak. They appear only as the **field names of the `UNITS` record**,
i.e. `UNITS` is the index over them.
* They are **not in the executable** either. **Control:** `WEAPONS` (a section
key) *is* in the executable; `Arbalest_155KG` is not — so the executable holds
table keys, not row names.
So nothing references a loadout by name. The selection is **which pak entry the
Hangar loads**, and there is one per stage per language (168 = 28 × 6). The
entry's `UNITS` roster is the flight for that stage, and it changes across the
campaign exactly as the story does:
| stages | pilots in `UNITS` |
|---:|---|
| 15 | Bird 14 (Sandra, Billy, Antonius, Carl) + `Rhino1-Katana`, `Rhino2-Ellen`, `Rhino3-Gene`, `Rhino4-Yoji` |
| 5 | Bird 14 + `Rhino1-Raymond`, `Rhino2-Gene`, `Rhino3-Katana`, `Rhino4-Ellen` |
| 5 | `Rhino2-Katana` **alone** |
| 2 | Bird 14 + `Rhino1-Raymond`, `Rhino2-Katana`, `Rhino3-Ellen` |
| 1 | `Rhino1-Katana`, `Rhino2-Ellen`, `Rhino3-Gene`, `Rhino4-Brandon` |
Every count is a multiple of 6 and they sum to **28** — the disc's stage count,
reached here by a fourth independent route.
🟡 The entry FILENAMES are not recovered. Probing `name_hash` over 8 templates ×
40 indices × 6 languages resolved **0**; the same probe's controls
`<lang>\weapon.tbl` and `<lang>\strings.tbl` resolved **12/12**, so the method
works and the naming convention is simply not one of the eight guessed.
## 🟡 Not settled
* Who *selects* a loadout record. The names encode flight position and pilot,
* Which entry is which stage — see the filename note above. The names encode flight position and pilot,
and [the stage definition table](stage-definition-table.md) has
`AI_TCAF_RhinoFlight` / `AI_TCAF_BirdFlight` — the join is not shown here.
* `ExSET_*`, `S02SET_*` and `NULL_*` are referenced by no loadout in this pak.

View File

@@ -43,12 +43,40 @@ def main():
elif set(SLOTS) <= set(n): loadouts.setdefault(nm, r)
elif set(n) == {'Type'}: allow.setdefault(nm, r)
idxd = [(h, U.parse(b)) for h, b in pak_entries(ars) if b[:4] == b'IDXD']
withu = [(h, r) for h, r in idxd if any(x['squadron'] == 'UNITS' for x in r)]
rosters = collections.Counter()
for h, recs in withu:
for r in recs:
if r['squadron'] == 'UNITS':
rosters[tuple(sorted(n for t, n, v in r['fields']))] += 1
print("# The Hangar loadout system")
print("# Regenerate: python3 tools/re-capture/hangar_loadouts.py")
print("# See docs/re/structures/hangar-loadout-system.md")
print("\n## counts")
print("\n## the pak is STAGE-SCOPED -- one config entry per stage per language")
print(" total pak entries %4d IDXD entries %3d" % (len(pak_entries(ars)), len(idxd)))
print(" entries carrying a UNITS roster %3d = %d stages x 6 languages"
% (len(withu), len(withu) // 6))
print(" entries without one %3d = %d x 6 (the item table + siblings)"
% (len(idxd) - len(withu), (len(idxd) - len(withu)) // 6))
print(" every roster count is a multiple of 6: %s ; they sum to %d stages"
% (all(c % 6 == 0 for c in rosters.values()), sum(c // 6 for c in rosters.values())))
print("\n## the %d distinct UNITS rosters -- who is in the flight, per stage" % len(rosters))
for t, c in rosters.most_common():
print(" %2d stage(s) %d pilots: %s" % (c // 6, len(t), ", ".join(t)))
print("\n## counts, UNION over all entries (each name first-seen)")
print(" arsenal items %3d loadout records %3d per-slot allow-lists %3d"
% (len(items), len(loadouts), len(allow)))
var = collections.defaultdict(set)
for h, recs in idxd:
for r in recs:
if set(U.named(r)) == {'Type'}:
var[r['squadron']].add(tuple(v for t, n, v in r['fields'] if n is None))
print("\n## allow-list CONTENT VARIES BY ENTRY -- distinct contents per name")
for k in sorted(var):
print(" %-18s %d" % (k, len(var[k])))
print("\n## loadout records")
for nm in sorted(loadouts):