diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 460fb55e..4c2accc4 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -300,8 +300,8 @@ items MISSION parks as emulator-blocked. | | Question | State | Answer / link | |---|---|---|---| -| Q1 | keyframe time unit + ramp shape | βœ… answered, 🟑 one gap | 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. 🟑 **The interpolation law is settled; the group TIMELINE for multi-keyframe elements is not** β€” `palogo_gamearts` is still at full alpha 9 frames after its declared `a=32`, and its declared 80-frame fade-in never draws β€” [`ui-keyframe-time-unit.md`](../re/ui-keyframe-time-unit.md). βœ… **REPLICATED 2026-08-29 β€” for ANIMATION, read `+36` as the time the NEXT pose is reached.** Three elements across two screens: `palogo_gamearts` and `palogo_seta` hold full alpha for **83 frames** and `palogo_sqex` for **β‰₯77**, where the current reading predicts **6–8** and the shifted one **80–102**. The elements that cannot discriminate (the `_eff` glows, on which the linear law was measured) fit both. ⚠️ Our decoder still defaults to the other reading (`SYLPHEED_KF_TIME_SHIFT=1` to flip) because it changes `rest()` on one element β€” but that is an unsound fallback guessing either way, so **static rendering is unaffected and animation timing should use the shift** | -| 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) | +| 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 βœ… **0/1 and 10/11 are the LOADING screen** β€” two variants, plain and dressed, decoded from their `pgloading_*` element names (2026-08-29). 🟑 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 **2 overlapping tied pairs on `EXTRAS`** β€” [`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) | | 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) | @@ -1174,7 +1174,7 @@ here until 2026-08-28 and is now settled.) | 🟑 | **GamePart ids behind the buttons** (Q4) | the *screens* are measured; the ids are a name match onto the executable's class names | | 🟑 | **the boot transitions in code** (Q6) | both levels decoded β€” phase at `this+132` (`entryβ†’2`, `2β†’0`, `2β†’3`, `3β†’4`, `4β†’2`) and state at `this+136` inside phase 4. Phase 0 = splash (`LOGO`), phase 2 = title + `PRESS β’Ά`, phase 4 = menu. Unknown: what the event *numbers* mean | | 🟑 | **β’· leaving the main menu** (Q5) | **upgraded 2026-08-29 (later).** The idle half of this objection is **refuted**: the main menu does not self-return for **β‰₯ 60 s** untouched, and the ~8–10 s idle belongs to the **title**. β’· is delivered (Canary logs `vk=5801`) and is the only input in β‰₯ 100 s before the return, so the **ordering is measured**; the latency is not (a backlogged probe void). The footer point stands β€” the main menu is still the only screen not advertising β’· β€” [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md#-refutation-attempt-2026-08-29--the-main-menus-own-footer-does-not-advertise-β“‘) | -| ❔ | **builds 0/1 and 10/11**, the `DELTASABER` plates (Q2) | never seen anywhere in the boot path, the title-side screens or the attract loop. A mission load is the remaining candidate and this container kills runs before one completes | +| 🟑 | **which loading bundle is `LOADING` and which `LOADING2`** (Q2) | βœ… the pair is identified β€” they are the loading screen, decoded from `pgloading_*` element names, and the executable names exactly two. What is open is only the assignment, and nothing observed maps a name to a bundle. ⚠️ The old row here said the pair was *unidentified `DELTASABER` plates never seen running*; that is withdrawn β€” a loading screen is not supposed to appear on the title path | (An earlier version of this table called the audio items blocked on "an emulator whose audio path can be observed". That was wrong β€” this build already has diff --git a/docs/re/INDEX.md b/docs/re/INDEX.md index 913dcb47..b08454bc 100644 --- a/docs/re/INDEX.md +++ b/docs/re/INDEX.md @@ -136,6 +136,7 @@ files, which is how the same ground got covered twice. | [`structures/stage-mission-tables.md`](structures/stage-mission-tables.md) | The stage table set β€” phases, routes, sub-objectives and AI parameters | βœ… the table set and how the stage record reaches it, validated across; **`AIParams` disc-wide: 23 objects, one shared 34-profile roster (782 records), loader `sub_8233C368`; `Type`β†’field-count holds except the two `_Test` templates** | | [`structures/texture-color-k8888.md`](structures/texture-color-k8888.md) | Texture colour interpretation β€” `k_8_8_8_8` (32bpp UI/HUD textures) | β€” | | [`ui-keyframe-time-unit.md`](ui-keyframe-time-unit.md) | What a keyframe time is worth, and what shape the ramp has | βœ… CONFIRMED from the running game's own draw stream β€” the ramp is **linear** (a declared 15-unit fade lands on `round(255Β·k/15)` for all seven samples) and the animation clock advances **2 time units per submitted frame**. 🟑 the seconds conversion (`1 unit = 1/60 s`) rests on a measured 27.6 present-frames/second | +| [`ui-keyframe-record-layout.md`](ui-keyframe-record-layout.md) | A keyframe's time word comes **before** its pose β€” the placement record, decoded | βœ… CONFIRMED, **decoded**. A group is an 8-byte header then `frames` records of `{u32 time; 36-byte pose}`, so the time precedes the pose; the group's lead-in word at `header+8` is pose 0's time and **every** pose is timed. Disc-wide over 13 991 groups in 33 archives, each test with a control: lead-in prepended is non-decreasing **13 991/13 991**; a non-zero lead-in is strictly below the next time **5 058/5 058** (control 70.9 %); a multi-segment alpha ramp runs at a constant `dΞ±/dt` **857/1 540** against **0/1 042** under the old reading. πŸ”΄ Retires two long-standing corpus claims β€” *"a group's data stops 4 bytes short of its final block's time slot"* and *"the last keyframe carries no time"* β€” both of which were this off-by-one. Adoption is free: all 12 `GP_TITLE` builds render byte-identically, and over 217 builds only two elements pick a different `rest()` pose, both between equally invisible ones. ❔ the executable's own parser was **not** found (the 40/60 stride query is weak, not negative) | | [`structures/ui-composable-bundles.md`](structures/ui-composable-bundles.md) | A screen build is not the only thing `compose` can draw | βœ… CONFIRMED by measurement over the disc, with the artifact to | | [`structures/ui-focus-and-effect-elements.md`](structures/ui-focus-and-effect-elements.md) | `_eff` glow layers are not focused-state records | βœ… CONFIRMED by measurement over all 965 screen builds on the disc, | | [`structures/ui-paint-order-key.md`](structures/ui-paint-order-key.md) | The paint order comes from a layer key in the T8aD sprite header | βœ… CONFIRMED on both screens whose paint order has been measured β€” | @@ -157,7 +158,7 @@ files, which is how the same ground got covered twice. | [`boot-config-and-gamepart-registry.md`](boot-config-and-gamepart-registry.md) | What the game reads at boot β€” `config.ini`, and which GameParts exist | βœ… `config.ini` selects the language (the disc's only config); ❔ its `[SYSTEM]` is empty so the boot order is not in config; 🟑 24/29 ids bind to a class, `GP_ADVERTISE_DEMO` is never registered | | [`movie-binding.md`](movie-binding.md) | Which movie plays where β€” boot intro, attract loop, new-game intro | βœ… decoded from the movie manifest (`ADVERTISE_MOVIE`β†’`ADV.wmv`, `MS00A`β†’`S00A.wmv`); attract identity confirmed independently by frame matching; 🟑 skippability unsettled | | [`ready-room-probe.md`](ready-room-probe.md) | S1 β€” the Ready Room probe: no-go, and not for the reason expected | βœ… it is 2D and enumerates (60 builds), but the pak is briefing/tactical-map content; and `kind == 0x3002` finds 0 buttons there | -| [`ui-title-build-map.md`](ui-title-build-map.md) | Which `GP_TITLE` build is which screen state | βœ… CONFIRMED for title / `PRESS β’Ά` / main menu / `EXTRAS` against live captures; the archive is 8 screens Γ— EN/JP, and "6/8/9 are submenus" is withdrawn | +| [`ui-title-build-map.md`](ui-title-build-map.md) | Which `GP_TITLE` build is which screen state | βœ… CONFIRMED for title / `PRESS β’Ά` / main menu / `EXTRAS` against live captures; the archive is 8 screens Γ— EN/JP, and "6/8/9 are submenus" is withdrawn βœ… **2026-08-29: the two "unidentified `DELTASABER` plates" are the LOADING screen** β€” builds 0/1 the plain variant, 10/11 the dressed one, decoded from their `pgloading_*` element names, and the executable (`sub_821C4EB0`, bytes checked in the image) names exactly five title-side screens: `TITLE_SCREEN`, `BUTTON`, `TITLE_MENU`, `LOADING`, `LOADING2`. 🟑 which loading bundle takes which of the two names is undecided. 🟑 the English member of a pair is the one in the first half of `GP_TITLE.p00` β€” 8/8 structurally, 3/3 where a capture can check it. | | [`ui-title-paint-order-capture.md`](ui-title-paint-order-capture.md) | The title screen's paint order, measured from the guest's draw submissions | βœ… CONFIRMED β€” the order in which the running game paints the title | | [`upstream-baseline.md`](upstream-baseline.md) | A stock-upstream baseline runs Stage 02 crash-free | βœ… CONFIRMED β€” upstream canary_experimental + only the pad | | [`weapon-datasheet-runtime.md`](weapon-datasheet-runtime.md) | Weapon DATA SHEET β€” runtime capture (Route B) | 🟑 first dynamic capture, 2026-07-28. The Arsenal's Gallery Mode panel is a | diff --git a/docs/re/ui-keyframe-time-unit.md b/docs/re/ui-keyframe-time-unit.md index f364effc..c2403112 100644 --- a/docs/re/ui-keyframe-time-unit.md +++ b/docs/re/ui-keyframe-time-unit.md @@ -1,5 +1,21 @@ # What a keyframe time is worth, and what shape the ramp has +> ## βœ… 2026-08-29 β€” the argument on this page about WHICH BLOCK OWNS A TIME is over +> +> It was never a choice between two readings. A placement group is +> `frames` records of `{u32 time; 36-byte pose}` after an 8-byte header, so the +> time word **precedes** its pose; the group's "lead-in word" is pose 0's time, +> and **no** time is missing. `SYLPHEED_KF_TIME_SHIFT` had the association right +> and pose 0 untimed, which is the only reason it looked like it cost build 7 +> 13.1 % of its pixels. Decoded disc-wide, with controls, in +> [`ui-keyframe-record-layout.md`](ui-keyframe-record-layout.md); the gate is now +> `SYLPHEED_KF_TIME_LEGACY=1`. +> +> **Everything else on this page stands** β€” the ramp is linear, the clock advances +> 2 units per submitted frame, and `1 unit = 1/60 s` is measured. Read the +> sections below with that correction applied: where a table pairs a time with a +> pose, the pairing is the corrected one. + **Status:** βœ… `CONFIRMED` for the two things the port is blocked on β€” the ramp is **linear**, and the animation clock advances **2 keyframe time units per frame the game submits**. 🟑 the conversion to *seconds* rests on one further step: the game