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.
78 lines
4.9 KiB
Markdown
78 lines
4.9 KiB
Markdown
# 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](../port/MISSION.md) 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`](../../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`](captures/menu-nav/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`](captures/menu-nav/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`](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`](captures/menu-nav/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`](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`](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`](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`](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.
|