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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 1–4 (Sandra, Billy, Antonius, Carl) + `Rhino1-Katana`, `Rhino2-Ellen`, `Rhino3-Gene`, `Rhino4-Yoji` |
|
||||
| 5 | Bird 1–4 + `Rhino1-Raymond`, `Rhino2-Gene`, `Rhino3-Katana`, `Rhino4-Ellen` |
|
||||
| 5 | `Rhino2-Katana` **alone** |
|
||||
| 2 | Bird 1–4 + `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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user