Two corpus claims died in one boot, and both were costing us. Q9's last row: a movie IS skippable with a single A. One tap ~45s into the boot put the title up at ~57s, against a ~193s no-input baseline measured over three boots, and Canary's own keystroke counter went 3->4 so exactly one press was delivered. The skipped-to title is fully functional -- it draws the PRESS A plate and a second A opens the main menu. What actually breaks the boot is hammering: the 88-press run in the traps doc. The scripts' "tapping breaks the title" comment is too broad and costs every scripted boot two and a half minutes. Q4's last row: A on NEW GAME does not hang. It opens DIFFICULTY (EASY/NORMAL/HARD/BACK, focus on NORMAL), then SELECT DATA, and only then does the guest throw -- at PC 0x82307128, which is inside sub_823070B0, the cache-manager STL erase this corpus already documents and which has nothing to do with the menu path. The screen sat unchanged for 90s because it was a menu waiting for input from a loop that never pressed anything. That is now a METHOD line: a screen that never changes is not necessarily hung, and the fix is to look at it and press something. Also METHOD: never run ps -ef in this container -- all three long-lived processes carry the entire loop prompt as argv.
6.7 KiB
The title menu — how it moves, and where each button goes
Status: ✅ CONFIRMED (measured, by driving the running game) for the
movement rules and for four of the five main-menu destinations. 🟡 the GamePart
id behind each destination is a name match onto the decoded id table, not a
measurement. ❔ NEW GAME deliberately untested.
Answers MISSION Q5 and most of Q4. Nothing here is on the disc in any form found so far — the port is authoring these rules from this page, not transcribing a field.
Q5 — movement
Two boots via tools/re-capture/boot_menu.sh, cursor read off the focus ring
with tools/re-capture/menu_focus.py.
| behaviour | evidence | |
|---|---|---|
| initial focus, main menu | TUTORIAL — the middle item, not the top |
2/2 boots, the first frame after the menu appears |
initial focus, EXTRAS |
MISSION SELECT — the top item |
extras-wrap.png |
| up / down | one item per press, no auto-repeat at the durations tried | |
| wrap at the top | ⬆ from the first item goes to the last | wrap-montage.png, panels 1→2 |
| wrap at the bottom | ⬇ from the last item goes to the first | same, panels 3→4, and 4 presses from EXTRAS landing on OPTIONS — i.e. wrapping — is what makes the count come out |
| left / right | nothing, on the main menu | cursor unmoved across one ⬅ and one ➡ |
| Ⓑ on a submenu | returns to the parent with focus restored to the item you entered from — LOAD GAME→LOAD GAME, TUTORIAL→TUTORIAL, OPTIONS→OPTIONS, EXTRAS→EXTRAS |
4/4 |
| Ⓑ on the main menu | goes to the title, which re-draws PRESS Ⓐ BUTTON after a beat |
|
| Ⓑ on the title | nothing |
Wrap holds on both screens tested — the 5-item main menu and the 3-item EXTRAS
submenu — so it is a menu rule, not a per-screen table.
🟡 Initial focus is reproducible but not established as invariant. Both of my
boots opened on TUTORIAL, and both used boot_menu.sh. The run recorded in
menu-state-in-memory.md reached EXTRAS with four
downs from the main menu, which only works from NEW GAME. Either the harness
path matters or something persists. Do not hardcode TUTORIAL without
re-testing it; what is solid is that the menu does not open on the top item
in this harness.
Q4 — where each button goes
Measured by driving: focus the item, press Ⓐ, read the screen's own title.
| button | screen it opens | evidence | GamePart id |
|---|---|---|---|
NEW GAME |
❔ not tested | — | — |
LOAD GAME |
the save-slot list, LOAD GAME / Current Storage |
q4-destinations.png left |
🟡 3 GP_LOAD |
TUTORIAL |
the lesson list, TUTORIAL, Level 1 / Level 2 |
same, middle | 🟡 25 GP_TUTORIAL |
OPTIONS |
OPTIONS — GAME / CONTROL / SOUND / SCREEN SETTINGS / BACK |
same, right | 🟡 8 GP_OPTIONS |
EXTRAS |
GP_TITLE.pak build 6 — MISSION SELECT / MOVIE THEATER / BACK |
ui-title-build-map.md |
🟡 5 GP_EXTRAS |
EXTRAS ▸ MISSION SELECT |
the stage list + Wide Area Space Map | 🟡 7 GP_MISSION_SELECT |
|
EXTRAS ▸ MOVIE THEATER |
❔ not tested | 🟡 6 GP_MOVIE_THEATER |
Say which, as the gate asks. The screen each button opens is measured —
I pressed the button and read the title off the framebuffer. The GamePart id is
not measured: it is the entry of the decoded 29-id table at .rdata 0x820A1630
(challenge-mission-gate.md §3) whose name matches
the screen I saw. The table is decoded; the binding of a button to an entry in
it is a name match I made by eye. The port should treat these ids as authored.
Worth noting that the ids and the paks are not one-to-one: GP_EXTRAS is id 5
with no pak of its own — its artwork is a build inside GP_TITLE.pak.
❔ NEW GAME was deliberately not pressed. Ⓐ on it leads to a standing
black-screen hang that ends the run
(ui-paint-order-third-permutation.md),
and this iteration needed the session. It is the one destination still unmeasured.
The cheap way to finish this, and to make it a measurement
0x828A690C holds a live screen id — 1 title, 3 main menu, 4 extras —
and 0x828F38AC the cursor (menu-state-in-memory.md).
Reading those while pressing Ⓐ turns "the screen said OPTIONS" into a measured
transition, and works under --gpu=null with no screenshots at all. ⚠️ Note that
those values are not GamePart ids — GP_TITLE is GamePart 0 and GP_EXTRAS
is 5, but the word reads 1 and 4 — so it is a third enumeration and mapping it to
the id table is itself unfinished work.
✅ NEW GAME — measured 2026-08-28, and it is not a hang
The one destination this page could not test has been driven. Ⓐ on NEW GAME
does not hang. It opens two more menus first:
NEW GAME -> DIFFICULTY -> SELECT DATA -> guest crash
DIFFICULTY—EASY/NORMAL/HARD/BACK, footerⒶ : OK Ⓑ : Back, opening focused onNORMAL(capture). It sat unchanged for 90 s with the emulator healthy — a menu waiting for input, which is exactly what "a standing hang" looks like to a screenshot loop that never presses anything.- Ⓐ on
NORMAL→SELECT DATA, a save-slot picker headedCurrent Storage: Dummy HDD, prompting to pick a file for the auto-save. - Then the guest throws, and Xenia pauses with
PC: 0x82307128(capture).
That crash is already in the corpus and is not new: 0x82307128 is inside
sub_823070B0, the cache-manager STL erase documented in
title-crash-stl-tree.md, whose trigger is an
incomplete on-disc shader/code cache — not the menu path. The corpus also already
holds captures/select-data-crash.png.
So Q4's last row closes as measured:
| button | screen it opens |
|---|---|
NEW GAME |
DIFFICULTY (then SELECT DATA) |
🟡 The GamePart ids for these two are unmeasured, like the rest — 24 GP_DIALOG
and 3 GP_LOAD / 2 GP_SELECT_STORAGE are name-match candidates and nothing
more.
Initial focus — a fourth data point, and it still varies
This boot opened the main menu on NEW GAME. Running tally across four
boots of the same harness: TUTORIAL, TUTORIAL, NEW GAME, NEW GAME.
Unchanged conclusion: do not hardcode it.