From 3e7a258a9aa7ae59af487e4296380f28786bc742 Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Sat, 29 Aug 2026 14:51:15 +0000 Subject: [PATCH] re: a cross-reference kept recommending a route the cited page had killed `menu-navigation-semantics.md` advertised "the cheap way to finish this": read 0x828A690C as a live screen id and 0x828F38AC as the cursor, under --gpu=null with no screenshots. `menu-state-in-memory.md` withdrew exactly that identity ON THE SAME DAY it was published -- three back presses send the "cursor" 36 -> 38 -> 40 -> 41, and a cursor returns when you go back. They are monotonic counters; the cross-run agreement is the same key sequence producing the same count. The recommendation stood for three days after the page it cited had killed it, and either document would have been believed on its own. Marked withdrawn where it was recommended, with what the words ARE still good for (did the game react?) and the consequence: a measured button->GamePart-id binding stays unfinished because no screen enum has been located. Also delivers Q4 to HANDOFF in the shape the port asked for -- exactly one main-menu button opens a GP_TITLE entry (EXTRAS -> entry 6/9); the other four leave the archive. That was measured on 2026-08-28 and was reachable only from docs/re/, which the protocol counts as undelivered. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd --- docs/port/HANDOFF.md | 2 +- docs/re/menu-navigation-semantics.md | 34 +++++++++++++++++++++------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 161b4867..7c399e6d 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -348,7 +348,7 @@ tool `cargo run -p sylpheed-formats --example tie_break_pixel_cost -- dat/GP_TIT | Q1 | keyframe time unit + ramp shape | ✅ answered | ramp is **linear**; **2 units per rendered frame**; **`1 unit = 1/60 s` — measured**, the idle title presents at 28.5 fps so the game is 30 Hz — [`ui-keyframe-time-unit.md`](../re/ui-keyframe-time-unit.md). ✅ **The group timeline is now DECODED too (2026-08-29) and the gap is closed**: a placement group is `frames` records of `{u32 time; 36-byte pose}` after an 8-byte header, so a pose's time is the word **before** it, pose 0's time is the group's lead-in word, and **every** pose is timed — including the last, which nothing could time before. Disc-wide over 13 991 groups with controls; the old reading makes **0 of 1 042** multi-segment alpha ramps constant-rate against 857 of 1 540. `SYLPHEED_KF_TIME_SHIFT` is retired (it had the association right but left pose 0 untimed, which is the whole reason it appeared to cost 13.1 % of build 7). Static renders are byte-identical — [`ui-keyframe-record-layout.md`](../re/ui-keyframe-record-layout.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`, and ✅ **pak entries 0/1 and 12/15 are the LOADING screen** — two variants, plain and dressed, decoded from their `pgloading_*` element names (2026-08-29). ⚠️ **Read that in ENTRY space.** This row said "0/1 and 10/11" until 2026-08-29; that is true only of `screen list`'s ordinals, where 10→entry 12 and 11→entry 15. In entry space 10/11 are the **publisher and developer splashes** (`palogo_sqex`, `palogo_gamearts`). The port caught it; see [METHOD](../re/METHOD.md#mechanics-that-have-bitten). 🟡 which of the two is `LOADING` vs `LOADING2` is undecided; 🟡 the English member of a pair is the one in the first half of the data segment — [`ui-title-build-map.md`](../re/ui-title-build-map.md) | | Q3 | paint order for the six screens | ✅ answered, ❔ tie-break | **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). ⚠️ **The key does not fully order a screen**: elements sharing a key are tied, and the tie-break is ❔ **undecodable from the bundle** — declaration table, `T8aD` header (exhaustive: every offset 0x00–0x7f at u8/u16/u32, both directions, **0** fields match the measured order against **64** for the control) and the RATC child order all give the same order the game does *not* use. ✅ **Your exposure is now measured at ZERO PIXELS (2026-08-29).** The 2 overlapping tied pairs on `EXTRAS` are `ptframe3`×`ptframe4` (the other is a `loop*` you never draw), and rendering the screen with that pair swapped changes **0 px** — because the two sprites put ink on ~3 600 pixels each and **share none of them**; the 102×132 "overlap" was a bounding-box artefact. Same on the main menu's `ptframe1`×`ptframe2`. This is blend-independent: layers that never touch the same pixel cannot be ordered wrongly. **Nothing about the tie-break can change a pixel on any of your five screens** — [`structures/ui-paint-order-derived-check.md`](../re/structures/ui-paint-order-derived-check.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) | +| Q4 | button → GamePart | ✅ answered | **measured** which screen all **5** buttons open, by pressing each one and reading the screen's own title off the framebuffer. ✅ **In the form you need it: exactly ONE main-menu button opens a `GP_TITLE` entry.** `EXTRAS` → **entry 6** (EN) / **9** (JP). The other four leave the archive: `NEW GAME` → `DIFFICULTY` → `SELECT DATA`; `LOAD GAME` → the save-slot list; `TUTORIAL` → the lesson list; `OPTIONS` → GAME/CONTROL/SOUND/SCREEN SETTINGS. None of those four is a `GP_TITLE` build — so a menu→submenu→back cycle inside this archive is `main menu ↔ EXTRAS` and nothing else. The **GamePart id is still a name match**, not a measurement, and 🔴 the "cheap way to measure it" this page used to point at is a dead route (the guest words are monotonic counters, not a screen id) — [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md) | | Q5 | navigation semantics | ✅ answered | **measured**: initial focus varies boot to boot (2× `TUTORIAL`, 2× `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 | ✅ answered | sequence **measured** end to end; the driver is **code, not data** — four search spaces closed, so the port **authors** the sequence — [`boot-config-and-gamepart-registry.md`](../re/boot-config-and-gamepart-registry.md) | | 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) | diff --git a/docs/re/menu-navigation-semantics.md b/docs/re/menu-navigation-semantics.md index 7b3b7046..68babcbc 100644 --- a/docs/re/menu-navigation-semantics.md +++ b/docs/re/menu-navigation-semantics.md @@ -66,15 +66,33 @@ 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 +### 🔴 The "cheap way to finish this" was a dead route — WITHDRAWN 2026-08-29 -`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. +This section used to say: *`0x828A690C` holds a live screen id (1 title, 3 main +menu, 4 extras) and `0x828F38AC` the cursor; read them while pressing Ⓐ and the +transition becomes a measurement, under `--gpu=null` with no screenshots.* + +**Do not do that. Those words are monotonic counters, not state.** +[`menu-state-in-memory.md`](menu-state-in-memory.md) withdrew that identity **on +the same day it was published** — driving deep and then pressing Ⓑ three times +sends the "cursor" `36 → 38 → 40 → 41`, and a cursor returns when you go back. +The values match across runs because the same key sequence produces the same +count, not because `3` *means* main menu. With the sign-in fix the sequence runs +`3 → 5`, skipping `4` entirely, so "4 = extras" was never a screen at all. + +This page kept recommending the route for three days after the page it cited had +killed it. ⚠️ **A cross-reference is not a citation unless you re-read the target** +— the two documents disagreed in the corpus and either would have been believed +on its own. + +✅ **What those words are still good for**: all three advance if and only if the +game responds to input, and are stable when it does not. Read them as *"did the +game react?"*, never as *"which screen is this?"*. Screen identity still has to +come off the framebuffer. + +❔ **So a measured button→GamePart-id binding remains unfinished**, and for the +reason it always was: no screen/state enum has been located in guest memory. The +ids in the table below stay a name match. ## ✅ `NEW GAME` — measured 2026-08-28, and it is not a hang