re: Q3 was already answered in docs/re -- deliver it, and test it on EXTRAS

The handoff had Q3 as "runtime-solved only". It is not: the layer key at
+0x0A of the T8aD sprite header, stable-sorted, is a file-derivable paint
order, already checked against five measured orders and already driving
the compositor for every build on the disc. That answer had simply never
reached the page the port agent reads.

The new evidence is EXTRAS -- the first screen composited from the rule
alone and scored against a framebuffer capture the rule had never seen.
0.9620 at zero shift, against a same-tooling control of 0.9657 on the
screen the rule was fitted to.

Written down with its limit rather than its headline: align_to_capture
correlates edges, and a paint-order change moves blends, not edges, by
=<45/255 on a few per cent of pixels. So the score is evidence for
placement and only CONSISTENT with the order. The sharp A/B needs a
rebuild and is named in the doc instead of being claimed.
This commit is contained in:
Sylpheed RE agent
2026-08-28 17:39:29 +00:00
parent 88b3ce9af5
commit bebdb18a02
3 changed files with 66 additions and 1 deletions

View File

@@ -29,7 +29,7 @@ authored version can be deleted.
|---|---|---|---|
| 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) |
| 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 | ❔ open | runtime-solved only; declaration table is refuted |
| 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 | ❔ open | labels are baked into sprites |
| Q5 | navigation semantics | ❔ open | |
| Q6 | boot sequence + what drives it | 🟡 partial | order observed, and the **attract cycle is timed**: ~810 s idle on the title → fade to black → ~85 s of video → title again, plate and all. The driver is still not decoded |
@@ -76,6 +76,24 @@ authored version can be deleted.
[`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.
* **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,
**stable-sorted** so equal keys keep declaration order; elements with no sprite
(`.prm` primitives, `.tbm`) have no key and take an implied one — the backdrop
and dim quads sort early, the screen-transition fade (`pteff00.prm`,
`pfeff00.prm`) sorts **last**. ✅ decoded. Checked against five paint orders read
off the running game, one of them (`GP_SAVE_LOAD`'s slot-list header, 6
instances) **exact and independent of the screens the rule was fitted to**, and
against a fresh `EXTRAS` capture. It reorders 341 of the disc's 965 builds, so
it is not a no-op dressed as a rule.
🟡 **The one residual: ties.** Where two elements share a key the game
sometimes paints them in an order nothing predicts — eight candidates refuted,
including declaration order, RATC child order, keyframe times, resting X/Y and
`kind`. Measured cost: on the three screens with ground truth it changes the
blend of **one element on one screen** (a title glow). Take the stable sort and
accept that.
* **The GamePart id table** — 29 entries at `.rdata 0x820A1630`, confirmed by the
executable's own registration strings. ✅ This is the screen vocabulary; which
button reaches which entry is Q4 and is *not* part of it.

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

View File

@@ -395,3 +395,50 @@ Three published numbers on this page were wrong the same way: each was computed
over a population I had not checked was the population in question — the six
language copies, then the top-level-only sprites, then the size of that gap. The
analysis was never the problem. **Check the sampling frame before the statistic.**
## `EXTRAS` — the derived order against a capture nobody had taken (2026-08-28)
The five orders above are all **measured**: the derived rule is checked against a
live child list. This one is the other direction — a screen with **no measured
order at all**, composited purely from the derived rule and scored against a
framebuffer capture taken the same day
([capture](../captures/title-builds/live-extras.png),
[composite](../captures/ui-layout/extras-composited.png)).
`GP_TITLE.pak` build 6, 18 elements, two `.prm` primitives:
```
sylpheed-cli screen render --build 6 --black --primitives GP_TITLE.pak extras.png
align_to_capture.py live-extras.png extras.png --region 150 100 1150 600
```
| composite | best shift | score |
|---|---|---|
| **build 6, `--black --primitives`** | **(0, 0)** | **0.9620** |
| build 6, no primitives | (0, 0) | 0.9576 |
| *control:* main menu vs `main-menu-oracle.png`, same tooling | (0, 0) | 0.9657 |
The control was run first and reproduces the screen this rule was fitted to, so
the harness is known-good before the new screen is scored. `EXTRAS` lands within
0.004 of it, at zero shift, with the primitives helping rather than hurting.
### ⚠️ What this does and does not show
**It does show** that every other part of the stack transfers to an unfitted
screen: element enumeration, resting pose, pivots, `fade` alpha, and that the
two primitives land somewhere harmless under the derived `implied_layer_key`.
**It does not show that the paint order is right.** `align_to_capture.py`
correlates *gradient magnitude* — edges — and a paint-order change moves blend
values, not edges. The earlier derived-vs-declaration A/B on the pause menu and
main menu measured exactly that: 1.13.8 % of pixels differing by ≤45/255, in
translucent overlaps. A score computed this way cannot see a difference that
size, so **0.9620 is evidence for placement and only consistent with the order.**
**The sharp test, not run.** Composite build 6 twice — the committed derived
order, and `compose` temporarily reverted to declaration order — and diff both
against the capture over the pixels where they disagree. That needs a rebuild of
`sylpheed-formats`, which is why it is named here rather than done; it is the
same A/B this page already ran on the two screens that had oracles, and `EXTRAS`
would be the first screen where the two orders can be judged against ground
truth rather than declared indistinguishable.