diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 10cc9aac..9a1bf995 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -29,7 +29,7 @@ authored version can be deleted. |---|---|---|---| | 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) | | 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) | +| 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) | | 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) | diff --git a/docs/re/METHOD.md b/docs/re/METHOD.md index 44cd1015..e4aa2139 100644 --- a/docs/re/METHOD.md +++ b/docs/re/METHOD.md @@ -564,3 +564,10 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the elements paints identically either way. Reporting 15 would have overstated the risk by 7×; the useful number is the one filtered by whether the difference can reach a pixel. +* **An exhaustive field search needs a positive control, or "found nothing" is + worthless.** Scanning a header for a field that reproduces a measured ordering + returned zero hits — which could equally mean the field is absent or the scan + is broken. Running the same scan against a *known* ordering (declaration order) + returned **64** hits, proving the scan finds ordering fields when they exist. + Only then is the zero a finding. The control costs four extra lines and turns a + silence into a negative with reach. diff --git a/docs/re/REFUTED.md b/docs/re/REFUTED.md index 99ba1390..23a61105 100644 --- a/docs/re/REFUTED.md +++ b/docs/re/REFUTED.md @@ -457,3 +457,10 @@ neighbourhood, not just the line. repeating one stale frame; cross-checked, it read surface mean 5.21 where `import` read 125.65 at the same moment. A dense negative from a frozen stream is not a negative. [`capture-harness-status.md`](capture-harness-status.md) +* "the `T8aD` layer key fully determines a screen's paint order" → **refuted, and + the remainder is undecodable.** Elements sharing a key are tied; on the title + the game paints the five tied `ptlogo_back2eff` glows `1,2,5,3,4` while the + declaration table, the RATC child order and **every** field in the `T8aD` + header (exhaustive 0x00–0x7f, u8/u16/u32, both directions — 0 matches against + 64 for the declaration-order control) all give `1,2,3,4,5`. + [`ui-paint-order-derived-check.md`](structures/ui-paint-order-derived-check.md) diff --git a/docs/re/structures/ui-paint-order-derived-check.md b/docs/re/structures/ui-paint-order-derived-check.md index 8c6e0fd6..17858c1b 100644 --- a/docs/re/structures/ui-paint-order-derived-check.md +++ b/docs/re/structures/ui-paint-order-derived-check.md @@ -72,3 +72,57 @@ worst on the disc at 37 tied pairs, 16 overlapping. * Overlap uses `pivot × 2` as the element's size (documented as the sprite's own dimensions for a `.t32`) at its resting placement, so scaled or rotated elements are approximated. + +--- + +## ❔ The tie-break is undecodable from the bundle — searched, with reach + +The audit above leaves one question: the layer key orders elements, but what +orders elements that **share** a key? On the title that tie-break decides two +total occlusions, so it is not academic. + +The game paints the five tied `ptlogo_back2eff` glows in the order +**eff1, eff2, eff5, eff3, eff4**. Three static structures were searched for +anything that reproduces it. + +**1. The declaration table.** Entries 14–18 are *byte-identical* apart from the +pivot, which is just half the sprite's own size: + +``` +14 ptlogo_back2eff1 00000000 ffffffff ffffffff 00000000 ffffffff 0000004e 0000003c 00000000 +15 ptlogo_back2eff2 00000000 ffffffff ffffffff 00000000 ffffffff 00000074 0000005b 00000000 +16 ptlogo_back2eff3 00000000 ffffffff ffffffff 00000000 ffffffff 000000aa 0000005b 00000000 +17 ptlogo_back2eff4 00000000 ffffffff ffffffff 00000000 ffffffff 00000149 0000005b 00000000 +18 ptlogo_back2eff5 00000000 ffffffff ffffffff 00000000 ffffffff 000001fb 0000007e 00000000 +``` + +**2. The `T8aD` headers.** All five carry identical `+0x04` (`0x8832`) and +identical `+0x08`/`+0x0A` (`32899`, the layer key itself). They differ only in +position and tile count. Searched **exhaustively**: every offset `0x00–0x7f`, at +u8, u16 and u32 width, sorted both ascending and descending — + +| | | +|---|---| +| fields sorting to the **measured** order | **0** | +| fields sorting to the **declaration** order (control) | **64** | + +The control matters: 64 fields *can* be found that reproduce a known ordering, so +the scan is capable of finding an ordering field when one exists. It finds none +for the order the game uses. + +**3. The RATC child order** — the bundle's second element list, which is +genuinely a different permutation from the declaration table on other screens. +For this family it reads `eff1, eff2, eff3, eff4, eff5`: declaration order again. + +### The answer + +❔ **Undecodable from the bundle, with reach.** All three static orderings give +`eff1…eff5`; the game gives `eff1, eff2, eff5, eff3, eff4`. That is consistent +with what [ui-screen-runtime](ui-screen-runtime.md) already concluded from the +other direction — the game builds a **reordered child list at load time** and +paints that. + +So Q3 resolves as: the layer key is ✅ **decoded** and orders 4 of the 5 measured +bundles exactly; the **tie-break within a key is ❔ undecodable**, and a consumer +must either use a measured order or accept declaration index as an arbitrary +stand-in.