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.
Menu navigation here has always been screenshot-driven, which is unusable under
--gpu=null -- the only configuration where the game does not hit the
software-rasterizer freeze. Without a memory signal, the one backend that runs
is the one that cannot be steered.
Snapshotting 0x82800000+3 MB at each menu of a rendered run and keeping the
4-byte words that differ between screens and hold small integers leaves exactly
four of 786 432. One has the property that matters -- it changes on a screen
transition and holds steady when only the highlight moves:
0x828A690C screen id 1 title, 3 main menu, 4 extras
0x828F38AC menu cursor (second copy at 0x828F38BC)
0x828F37B4 per-menu value
Verified on a fresh --gpu=null run with no display at all, driving the same keys
blind: title 1/2/12, main menu 3/4/45, after 4x down 3/12/45, extras 4/14/49 --
4/4 exact against the rendered run, across two runs and two GPU backends. That
is the check that matters, since this corpus has already had to mark one runtime
address run-dependent.
tools/re-capture/menu_state.py reads them; `menu_state.py watch` prints on
change.
Open: the rest of the sequence into a mission. Blind driving reached extras
(screen 4) and a further A did not move it, so MISSION SELECT needs a cursor
move first. Screen ids beyond 4 are unmapped, and the rendered run freezes on
entering that screen -- so map ids up to the freeze, then step blind past it.