docs/re: the title-screen oracle — what it settled, and what it refuted
`BACKLOG.md`'s "the declaration table is not a paint order on every screen" had been reasoned about but never measured. It is now measured, and the entry says so both ways. Confirmed: a framebuffer capture of Canary on the title screen shows the `PROJECT SYLPHEED` wordmarks over the full-screen background, so declaration order is not the paint order there. Refuted, and recorded rather than quietly dropped: * the `0x10`-adjacency first step this entry proposed. The background *is* next to a `kind = 0x10` `.prm` on both screens that have one — but on opposite sides (`GP_TITLE` puts the background after it, `GP_MISSION_SELECT` before), so no rule keyed on the `.prm` orders both. `GP_READY_ROOM` and `GP_OPTIONS` cannot serve as the third and fourth witnesses: neither carries a `.prm` or a full-screen background at all. * the placement-region group order and the RATC child order, as above. * reverse declaration order, by the same capture — it would draw the background over the copyright line, which is visible. And separated out: part of what the bad render showed was not ordering at all but the pivot/scale defect fixed two commits back. `ui-rat-layout.md` gains that rule, the correlation evidence, the unmeasured pivot-vs-centre gap, and the disc-wide demotion of "pivot is exactly half the texture" (49 % agree, 37 % are off by more than 16 px — `ptcopyright` is 694 px wide in the framebuffer and `pivot*2` says 618). Captures: `title-screen-oracle.png` (the game surface cropped out of the emulator window at (1,45), 1:1, bottom 45 rows clipped by the display) and `title-screen-composite-2026-08-18.png` beside it.
This commit is contained in:
@@ -58,8 +58,9 @@ out-of-range value anywhere in any bundle), and the tutorial bundle, which has n
|
||||
The table lists **both** sprites and `.rat` records — it is the screen's element list.
|
||||
`pgp_ttrl` declares 11: six `eff*`, `msg`, and four `.rat`s (`title`, `btn10..12`).
|
||||
|
||||
✅ **Verified:** for all 7 `.t32` entries the declared pivot is *exactly* half the decoded
|
||||
texture's dimensions — `eff10` 408×120 → 204,60; `eff21` 428×360 → 214,180; `msg` 381×38 →
|
||||
✅ **Verified — but see the disc-wide correction at the end of this document, which
|
||||
demotes this to a property of *this bundle*:** for all 7 `.t32` entries the declared
|
||||
pivot is *exactly* half the decoded texture's dimensions — `eff10` 408×120 → 204,60; `eff21` 428×360 → 214,180; `msg` 381×38 →
|
||||
190,19; and so on, 7/7 with no mismatch (`tools/re-capture/ratc_decls.py`).
|
||||
|
||||
`GP_PAUSE_MENU.pak`'s six bundles are `{in-mission, tutorial} × {English, Japanese}`, with
|
||||
@@ -191,7 +192,11 @@ recording them because a static-only reading would have shipped them:
|
||||
screen's draw list lives. **It is the bundle's own declaration table** (above): that
|
||||
table is not the child listing — the children are grouped by type (every `.t32`, then
|
||||
every `.rat`), while the declaration table names *elements*, in a plausible back-to-front
|
||||
order, and it is exactly the missing set that appears there. For the in-mission pause
|
||||
order, and it is exactly the missing set that appears there. **"Plausible" is
|
||||
as far as that goes** — a framebuffer capture of the title screen refutes it on
|
||||
that screen (its background is element 13 and it is drawn behind elements 0–5),
|
||||
and no other ordering in the bundle replaces it; see
|
||||
[`BACKLOG.md`](../BACKLOG.md). For the in-mission pause
|
||||
menu it lists 23: `eff11/12/10`, `eff02(+a)`, `eff03(+a)`, `eff01`, `title.rat`,
|
||||
`btn00/01/04/05.rat`, `msg`, `deli1…4`, `eff30…33`, `loop1.rat`. Note what it does
|
||||
**not** list: the focused button variants, which are reached through each base record's
|
||||
@@ -338,3 +343,82 @@ and that sprite exists **nowhere** — not as a pak member, not as a child of th
|
||||
bundles. It is the second such dangling asset found, after
|
||||
[`SUBTITLE_S12B.tbl`](../movie-subtitle-link.md); a reimplementation should skip
|
||||
a missing sprite rather than treat it as a decode failure.
|
||||
|
||||
## The pivot is the scale centre — measured against the framebuffer (2026-08-18)
|
||||
|
||||
**Status:** ✅ `CONFIRMED` that a keyframe's scale grows the element **about its
|
||||
declared pivot** rather than about the keyframe's own corner. 🟡 the pivot and
|
||||
the sprite centre are *not* told apart by this measurement — see the scope below.
|
||||
|
||||
The compositor had been reading a keyframe as `top-left = (X,Y)` and
|
||||
`size = decoded · scale`, ignoring the pivot entirely. That is right for every
|
||||
element at 100 %, which is why the pause menu and the ARSENAL rulers above never
|
||||
caught it, and wrong for every element that is scaled.
|
||||
|
||||
### The measurement
|
||||
|
||||
`GP_TITLE.pak` build 7, element 13:
|
||||
|
||||
```
|
||||
ptbase2.t32 decoded 640x360 pivot (320,180) one keyframe: (320,180) 200%,200%
|
||||
```
|
||||
|
||||
Composited from the corner that is a 1280×720 rect at 320..1600 × 180..900 — a
|
||||
quarter-screen slab hanging off the bottom-right, with the top-left quadrant
|
||||
bare. Anchored at the pivot it is `(320,180) − (320,180)·(2−1) = (0,0)`, i.e.
|
||||
exactly the screen.
|
||||
|
||||
The oracle is a framebuffer capture of Canary sitting on the title screen
|
||||
([`captures/title-screen-oracle.png`](../captures/title-screen-oracle.png), the
|
||||
game surface cropped out of the window at offset (1,45), 1:1, the bottom 45 rows
|
||||
clipped by the display):
|
||||
|
||||
- the background art reaches all four edges, so the corner reading is refuted
|
||||
outright;
|
||||
- normalised cross-correlation of the composite's background against the capture,
|
||||
searched over ±40 px in both axes, peaks at **(0,0)** — 0.90 on the planet limb,
|
||||
0.72 on the lower-left ship. Not "close": the correct shift is the argmax.
|
||||
|
||||
Calibration for that crop is `ptcopyright.t32`, which is unscaled: 694×20 placed
|
||||
at (293,655), and the capture's glyph run is x 295…986, y 700…718 — inside that
|
||||
rect to the pixel once the 45 px of window chrome is taken off. So at 100 % the
|
||||
keyframe really is the top-left, as this document already said.
|
||||
|
||||
Residual, stated rather than glossed: the composite is uniformly brighter than
|
||||
the capture (about +14 in the dark starfield) and less blue in the planet. That
|
||||
is **not** placement — it is the two full-screen `PRMD` elements (`pteff00.prm`,
|
||||
`pteff02.prm` at fade-alpha ≈ 204) and `pteff04.t32` that the compositor does not
|
||||
draw at all. Their geometry is known (a colour and four corners); their blend is
|
||||
not.
|
||||
|
||||
### Scope, and what this does *not* settle
|
||||
|
||||
Swept over every screen build on the disc (`tests/ui_paint_order_disc.rs`):
|
||||
|
||||
| | count | of 5 130 resting placements with a decoded sprite |
|
||||
|---|---|---|
|
||||
| scaled ≠ 100 % — i.e. moved by this fix | **865** | 17 % |
|
||||
| of those, where "about the pivot" and "about the sprite centre" differ by ≥2 px | **213** | 4 % |
|
||||
|
||||
`ptbase2`'s pivot **is** half its size, so the capture proves only that scaling is
|
||||
not about the keyframe corner. Pivot-anchoring is chosen because that is the field
|
||||
the format carries and this document already called it "a rotation/scale centre";
|
||||
the 213 placements that could tell the two apart are **unmeasured**, and a capture
|
||||
of any one of them mid-animation would settle it.
|
||||
|
||||
### Correction: "pivot = half the texture" does not generalise
|
||||
|
||||
The ✅ above — *"for all 7 `.t32` entries the declared pivot is exactly half the
|
||||
decoded texture's dimensions, 7/7 with no mismatch"* — is a property of the
|
||||
tutorial pause bundle, not of the disc. Across all 5 130 placements:
|
||||
|
||||
- **2 521 (49 %)** have `pivot·2` equal to the decoded size within 1 px;
|
||||
- **1 884 (37 %)** are off by more than 16 px.
|
||||
|
||||
Some of that is already explained here — the `.rat` layer is inherited from the
|
||||
Japanese master, so its baked pivot belongs to another language's sprite. But it
|
||||
also happens on `.t32` declarations (`pgpeff02.t32` in the in-mission pause build:
|
||||
decoded 265×198, declared pivot 19,18), so the rule is not merely a
|
||||
language-inheritance artefact. **Do not derive a texture size from a pivot**; the
|
||||
capture backs the decoded size, not the pivot — `ptcopyright` is 694 px wide in
|
||||
the framebuffer, and `pivot·2` says 618.
|
||||
|
||||
Reference in New Issue
Block a user