re: the stage drift is navigation, not the save -- slot 01 is deterministically Stage 02

Refutes my own claim from the previous commit that slot 01 is an auto-save whose
restored mission drifts.  savedata has not been written since 2026-08-23; every run
today left it untouched (only the .gpd profile files moved), and there is exactly
one save, so a wandering save-list cursor cannot explain it either.

Decoded the save statically (GDHA + zlib at 0x92, 545 bytes): only SHAB record 0
carries clear data, records 1-15 are zeroed.  Three independent readings agree it
is Stage 02 -- the 324773 ms clear time is the 05:24.77 that SESSION-2026-08-11
documents for Stage 01, the 4101 points match weapon-datasheet-runtime.md's
"Stage 02, At Standby, 5% clear, 4101 P", and the GHAD block at tag+8 gives
+52 = 2.  Stage 02 is the Acropolis escort mission, which is exactly the roster the
last run produced.

So the odd run out was the S01 "Glasner Training Area" one -- a TUTORIAL, reached
because the nav issued dpad-down + A from a state never confirmed to be the main
menu.  Fix is two guards: verify screen_id reads `menu` immediately before the
dpad, and assert Acropolis is in the definition table before comparing rosters.

Caveats kept: the GHAD field base was not isolated by the search (7 candidates, 3
giving stage 2 -- tag+8 chosen because it agrees with the other two readings), and
the tutorial mis-selection was not demonstrated by re-running the emulator.
This commit is contained in:
Sylpheed RE agent
2026-08-26 22:32:30 +00:00
parent a21927ab01
commit c9dc8dd4db

View File

@@ -0,0 +1,85 @@
# ✅ The stage drift is my NAVIGATION, not the save — slot 01 is deterministically Stage 02
Settled 2026-08-26, **statically** — no emulator needed.
## ❌ Refuted: "slot 01 is the auto-save, so the restored mission moves between runs"
[The previous note](two-entity-enumerations.md) blamed save drift for one run
loading the S01 training area (with `UN_S01_Asteroid_cmesh_*`) and another
loading a Stage-02-style escort roster. The file says otherwise:
```
2026-08-23 16:37 278 .../535107D4/00000001/game01/savedata
2026-08-26 22:18 26596 .../FFFE07D1/.../535107D4.gpd
```
**`savedata` has not been written since 23 August.** Every run today left it
untouched; only the `.gpd` profile/achievement files moved. And there is exactly
**one** save (`game01`), so a wandering save-list cursor cannot explain it either.
## ✅ What the save actually holds: Stage 02
The save is `GDHA` + a zlib payload at `0x92` (545 bytes decompressed), carrying
`GDAA`, `GHAD`, `NETA`, 2 × `BUNK` and the 16-record `SHAB` table.
```
rec 0 (stage 1): (2, 4101, 324773, ...)
rec 1 (stage 2): (0, 0, 0, ...)
...
rec 15 (stage 16): (0, 0, 0, ...)
```
**Only record 0 carries clear data**; records 115 have zeros in all three result
fields. Three independent readings agree on what that means:
1. `324773` ms = **05:24.77**, which
[`SESSION-2026-08-11.md`](SESSION-2026-08-11.md) documents as exactly what
MISSION SELECT displays as the Stage 01 best time.
2. `4101` matches
[`structures/weapon-datasheet-runtime.md`](structures/weapon-datasheet-runtime.md),
whose capture session is recorded as *"Stage 02, 'At Standby', 5 % clear,
**4101 P**"* — the same save, already described in the corpus.
3. Reading the `GHAD` field block at tag+8 (i.e. past the tag and its length word)
gives `+52` = **2**, the documented 1-based `Stage` field.
So `LOAD GAME → slot 01` restores **Stage 02** every time. It is deterministic.
🟡 One caveat, stated rather than smoothed over: brute-forcing the `GHAD` field
base gives **seven** candidate offsets whose `+52` is a plausible stage, three of
them yielding 2. I picked tag+8 because it is the natural layout and it agrees
with the two independent readings above — not because the search isolated it.
❌ And "stage = filled `SHAB` count + 1" must not be used: all 16 records are
*present*, and the corpus already lists that rule as **refuted**. Presence is not
filledness.
## ✅ So the S01 asteroid run was never the save
Stage 02 is the `Acropolis` escort mission — which is precisely the roster the
most recent run produced (`UN_f101_TCAF_Acropolis`, `UN_bf001_TCAF_SchlosBase`,
cruisers, frigates). The odd run out is the earlier one, whose briefing read
**"Glasner Training Area"** — a *tutorial*, not a story stage, and
[`structures/unit-struct-runtime.md`](structures/unit-struct-runtime.md) confirms
tutorials are separately reachable ("all six tutorials **and** Stage 02").
The cause is in my own driving: that run's navigation log shows the menu probe
reading `other` repeatedly and the `dpad down` + Ⓐ being issued from a state that
was not confirmed to be the main menu, so the selection landed on a tutorial
entry instead of `LOAD GAME`. The nav taps a fixed sequence and never verifies
*what it selected*.
## Consequence — the cheap fix
Runtime rosters are reproducible after all, provided the route is verified. Two
one-line guards, neither of which needs new tooling:
* Confirm `screen_id.py` reads `menu` **immediately before** the `dpad down`, not
merely at some point earlier in the loop.
* After reaching flight, assert the definition table contains
`UN_f101_TCAF_Acropolis` (Stage 02's signature) before trusting any comparison
against an earlier run.
❔ Not settled: I did not re-run the emulator to demonstrate the tutorial
mis-selection directly. The evidence here is the save file plus the earlier run's
own screen log and briefing title, which is strong but circumstantial about
*which* menu entry was hit.