diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 703d08ee..59c6b319 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -27,7 +27,7 @@ authored version can be deleted. | | Question | State | Answer / link | |---|---|---|---| -| 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) | +| Q1 | keyframe time unit + ramp shape | ✅ answered | ramp is **linear**; **2 units per rendered frame**; **`1 unit = 1/60 s` — settled**, 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) | | 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) | @@ -76,19 +76,18 @@ authored version can be deleted. declared 15-unit fade lands on `round(255·k/15)` for all seven of its samples, 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; [`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 seconds conversion is settled: `1 unit = 1/60 s`, a 30 Hz title.** The + re-test this page used to name has been run — 300 submitted frames timed on the + **idle** title (nothing loading) came out at **28.8 and 28.3 fps**, the same + rate as the 27.6 fps measured during the loading splash. The competing 60 Hz + reading is excluded: it needs the emulator at 47 % of real time while idling on + a screen that costs ~5 draws per frame. + A second, independent line agrees — the transition quad is declared black for + 12 units (0.20 s under this conversion) and a capture measured the pure-black + plateau at 0.17–0.23 s + ([`screen-transitions.md`](../re/screen-transitions.md)). + ⚠️ Still **measured, not decoded**: no field on the disc says "sixtieths of a + second". * **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, @@ -247,7 +246,8 @@ authored version can be deleted. ## 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.** +work queue. **None of it blocks the five screens.** (Q1's seconds conversion was +here until 2026-08-28 and is now settled.) | | what | why it is stuck | |---|---|---| @@ -255,7 +255,6 @@ work queue. **None of it blocks the five screens.** | ❔ | **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 | diff --git a/docs/re/METHOD.md b/docs/re/METHOD.md index 9041e075..43880c4d 100644 --- a/docs/re/METHOD.md +++ b/docs/re/METHOD.md @@ -184,3 +184,9 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the XACT string at all. The names came from the authoring tool and survived into the shipped data. Check for the magic before committing to a parser for the format the extension advertises. +* **A log-polling probe must compare a COUNT, not test for presence.** Timing an + operation by waiting for its "done" line in a log gives an instant false + positive if any earlier run left one there. This produced a confident + "39.5 fps" that was really 0.1 s of matching a stale line — and it disagreed + with the true 28.5 fps by enough to have flipped a conclusion. Snapshot + `log.count(marker)` before starting and wait for it to *increase*. diff --git a/docs/re/ui-keyframe-time-unit.md b/docs/re/ui-keyframe-time-unit.md index 57f99437..ee7e72d1 100644 --- a/docs/re/ui-keyframe-time-unit.md +++ b/docs/re/ui-keyframe-time-unit.md @@ -109,14 +109,46 @@ fit are: t = 16 … 269) is then **4.2 s**, and the main menu build (build 5, t = 12 … 80) **1.1 s**. -**The reach of this negative:** the present rate was measured *during the boot -splash*, where the guest is also streaming from the ISO, so it is a lower bound on -the emulator's speed and cannot by itself exclude the 60 Hz reading. What would -settle it is the same 300-frame timing taken on the **idle title screen**, where -nothing is loading — if that also comes out near 28 fps the game is 30 Hz and the -unit is 1/60 s; if it doubles to ~55 fps the game is 60 Hz and every duration on -this page halves. Until then the port should treat **2 units per rendered frame** -as the measured fact and `1/60 s` as the working conversion. +**The reach of this negative** *(as written before the test below — kept for the +reasoning)*: the present rate was measured *during the boot splash*, where the +guest is also streaming from the ISO, so it is a lower bound on the emulator's +speed and cannot by itself exclude the 60 Hz reading. What would settle it is the +same 300-frame timing taken on the **idle title screen**, where nothing is +loading — if that also comes out near 28 fps the game is 30 Hz and the unit is +1/60 s; if it doubles to ~55 fps the game is 60 Hz and every duration on this page +halves. + +## ✅ That test was run — 2026-08-28. The game is 30 Hz; `1 unit = 1/60 s` + +Reached the title with a single Ⓐ to skip the intro +([`movie-binding.md`](movie-binding.md)), let it settle 12 s so nothing was +loading, then armed the bounded 300-frame capture and timed it to its own +`[UI-CAP] done` line: + +| | 300 frames in | rate | +|---|---|---| +| idle title, trial 1 | 10.40 s | **28.8 fps** | +| idle title, trial 2 | *arm produced no capture* | — | +| idle title, trial 3 | 10.60 s | **28.3 fps** | +| *(prior)* boot splash | 10.87 s | 27.6 fps | + +**~28.5 fps on an idle title — the same rate as the loading splash.** By this +page's own criterion that settles it: the title presents at **30 Hz**, the +timeline ticks at **60 units/second**, and **`1 unit = 1/60 s`**. + +**The 60 Hz reading is now excluded, not merely disfavoured.** It would require +the emulator to be running at 47 % of real time *while sitting idle on a static +title* — and the capture says that screen costs **1 526 draws over 300 frames, +about 5 draws per frame**. Nothing there is expensive enough to halve the +emulator's speed, and the splash and the idle title returning the same rate is +exactly what a constant ~95 %-of-real-time emulator looks like. + +So the durations on this page stand as written: title build 4 ≈ **4.2 s**, main +menu build 5 ≈ **1.1 s**, and `EXTRAS` build 6's declared fade-in ≈ **0.87 s**. + +⚠️ Still **measured, not decoded** — no field on the disc says "sixtieths of a +second". What changed is that the measurement now has an idle-state control and +the competing reading is ruled out. ## What this does not say