This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/menu-navigation-semantics.md
Sylpheed RE agent 4e745c8177 re: the title menu wraps -- Q5 measured, Q4 driven, and one of my own
method lines withdrawn

Q5, measured off two boots: up/down move one item and WRAP at both ends
on the 5-item main menu and the 3-item EXTRAS alike; left/right do
nothing; B goes up one level and restores focus to the item you came
from (4/4); B on the main menu returns to the title; B on the title does
nothing. The menu opens on TUTORIAL -- the middle item -- 2/2, though a
third recorded run implies NEW GAME, so that one is reproducible rather
than invariant and says so.

Q4 by driving: LOAD GAME opens the save-slot list, TUTORIAL the lesson
list, OPTIONS the settings menu, EXTRAS build 6, MISSION SELECT the stage
list. NEW GAME is not tested -- A on it hangs the emulator and this
iteration needed the session. The GamePart ID behind each is NOT
measured: it is the entry of the decoded id table whose name matches the
screen I saw, and the page says so rather than wearing the badge.

And the withdrawal. Last iteration I wrote that these menus drop d-pad
presses shorter than ~0.3s. They do not. Once wrap is measured, every
press count I had is exactly right -- four presses moved four steps
THROUGH the bottom, which lands one above where a non-wrapping menu
would. I invented hardware flakiness instead of testing the ends of the
list. METHOD keeps the withdrawal rather than deleting the line.

Also: label brightness is not a cursor oracle here -- the background art
outshines the highlight on some rows. menu_focus.py reads the focus ring
instead, 254 vs <82, no tuning.
2026-08-28 17:53:46 +00:00

4.9 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 fromLOAD GAMELOAD GAME, TUTORIALTUTORIAL, OPTIONSOPTIONS, EXTRASEXTRAS 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 id1 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.