The names in menu-state-in-memory.md are wrong for at least two of the three
words, and the test that shows it is going backwards. Driving deep into the
menus, then three presses of B:
0x828F38AC "cursor" 36 -> 38 -> 40 -> 41
0x828F37B4 "misc" 971 -> 1067 -> 1068 -> 1068
0x828A690C "screen" 56 -> 56, 56, 56
A cursor returns when you go back. These only ever increase -- monotonic
counters, with 0x828F38AC advancing about 2 per input. Driving forward produced
1,3,4,5,6,8,10,12,25,29,32,33,50,53,56 for the "screen id", which is an identity
sequence only if the game has 56+ screens and never revisits one -- exactly what
a counter also looks like.
Withdrawn: 0x828A690C as a screen IDENTITY (1 title, 3 main menu, 4 extras). The
values are path-dependent; they matched across runs because the same key sequence
produces the same count, not because 3 means main menu.
Survives: all three advance if and only if the game responds, and are stable when
it does not. That is a real input-progress signal, reproducible across runs and
both GPU backends, and it is what made blind navigation work. Read it as "did
the game react?", never "which screen is this?".
It also retro-confirms the freeze diagnosis: with the sign-in fix the sequence
runs 3 -> 5, skipping 4 entirely, so "4 = extras" was never a screen -- it was the
count at which the game stopped responding. The counter reading explains both
observations.
Open: no mission reached. Counters at 56/41/1068, guest churn 0.190% (alive;
frozen is 0.000%), DEF_VTABLE and INST_VTABLE still 0. Without a real screen
identity, navigation is dead reckoning; finding a genuine state enum is next, and
the snapshot-and-diff method can be repeated with these counters excluded.
4.8 KiB
The menu's screen id and cursor, in guest memory
✅ Found and verified 2026-08-26. Menu navigation in this corpus has always
been driven by screenshots. That is unusable under --gpu=null, which is the
only configuration where the game does not hit the software-rasterizer freeze
(mission-freeze-heap-exhaustion.md) — so
without a memory signal, the one backend that runs is the one that cannot be
steered.
The words
| address | meaning |
|---|---|
0x828A690C |
screen id — 1 title, 3 main menu, 4 extras |
0x828F38AC |
menu cursor (a second copy at 0x828F38BC) |
0x828F37B4 |
tracks the screen, distinct value per menu |
How they were found
Snapshot 0x82800000 + 3 MB at each menu of a rendered run, then keep the
4-byte words that (a) differ between screens and (b) hold small integer values.
Of 786 432 words, exactly four qualified — and one of them,
0x828A690C, has the property that matters: it changes on a screen transition
and stays put when only the highlight moves.
screen: title -> main menu -> (4x down) -> extras
828a690c: 1 -> 3 -> 3 -> 4 <- screen id
828f38ac: 2 -> 4 -> 12 -> 14 <- cursor moves with the highlight
828f37b4: 12 -> 45 -> 45 -> 49
✅ Verified against a second run on a different GPU backend
The values were then read on a fresh --gpu=null run — no display at all —
while driving the same key sequence blind:
| step | rendered (lavapipe) | blind (--gpu=null) |
|---|---|---|
| title | 1 / 2 / 12 | 1 / 2 / 12 |
| main menu | 3 / 4 / 45 | 3 / 4 / 45 |
| after 4× down | 3 / 12 / 45 | 3 / 12 / 45 |
| extras | 4 / 14 / 49 | 4 / 14 / 49 |
4 / 4 exact, across two runs and two backends. That is the check that matters: the words are not run-dependent, unlike the OB counter address this corpus had to mark run-dependent earlier.
tools/re-capture/menu_state.py reads them (menu_state.py watch polls and
prints on change).
❔ Still open: the rest of the sequence into a mission. Blind driving reached
extras (screen 4) and a further Ⓐ did not move it — 4 / 14 / 49 twice — so
MISSION SELECT needs a cursor move first. The screen ids beyond 4 are unmapped;
the same snapshot-and-diff method extends to them, but it must be done on a
rendered run, and the rendered run freezes on entering that screen. The way
through is to map the ids up to the freeze and step blind past it.
🔴 Correction: these are counters, not a screen id and a cursor
2026-08-26, same day. The names above are wrong for at least two of the three words, and the test that shows it is going backwards.
Driving deep into the menus and then pressing Ⓑ (back) three times:
| before | after 3× Ⓑ | |
|---|---|---|
0x828F38AC ("cursor") |
36 | 38 → 40 → 41 |
0x828F37B4 ("misc") |
971 | 1067 → 1068 → 1068 |
0x828A690C ("screen id") |
56 | 56, 56, 56 |
A cursor returns when you go back. These only ever increase — they are
monotonic counters, and 0x828F38AC advances by ~2 per input. Driving
forward gave 1, 3, 4, 5, 6, 8, 10, 12, 25, 29, 32, 33, 50, 53, 56 for the
"screen id": that is a plausible identity sequence only if the game has 56+
screens and you never revisit one, which is precisely what a counter also looks
like.
🔴 Withdrawn: "0x828A690C = screen id (1 title, 3 main menu, 4 extras)" as
an identity. The values are path-dependent — they match across runs because the
same key sequence produces the same count, not because 3 means main menu.
0x828F38AC is not a cursor and 0x828F37B4 is not a per-menu value.
✅ What survives, and is still useful: all three advance if and only if the game responds, and they are stable when it does not. That is a genuine input-progress signal, reproducible across runs and both GPU backends, and it is what made blind navigation work. Read it as "did the game react?", never as "which screen is this?".
✅ And it retro-confirms the freeze diagnosis. With the sign-in fix the
sequence runs 3 → 5, skipping 4 entirely — so the old "4 = extras" was never
a screen at all: it was the count at which the game stopped responding, i.e. the
blocked state. Two readings of the same number, and the counter reading explains
both.
❔ Still open: a mission is not reached. After a dozen presses the counters are at
56 / 41 / 1068, guest churn is 0.190 % (alive — frozen is 0.000 %), and
DEF_VTABLE / INST_VTABLE scans are still 0 / 0. Without a real screen
identity, navigation is dead reckoning; finding an actual screen/state enum is
the next job, and the snapshot-and-diff method that found these counters can be
repeated with the counters themselves excluded.