From 2c4005795bd61dc9d6affa78952c5729f9fff95d Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Fri, 28 Aug 2026 19:02:49 +0000 Subject: [PATCH] port: audit the handoff -- four stale statements, a broken link, and the residue in one place Every MISSION row is answered, so this iteration checked the deliverable itself rather than opening new scope: an answer that is not reachable from HANDOFF.md has not been delivered, and I have been editing that page by surgical replacement for a dozen iterations. Four things were wrong or stale: - the Q5 row still said initial focus was 2x TUTORIAL, 1x NEW GAME while its own bullet said four boots -- I updated the body last time and not the row; - the Q2 bullet still described the other four main-menu destinations as "likely by name -- an inference, not a measurement", which Q4 has since MEASURED, and it listed GP_MISSION_SELECT as a main-menu destination when MISSION SELECT lives under EXTRAS; - the Q1 bullet claimed that if the game presents at 60 Hz "nothing else on this page changes". That is now false: Q7's declared fade-in lengths are keyframe-derived and would halve. The same bullet also missed that the transition capture is a SECOND independent measurement supporting 1 unit = 1/60 s -- the quad is declared black for 12 units, 0.20 s under the conversion, and the capture measured the plateau at 0.17-0.23 s. Recorded as corroboration, not proof, because the plateau spans two screens' fades and cannot separate them; - bgm-two-stems.md linked ../port/MISSION.md from inside structures/, which resolves nowhere. Also named the two videos actually in scope, since "3.3 GB of video, two files are in scope" is not much use without the filenames. And added a What Is Still Open section. With every row green the residue was scattered across eight pages; it is now one table with why each item is stuck. Two entries share a single unblocker -- an emulator whose audio path can be observed -- and they are the only ones that would change a port decision. --- docs/port/HANDOFF.md | 51 ++++++++++++++++++++++------- docs/re/structures/bgm-two-stems.md | 2 +- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 73f6e90..703d08e 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -31,7 +31,7 @@ authored version can be deleted. | 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 | ✅ 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) | +| 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) | | Q8 | menu audio bindings | ✅ answered | cue vocabulary + bank **decoded**; event binding is a **name match** (the authors' own event names); SE audio is **undecodable from the disc** — no XACT container exists anywhere — [`menu-audio-cues.md`](../re/menu-audio-cues.md) | @@ -51,9 +51,8 @@ authored version can be deleted. captures for the four English screens the boot path shows; [`ui-title-build-map.md`](../re/ui-title-build-map.md). **Withdrawn:** the earlier "builds 6/8/9 are submenus" — 8 is the Japanese main - menu. The other four main-menu buttons leave the archive (`GP_SAVE_LOAD`, - `GP_TUTORIAL`, `GP_OPTIONS`, `GP_MISSION_SELECT` are the likely destinations by - name — an inference, not a measurement). + menu. The other four main-menu buttons leave the archive, and where each one + goes is **measured** — see the button-destination bullet below. * **Buttons are identifiable as data.** Element kind `0x3002` = button, `0x0` = decoration, `0x10` = primitive. ✅ decoded **for the title-side screens**. ⚠️ `0x3002` is one member of a `0x3000` family with sub-bits, and it is not the @@ -78,10 +77,18 @@ authored version can be deleted. with `k` stepping 2, 4, 6, 8, 10, 12, 14 on seven consecutive submitted frames. **measured**, not decoded — the disc says `t=30`, it does not say what a `t` is. The seconds conversion (`1 unit = 1/60 s`, so a 30 fps screen) rests on a - measured 27.6 present-frames/second and is the one part still worth re-testing; - [`ui-keyframe-time-unit.md`](../re/ui-keyframe-time-unit.md) names the test. - If it turns out the game presents at 60 Hz, every duration halves — nothing - else on this page changes. + measured 27.6 present-frames/second; [`ui-keyframe-time-unit.md`](../re/ui-keyframe-time-unit.md) + names the re-test. 🟡 **A second, independent measurement now supports it**: the + transition quad is declared black from `t=0` to `t=12` — 12 units, i.e. 0.20 s + under this conversion — and a 30 fps capture measured the pure-black plateau at + **0.17–0.23 s** ([`screen-transitions.md`](../re/screen-transitions.md)). That + is corroboration and not proof: the plateau spans the outgoing screen's + fade-out tail as well as the incoming screen's black, and this measurement + cannot separate them. + ⚠️ If the game turns out to present at 60 Hz, **every duration on this page + derived from keyframe units halves** — including Q7's declared fade-in lengths + (0.87 s / 0.97 s / 4.08 s). The wall-clock numbers measured off a capture (the + ~0.4 s fade-out, the black hold, the attract timings) do not move. * **The paint order is derivable from the file.** Each `T8aD` sprite header carries a **`u16` layer key at `+0x0A`** (the upper half of the 32-bit word at `+0x08` is zero in all 21 184 sprites on the disc). Paint order is that key, @@ -105,7 +112,8 @@ authored version can be deleted. 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**: 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. + `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 @@ -208,8 +216,10 @@ authored version can be deleted. * **`ADV.wmv` is WMV3 video + WMA Pro audio**, 1280×720 at 30 fps, 137 s. Godot 4 plays only Ogg Theora natively. How to handle that is the port's decision, not ours — but it is not optional. -* **The disc holds 3.3 GB of video.** Only the boot intro and the one new-game - intro are in scope. +* **The disc holds 3.3 GB of video, and exactly two files are in scope:** + `dat/movie/ADV.wmv` (the boot intro *and* the attract loop — one asset) and + `dat/movie/S00A.wmv` (the new-game intro, 93.9 s). Named from the movie + manifest, ✅ decoded. * **`Static.slb` over-declares its size** by 616 768 bytes — it is the highest-offset entry in `sound.pak` and its size field is an allocation size. A reader must allow a short read there and only there. @@ -234,6 +244,25 @@ authored version can be deleted. * **`JNGL_001.slb` does not decode.** One bank in 9 519; its payload is not a whole number of XMA1 packets from any known data offset. +## What is still open + +Every question above is answered, so this is the honest residue rather than a +work queue. **None of it blocks the five screens.** + +| | what | why it is stuck | +|---|---|---| +| 🟡 | **cue → event binding as a measurement** (Q8) | nobody has watched the game emit `SE_UI_CURSOR` on a d-pad press. Needs the guest's cue call observed with its argument | +| ❔ | **the wave index into `Static.slb`** (Q8) | same instrumentation. This container runs `--mute=true` against an SDL dummy device, so it cannot watch the audio path at all | +| 🟡 | **the paint-order tie-break** (Q3) | eight candidates refuted; costs one element's blend on one screen | +| 🟡 | **GamePart ids behind the buttons** (Q4) | the *screens* are measured; the ids are a name match onto the executable's class names | +| 🟡 | **`1 unit = 1/60 s`** (Q1) | two independent measurements agree, neither is proof; if it is 60 Hz every keyframe-derived duration halves | +| ❔ | **the boot transitions in code** (Q6) | bounded as code-not-data, not proven. `sub_821C6458` — the substantial function in `GamePart_Title`'s neighbourhood — has **not been read** | +| ❔ | **`MS00A` → `S00A.wmv` never watched** (Q9) | decoded from the manifest; the one run that reached it crashed at `SELECT DATA` first | +| ❔ | **builds 0/1 and 10/11**, the `DELTASABER` plates (Q2) | never seen on screen anywhere in the boot path, the title-side screens or the attract loop | + +The first two share one unblocker — an emulator build whose audio path can be +observed — and would be the only entries here that change a port decision. + ## Reference data Committed alongside the findings, so the port can be built without a disc in the diff --git a/docs/re/structures/bgm-two-stems.md b/docs/re/structures/bgm-two-stems.md index e503543..2a384e1 100644 --- a/docs/re/structures/bgm-two-stems.md +++ b/docs/re/structures/bgm-two-stems.md @@ -5,7 +5,7 @@ over all 32 BGM banks. 🟡 for the *role* of the second wave — **measured** b signal analysis, and the two readings that survive are named below. ❔ which bank is the menu's music is **undecodable from the disc**, with the reach stated. -Answers [MISSION Q10](../port/MISSION.md). It also **withdraws the premise**: the +Answers [MISSION Q10](../../port/MISSION.md). It also **withdraws the premise**: the handoff said `BGM_001.slb` is *three* sub-waves of 10 KB / 4.47 MB / 4.67 MB. The 10 KB is the bank **header**, not a wave.