diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 3a1324a..cb00d1f 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -30,12 +30,12 @@ authored version can be deleted. | Q1 | keyframe time unit + ramp shape | ✅ answered | ramp is **linear**; the clock advances **2 units per rendered frame**; working conversion **1 unit = 1/60 s** — [`ui-keyframe-time-unit.md`](../re/ui-keyframe-time-unit.md) | | Q2 | which build is which screen state | ✅ answered | `GP_TITLE` is **8 screens shipped twice, EN/JP**: 4/7 title art, 2/3 the `PRESS Ⓐ` plate, 5/8 main menu, 6/9 `EXTRAS`, 0/1 and 10/11 two unidentified `DELTASABER` plates — [`ui-title-build-map.md`](../re/ui-title-build-map.md) | | Q3 | paint order for the six screens | ✅ answered | **decoded**: a `u16` layer key at `+0x0A` of each `T8aD` sprite header, stable-sorted with declaration index; unkeyed elements get an implied key. Confirmed on 5 measured orders + `EXTRAS` vs a capture. One residual: the **tie-break** is unknown and bites on one element of the title — [`structures/ui-paint-order-key.md`](../re/structures/ui-paint-order-key.md) | -| Q4 | button → GamePart | 🟡 mostly answered | **measured** which screen each button opens (4 of 5; `NEW GAME` untested — it hangs). The **GamePart id is a name match** onto the decoded id table, not a measurement — [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md) | +| Q4 | button → GamePart | ✅ answered | **measured** which screen all **5** buttons open — `NEW GAME` → `DIFFICULTY` → `SELECT DATA`, not a hang. The **GamePart id is still a name match**, not a measurement — [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md) | | Q5 | navigation semantics | ✅ answered | **measured**: initial focus varies boot to boot (2× `TUTORIAL`, 1× `NEW GAME`); ⬆⬇ one step, **wraps both ends**; ⬅➡ do nothing; Ⓑ returns to the parent **with focus restored**; Ⓑ on the main menu → title; Ⓑ on the title → nothing — [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md) | | Q6 | boot sequence + what drives it | 🟡 partial | order observed and the attract cycle timed: ~8–10 s idle → fade to black → **`ADV.wmv` in full (137 s)** → title again. The **movie manifest** supplies the boot-side asset order (slot key = role); the code that decides to advance is still not decoded | | Q7 | transitions | ✅ answered | a **fade through black**, drawn by the screen's own last-painting `.prm` quad. Fade-in ramp is **decoded** from its keyframes; the ~0.4 s fade-out is **measured** (not in the file) — [`screen-transitions.md`](../re/screen-transitions.md) | | Q8 | menu audio bindings | 🟡 mostly answered | the **UI cue vocabulary is decoded** (`SE_UI_CURSOR`/`DECIDE`/`CANSEL`/`IMPOSI`, all in `Static.slb` per `BANK_SE`); the event binding is a **name match**, and the SE audio is **not extractable yet** — [`menu-audio-cues.md`](../re/menu-audio-cues.md) | -| Q9 | video binding + playback rules | ✅ answered | **decoded** from the movie manifest: `ADVERTISE_MOVIE`→`ADV.wmv` (boot intro *and* attract are one asset), `MS00A`→`S00A.wmv` is the new-game intro, `STAFF_ROLL`→the credits reel. 🟡 skippability unsettled — [`movie-binding.md`](../re/movie-binding.md) | +| Q9 | video binding + playback rules | ✅ answered | **decoded** from the movie manifest: `ADVERTISE_MOVIE`→`ADV.wmv` (boot intro *and* attract are one asset), `MS00A`→`S00A.wmv` is the new-game intro, `STAFF_ROLL`→the credits reel. ✅ **one Ⓐ skips a movie** (title at 57 s vs a 193 s baseline) — [`movie-binding.md`](../re/movie-binding.md) | | Q10 | music-bank sub-wave roles (intro+loop?) | ✅ answered | **two stems of one performance, played together** — sample-synchronous, equal duration, 32/32 banks. **Concatenating is wrong.** Not a seamless loop either — [`structures/bgm-two-stems.md`](../re/structures/bgm-two-stems.md) | | S1 | Ready Room go/no-go | ✅ **no-go** | it is 2D and enumerates fine (60 builds), but `GP_READY_ROOM.pak` holds **briefing/tactical-map** content, not the six-button Ready Room menu — [`ready-room-probe.md`](../re/ready-room-probe.md) | @@ -104,12 +104,15 @@ authored version can be deleted. and **wrap at both ends** (5-item main menu and 3-item `EXTRAS` both). ⬅➡ do nothing. Ⓑ goes up one level **and restores focus to the item you came from**; Ⓑ on the main menu returns to the title; Ⓑ on the title does nothing. **Initial - focus is not stable**: three boots of the same script gave `TUTORIAL`, - `TUTORIAL`, `NEW GAME`. Do not hardcode it; pick one and say you picked it. All **measured**, none of it on the disc. + focus is not stable**: four boots of the same script gave `TUTORIAL`, + `TUTORIAL`, `NEW GAME`, `NEW GAME`. Do not hardcode it; pick one and say you picked it. All **measured**, none of it on the disc. * **Each button's destination is measured; its GamePart id is not.** + **`NEW GAME` → `DIFFICULTY`** (`EASY`/`NORMAL`/`HARD`/`BACK`, opening on + `NORMAL`) **→ `SELECT DATA`** — it does *not* hang; the run then hits the + already-documented `sub_823070B0` cache crash, which is not a menu problem. `LOAD GAME` → the save-slot list, `TUTORIAL` → the lesson list, `OPTIONS` → the settings menu, `EXTRAS` → `GP_TITLE` build 6, `EXTRAS ▸ MISSION SELECT` → - the stage list. ❔ `NEW GAME` is untested — Ⓐ on it hangs the emulator. The + the stage list. The GamePart ids (`3`, `25`, `8`, `5`, `7`) are the entries of the decoded id table whose **names match the screens seen**; that binding is authored, not measured. @@ -135,9 +138,12 @@ authored version can be deleted. separate boot slot, and 15 of 19 captured attract frames match `ADV.wmv` with a monotonically advancing playhead ending at its full 137 s. One video, not two. ✅ The attract movie **plays to its end**; nothing cuts it short. - 🟡 **Whether a movie is skippable is unsettled** — the corpus says Ⓐ skips one - every time, while the boot harness deliberately never taps during a movie - because it breaks the title. Do not rely on either until it is retested. + ✅ **A movie is skippable with a single Ⓐ.** Measured: one tap ~45 s into the + boot brought the title at ~57 s against a ~193 s no-input baseline over three + boots, with Canary's own keystroke counter proving exactly one press was + delivered — and the skipped-to title is **fully functional** (`PRESS Ⓐ` plate + present, Ⓐ opens the main menu). What breaks the boot is *hammering*: 88 + presses left a permanent black screen. One press is fine. * **The menu's sound events are named on the disc.** `tables.pak`'s `SOUNDS` record carries 322 `SE_*` cues, and the low block is the UI vocabulary — named diff --git a/docs/re/METHOD.md b/docs/re/METHOD.md index c2df6e1..77e0f8b 100644 --- a/docs/re/METHOD.md +++ b/docs/re/METHOD.md @@ -169,3 +169,12 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the drive every correlation toward zero and the ranking becomes noise; one control frame scored 0.000 and another tied the wrong movie at 0.98. Read a dark frame's runner-up as *no evidence*, not as evidence for the runner-up. +* **A screen that never changes is not necessarily hung — it may be a menu.** A + 90-second screenshot loop that presses nothing will report a perfectly healthy + `DIFFICULTY` menu as a standing hang, and that reading survived in this corpus + for months. Before calling a static screen a hang, *look at it*, and press + something. +* **Never run `ps -ef` in this container.** Each of the three long-lived + processes carries the whole loop prompt as its argv, so the listing is tens of + kilobytes of the prompt you already have. Use `pgrep -x ` or + `ps -o pid=,stat= -C `. diff --git a/docs/re/REFUTED.md b/docs/re/REFUTED.md index efe2bad..51aae07 100644 --- a/docs/re/REFUTED.md +++ b/docs/re/REFUTED.md @@ -35,6 +35,14 @@ neighbourhood, not just the line. ## Screens, classes and RTTI +* "Ⓐ on `NEW GAME` leads to a standing black-screen hang" → it opens + **`DIFFICULTY`**, then **`SELECT DATA`**. What looked like a hang was a menu + waiting for input that nobody pressed; the crash that follows is the already + documented `sub_823070B0` cache throw. + [`menu-navigation-semantics.md`](menu-navigation-semantics.md) +* "tapping Ⓐ during the boot movie breaks the title" → **one** tap skips the + movie cleanly and the title works normally. It is *hammering* (88 presses) that + breaks it. [`movie-binding.md`](movie-binding.md) * "the attract movie runs ~85 s, so it is not `ADV.wmv` (137 s)" → **mine, and wrong.** Sampling began 39 s into the movie, so what was timed was its tail. The attract movie **is** `ADV.wmv`, played in full. diff --git a/docs/re/captures/newgame-path/newgame-difficulty.png b/docs/re/captures/newgame-path/newgame-difficulty.png new file mode 100644 index 0000000..f92d5e5 Binary files /dev/null and b/docs/re/captures/newgame-path/newgame-difficulty.png differ diff --git a/docs/re/captures/newgame-path/newgame-selectdata-crash.png b/docs/re/captures/newgame-path/newgame-selectdata-crash.png new file mode 100644 index 0000000..d7b6714 Binary files /dev/null and b/docs/re/captures/newgame-path/newgame-selectdata-crash.png differ diff --git a/docs/re/captures/newgame-path/title-after-single-A-skip.png b/docs/re/captures/newgame-path/title-after-single-A-skip.png new file mode 100644 index 0000000..2199efb Binary files /dev/null and b/docs/re/captures/newgame-path/title-after-single-A-skip.png differ diff --git a/docs/re/menu-navigation-semantics.md b/docs/re/menu-navigation-semantics.md index 5874e2f..7657d7d 100644 --- a/docs/re/menu-navigation-semantics.md +++ b/docs/re/menu-navigation-semantics.md @@ -75,3 +75,45 @@ transition, and works under `--gpu=null` with no screenshots at all. ⚠️ Note 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 on **`NORMAL`** + ([capture](captures/newgame-path/newgame-difficulty.png)). 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 headed + `Current Storage: Dummy HDD`, prompting to pick a file for the auto-save. +* Then the guest throws, and Xenia pauses with `PC: 0x82307128` + ([capture](captures/newgame-path/newgame-selectdata-crash.png)). + +**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`](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.** diff --git a/docs/re/movie-binding.md b/docs/re/movie-binding.md index 9710b6d..7323277 100644 --- a/docs/re/movie-binding.md +++ b/docs/re/movie-binding.md @@ -97,3 +97,31 @@ are in play order and the key *is* the role. That is not the whole driver — it says what plays, not what decides to advance — but it is the first file-side piece of Q6's second half, and it means the boot-side asset order does not have to be authored. + +## ✅ Skippable — settled 2026-08-28: **one Ⓐ skips the movie** + +The test named above was run, and the answer is unambiguous. + +| | | +|---|---| +| baseline: title arrives with **no** input | ~193 s, ~196 s, ~193 s (three boots) | +| **one Ⓐ tapped at ~45 s into the boot** | **title at ~57 s** | + +The press is provably the cause and provably singular: Canary's own +`[RE-INPUT] XamInputGetKeystrokeEx reached the driver` counter went **3 → 4** +across the tap, so exactly one keystroke was delivered, and the title arrived +~12 s later instead of ~150 s later. + +**And the skipped-to title is fully functional**, which is the part that matters +for the harness folklore: it draws the `PRESS Ⓐ BUTTON` plate (2 408 plate pixels +by the same probe used elsewhere), and a second Ⓐ opened the main menu normally +([`title-after-single-A-skip.png`](captures/newgame-path/title-after-single-A-skip.png)). + +### So what is `skip_intro.sh` protecting against? + +Not a single tap. The failure recorded in +[`canary-scripted-input-traps.md`](canary-scripted-input-traps.md) is run G — +**88 presses at 4 s intervals through the whole boot** — which left a permanent +black screen. Hammering breaks it; one press does exactly what the button is for. +The scripts' `waiting it out (tapping breaks the title)` comment is **too broad**, +and it costs every scripted boot ~2.5 minutes.