From 0ae0d4bca47c6a3b624aea09ab089a1d8aecc783 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Thu, 27 Aug 2026 13:27:19 +0000 Subject: [PATCH] re: the stage numbering is consistent everywhere; the manifest still declares the cut S17 Correction first: the previous commit claimed the corpus 'did not know the stage NUMBERS'. It did -- structures/mission-script-ssb.md has recorded S01-S16, S18-S29, S17 absent by a three-way convergence (hash lookup, the table sweep, the loader's != 16 guard) since before that pass. What was actually new there is the player craft per stage and ResourceID's ordinal tags; the numbering is a fourth independent route, not a discovery. The question this item asked: do the ISL StageNN.ssb files and the UnitGroup_SNN.tbl tables use the same numbering, or is something silently mis-labelled? They are identical. Stage\StageNN.ssb, stage\UnitGroup_SNN.tbl and stage\Route_SNN.tbl all resolve for the same 28 N with an EMPTY symmetric difference; AIParams is the same minus 18-23. Nothing in the corpus is mis-keyed. Formation_SNN and Message_SNN resolve 0/40 -- those are field names, not files, and the four families above are the controls that make that negative real. New: Stage\script.tbl's SCRIPTS record lists 29 MISSION*.ssb fields -- MISSION1..MISSION29, including MISSION17 = Stage17.ssb -- and only 28 of 29 name a file that ships. Control: MISSIONn -> Stage.ssb with the same n, 0/29 mismatched. So S17 is a cut mission the shipping manifest still names, which is why the loader carries an explicit != 16 guard rather than just running 0..27. Artefact +14 lines / 0 deletions; the other four regenerate byte-identical. --- docs/re/BACKLOG.md | 26 +++++++++++ docs/re/data/stage-numbering.txt | 14 ++++++ .../stage-numbering-and-player-craft.md | 43 +++++++++++++++++-- tools/re-capture/stage_numbering.py | 31 +++++++++++++ 4 files changed, 111 insertions(+), 3 deletions(-) diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 94ff47da..0f37cbab 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -827,6 +827,32 @@ 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) THE STAGE NUMBERING IS CONSISTENT EVERYWHERE — and the + manifest still declares the cut S17.** Also a **correction to my own claim** + in the entry below. + [structures/stage-numbering-and-player-craft](structures/stage-numbering-and-player-craft.md), + artefact `data/stage-numbering.txt` (+14 lines, 0 deletions — purely additive). + ❌ **Correction first**: the entry below says the corpus "did not know the + stage NUMBERS". It did — `structures/mission-script-ssb.md` has recorded + **S01-S16, S18-S29, S17 absent** by a three-way convergence (hash lookup, the + table sweep, the loader's `!= 16` guard) since before that pass. What was + actually new there is the **player craft per stage** and `ResourceID`'s + ordinal tags. The numbering is a fourth independent route, not a discovery. + ✅ **The question this item asked** — do the ISL `StageNN.ssb` files and the + `UnitGroup_SNN.tbl` tables use the same numbering, or is something silently + mis-labelled? **They are identical**: `Stage\StageNN.ssb`, + `stage\UnitGroup_SNN.tbl` and `stage\Route_SNN.tbl` all resolve for the same + 28 N, **symmetric difference EMPTY**; `AIParams` is the same minus 18-23. + Nothing in the corpus is mis-keyed. (`Formation_SNN`/`Message_SNN` resolve + 0/40 — field names, not files; the four families above are the controls that + make that negative real.) + 🔑 **New**: `Stage\script.tbl`'s `SCRIPTS` record lists **29** `MISSION*.ssb` + fields — `MISSION1`..`MISSION29`, **including `MISSION17 = Stage17.ssb`** — + and only **28 of 29** name a file that ships. Control: `MISSIONn` -> + `Stage.ssb` with the same `n`, **0/29 mismatched**. So S17 is a **cut + mission the shipping manifest still names**, which is why the loader carries + an explicit `!= 16` guard rather than just running 0..27. + * ✅✅ **(2026-08-27) THE DISC'S STAGE NUMBERS ARE NOT 1..28 — S01-S16, S17 ABSENT, S18-S23 the tutorials, S24-S29 — and the player's craft per stage falls out with it.** diff --git a/docs/re/data/stage-numbering.txt b/docs/re/data/stage-numbering.txt index dddd10a6..54155121 100644 --- a/docs/re/data/stage-numbering.txt +++ b/docs/re/data/stage-numbering.txt @@ -8,6 +8,20 @@ stage\AIParams_SNN.tbl N = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 24, 25, 26, 27, 28, 29] (count 22) stages with NO AIParams (the tutorials): [18, 19, 20, 21, 22, 23] +## CONTROL every per-stage file family uses the SAME numbering + Stage\StageNN.ssb 28 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] + stage\UnitGroup_SNN.tbl 28 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] + stage\Route_SNN.tbl 28 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] + stage\AIParams_SNN.tbl 22 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 24, 25, 26, 27, 28, 29] + stage\Formation_SNN.tbl 0 [] + stage\Message_SNN.tbl 0 [] + ssb == UnitGroup == Route : True symmetric differences [] [] + +## the manifest DECLARES a stage the disc does not ship + Stage\script.tbl SCRIPTS: 29 MISSION*.ssb fields, numbers [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] + CONTROL MISSIONn -> Stage.ssb with the same n: 0/29 mismatched + entries whose .ssb actually ships: 28/29 ; missing: ['MISSION17'] + ## ResourceID -- the player craft, keyed by stage tag 24 name rou_f002 tag 25 name rou_f002 diff --git a/docs/re/structures/stage-numbering-and-player-craft.md b/docs/re/structures/stage-numbering-and-player-craft.md index d030e892..93ff3192 100644 --- a/docs/re/structures/stage-numbering-and-player-craft.md +++ b/docs/re/structures/stage-numbering-and-player-craft.md @@ -1,8 +1,16 @@ # ✅✅ The disc's stage NUMBERING, and which craft the player flies in each -The corpus has known "the disc has 28 stages" and "the six tutorials have no -`AIParams`" for a while. **It did not know the stage NUMBERS.** They are not -1…28: +> ## ❌ CORRECTION (2026-08-27, same day) — the NUMBERING was already known +> +> This page first claimed the corpus "did not know the stage NUMBERS". Wrong: +> [mission-script-ssb](mission-script-ssb.md) has recorded **S01–S16, S18–S29, +> S17 absent** by a three-way convergence (hash lookup, the table sweep, and the +> loader's `!= 16` guard) since before this pass. What is new here is the +> **player craft per stage** and `ResourceID`'s ordinal tags; the numbering +> below is a re-derivation, not a discovery. Kept because it is the fourth +> independent route and because the craft mapping is keyed on it. + +The disc's stage numbers are not 1…28: ``` S01 … S16 story (16) @@ -63,6 +71,35 @@ Every number closes against `ResourceID`, from a different file: 🔑 So **the player flies the DeltaSaber A in exactly one mission — S28** — and the DeltaSaber T only for the first six. +## ✅ Every per-stage file family uses the SAME numbering + +Checked because a mismatch would have silently mis-labelled stages across the +corpus. It does not exist: + +| family | count | N | +|---|---:|---| +| `Stage\StageNN.ssb` | 28 | 1–16, 18–29 | +| `stage\UnitGroup_SNN.tbl` | 28 | identical | +| `stage\Route_SNN.tbl` | 28 | identical | +| `stage\AIParams_SNN.tbl` | 22 | the same minus 18–23 | + +**`ssb == UnitGroup == Route`, symmetric difference empty.** (`Formation_SNN` and +`Message_SNN` resolve 0/40 — those are field names, not files; the four families +above are the positive controls that make that a real negative.) + +## 🔑 The manifest declares a stage the disc does not ship + +`Stage\script.tbl`'s `SCRIPTS` record lists **29** `MISSION*.ssb` fields — +`MISSION1` … `MISSION29`, **including `MISSION17 = Stage17.ssb`**. Only **28 of +the 29** name a file that is actually in the pak; the one missing is +`MISSION17`. Control: `MISSIONn` → `Stage.ssb` with the same `n`, **0/29 +mismatched**. + +So S17 is not a numbering quirk — it is a **cut mission that the shipping +manifest still names**, which is why the loader carries an explicit `!= 16` +guard ([isl-condition-builtins](isl-condition-builtins.md)) rather than simply +running 0…27. + ## 🟡 Not settled * The Arsenal entry FILENAMES are still unrecovered, so *which* of the 168 diff --git a/tools/re-capture/stage_numbering.py b/tools/re-capture/stage_numbering.py index f5ee2098..49293c1e 100644 --- a/tools/re-capture/stage_numbering.py +++ b/tools/re-capture/stage_numbering.py @@ -31,6 +31,37 @@ def main(): print(" stage\\AIParams_SNN.tbl N = %s (count %d)" % (ai, len(ai))) print(" stages with NO AIParams (the tutorials): %s" % [i for i in ug if i not in ai]) + print("\n## CONTROL every per-stage file family uses the SAME numbering") + fam = [('Stage\\StageNN.ssb', 'Stage\\Stage%02d.ssb'), + ('stage\\UnitGroup_SNN.tbl', 'stage\\UnitGroup_S%02d.tbl'), + ('stage\\Route_SNN.tbl', 'stage\\Route_S%02d.tbl'), + ('stage\\AIParams_SNN.tbl', 'stage\\AIParams_S%02d.tbl'), + ('stage\\Formation_SNN.tbl', 'stage\\Formation_S%02d.tbl'), + ('stage\\Message_SNN.tbl', 'stage\\Message_S%02d.tbl')] + sets = {} + for label, tpl in fam: + sets[label] = {i for i in range(0, 40) if U.name_hash(tpl % i) in H} + print(" %-26s %2d %s" % (label, len(sets[label]), sorted(sets[label]))) + a, b = sets['Stage\\StageNN.ssb'], sets['stage\\UnitGroup_SNN.tbl'] + c = sets['stage\\Route_SNN.tbl'] + print(" ssb == UnitGroup == Route : %s symmetric differences %s %s" + % (a == b == c, sorted(a ^ b), sorted(a ^ c))) + + import re as _re + sh = U.name_hash('Stage\\script.tbl') + sc = [r for r in U.parse(dict(pak_entries(mg))[sh]) if r['squadron'] == 'SCRIPTS'][0] + n = U.named(sc) + miss = {k: v for k, v in n.items() if k.startswith('MISSION') and v.endswith('.ssb')} + nums = sorted(int(_re.match(r'MISSION(\d+)', k).group(1)) for k in miss) + bad = [(k, v) for k, v in sorted(miss.items()) + if v != 'Stage%02d.ssb' % int(_re.match(r'MISSION(\d+)', k).group(1))] + ship = [k for k, v in miss.items() if U.name_hash('Stage\\' + v) in H] + print("\n## the manifest DECLARES a stage the disc does not ship") + print(" Stage\\script.tbl SCRIPTS: %d MISSION*.ssb fields, numbers %s" % (len(miss), nums)) + print(" CONTROL MISSIONn -> Stage.ssb with the same n: %d/%d mismatched" % (len(bad), len(miss))) + print(" entries whose .ssb actually ships: %d/%d ; missing: %s" + % (len(ship), len(miss), sorted(set(miss) - set(ship)))) + recs = U.parse(E[U.name_hash('eng\\GP_HANGAR_ARSENAL_3D.tbl')]) rid = [r for r in recs if r['squadron'] == 'ResourceID'][0] print("\n## ResourceID -- the player craft, keyed by stage")