# `Stage\StageNN.ssb` — the mission scripts, found Status: ✅ located, container header and both symbol tables decoded, verified against the loader's own disassembly; 🟡 the mission-level instruction stream is only partly read; ❔ the 25 ISL opcodes and 147 built-ins are not yet decoded. [mission-phase-advance](../mission-phase-advance.md) established that a phase ends when its **script** says so, and recorded the bytecode as "not on the disc under any obvious name". **It is on the disc** — the earlier search failed because every pak entry is `Z1` + zlib, so no plaintext name is greppable. ## ✅ How the loader names it — a table key, not a filename `sub_8225EE20` never opens a file. It resolves a chain of *table keys*: ``` 0x8225F090 vtbl[1]() on the game-part object -> group index 0x8225F0AC index into 0x820A1630 (29 GamePart names) -> "GP_MAIN_GAME" [17] 0x8225F0D4 prefix "game:\" (0x820A1864) 0x8225F168 look up key GP_SCRIPT (0x820A8244) 0x8225F1B8 look up section SCRIPTS (0x820A823C); 0x824A9088 = fatal if absent 0x8225F1DC sub_824483D0 enumerates that section against the MISSION*/PRT name arrays ``` `dat/tables.pak` → `name_hash("eng\GP_MAIN_GAME.tbl")` carries `GP_SCRIPT.TABLE = script.tbl`, and `name_hash("Stage\script.tbl") = 0x75FE4656` is the manifest — an IDXD whose `SCRIPTS` record has **40 fields**: ``` MISSION1 = Stage01.ssb … MISSION29 = Stage29.ssb MISSION_{START,END,UPDATE,FAILED,RESTART}_PRT = pgmsg_*.prt DIALOG_MESSAGE, DIALOG_LOCAL_STRING, FONT, FONT_SIZE, TEXT_POS, TEXT_LINES ``` Verified here: the record is present and reads exactly that. ## ✅ 28 scripts, and the missing one is S17 — a three-way convergence `name_hash("Stage\\StageNN.ssb")` resolves for **28** stages: S01–S16, S18–S29. **S17 is absent**, and that agrees with two entirely independent findings: * the table sweep in [stage-definition-table](stage-definition-table.md) — S17 appears in none of the five per-stage table families; * the loader's own guard, `sub_8225EC78`: `if (n == 16 || n > 32) return`. Index 16 skipped ⇒ the mission number there is **0-based** ⇒ it is `MISSION17` that is skipped. Three routes to the same conclusion, from data, from a hash lookup and from control flow. `MISSION30..33` exist as literals in the executable but appear in no manifest and on no disc. Stage 02, verified byte-for-byte: ``` dat/GP_MAIN_GAME_E.pak key 0x5C2D6E8B = name_hash("Stage\Stage02.ssb") 226,596 bytes decompressed md5 aff69b5ade4c3c061a132ee5b88ac5a1 ``` Byte-identical across all six language paks, so the scripts are not localised. ## ✅ Container header (big-endian, 0x24 bytes) Field meanings taken from `ScriptMission::Load` (`sub_82260568`), not guessed: | off | Stage02 | meaning | |---|---|---| | `+0x00` | `00000002` | version, always 2 | | `+0x04` | `00000000` | → `[ScriptMission+36]` (`0x822605A4`) | | `+0x08` | `00000024` | code offset → `[+24]` code base, `[+28]` pc (`0x822605AC`) | | `+0x0C` | `00034C80` | symbol table 1 → `[+44]` (`0x822605C0`) | | `+0x10` | `00036D60` | symbol table 2 | | `+0x14…0x23` | `FF×16` | four `-1` slots, patched at runtime | Both tables are `u32 count; u32 offsets[count]`, offsets relative to the word after `count`, each record `u32 type; char name[]` NUL-padded to 4. A zero offset is an unused slot. ## ✅ The symbol tables tie the script to the data tables Stage 02, parsed here: | table | count | slots used | types | |---|---|---|---| | symtab1 | 359 | 326 | **6** ×197 message ids, **1** ×119 route names, **7** ×10 `SUBOBJ_*`/`eff_*` | | symtab2 | 122 | 122 | **2** ×111 unit ids, **8** ×11 characters | Two things fall out, and both close loops elsewhere: * **Type 1 symbols are `Route_TCN001_p1F`-style names** — so the script holds the route names *as symbols*. That reconciles the finding in [mission-phase-advance](../mission-phase-advance.md) that *nothing in the executable parses `Route_*_p*`: the parsing never happens because the compiler already resolved them, and the script simply refers to the ones it wants. The `_pN` convention is real, and it is the **script's**, not a runtime parser's. * **Type 2 is 111 unit ids** (`ADN101`, `TCN328`, `NP_Amalthea`) — exactly the **111 squadron records** in `stage\UnitGroup_S02.tbl`. The script addresses squadrons one-for-one. Phase-tagged message ids give the phase count directly: Stage 02 carries `S02_P1_*`, `S02_P2_*` and `S02_P3_*` → **3 phases**, matching the stage record. Stage 01 has two, Stage 29 has one. ## 🔴 Refuted along the way * **The `.embsec_` sections are not the payload.** All seven (129,472 B total) decode as **32,368 PPC instructions in 108 functions**, each starting with a real prologue (`mflr r12; bl `). They are code — overlays/math — not script. * **`hidden/MiscBin.pak` does not hold it** — zero hits across its 40 entries, and zero across `DefTables.pak`'s 1,465. * Sweeping all 41 paks / 26,443 TOC entries, the manifest strings appear in **exactly one entry per pak**, `0x75FE4656`, in the six `GP_MAIN_GAME_.pak`. Nothing is DLC, generated, or in `$SystemUpdate`. ## ❔ Still open: the instruction encoding * **ISL (the `ScriptPhase` VM) — dispatcher confirmed**, not decoded: `0x822635D4` reads a big-endian u32 and takes the opcode from its **low byte**, `cmplwi 0x18` ⇒ 25 opcodes, jump table `0x822635FC`. * **The mission-level stream at `+0x24` is not pinned.** Its first 48 words form three near-identical groups — one per Stage 02 phase — each ending in a pair of offsets that land on regions where ≥80 % of low bytes are in `0..0x18`, i.e. plausible ISL entry points, two per phase. But `1883`'s operand is *not* uniformly a code pointer: two of them land on IEEE floats (`44 8E 80 00` = 1140.0, `43 B4 00 00` = 360.0). Whether offsets are file- or code-base-relative (±0x24) is unsettled. **The next step for the port** is decoding the 25 ISL opcodes and the 147 built-ins (`0x8227226C`) against `Stage02.ssb`, looking for calls to built-ins **6** (`0x82272540`) and **62** (`0x82272558`) — the two that set `[ScriptPhase+196]` and so end a phase. That yields the actual per-phase clear condition for every stage.