re(ui): decode keyframe +12 as screen-plane rotation in degrees

The rotated quads on the title screen come from the keyframe block after
all. The earlier negative -- "every GP_TITLE build 4 element has all three
angle words at zero" -- read the right bytes over too small a region: it
walked the top-level declaration table, and the rotated elements are the
nested leaf records ptloop01.rat / ptloop02.rat.

Confirmed against the framebuffer rather than against our own renderer.
The two records declare +12 = 30 and -45; the GPU capture submits their
quads at +30.26 and -45.28 degrees -- magnitude and sign, two different
values. Corroborated by shape in GP_BUNK 117ca14f, where +12 ramps
0 -> 360 with position, scale and alpha constant: a spin in place.

Identifying which draw it was needed edge lengths, not bounding boxes:
400x1076 and 400x1444 against pteff03/pteff03a 399x180 at the elements'
two different declared scales, 600% (1080) and 800% (1440). The same test
names three known-positives in the capture (ptlogo1, ptcopyright,
ptbtn00), so it passes its own control.

Keyframe gains rotation_deg plus unknown_4/unknown_8, carried rather than
dropped. NOT rendered -- ui_layout::blit is axis-aligned only, so the
reference renderer and the port will both draw these upright until a
rotating blit exists.

The census tool ships with the trap that broke its first version: nested
RATC blobs are not 4-byte aligned, so an aligned scan found 0/3 of its
own control blocks and missed 16 341 blocks. Disc-wide +12 is non-zero in
14.50 % of 83 862 blocks.

sylpheed-formats tests, SYLPHEED_DISC set: 131 passed, 0 failed across the
6 suites finished at commit time; the run had not yet completed.
This commit is contained in:
Sylpheed RE agent
2026-08-28 22:41:32 +00:00
parent abe7477476
commit ffd1a30ba7
10 changed files with 430 additions and 42 deletions

View File

@@ -109,12 +109,14 @@ authored version can be deleted.
test is to blend bit-`0x02` sprites additively and re-correlate the title
against the capture. Note `ptlogo_back2eff` is `0x8830` despite its name, so
this is a field and not a naming pattern. ❔ Not diagnosed.
⚠️ **Read the correction below before using this.** The rotated draw was
identified as the swoosh by elimination and that identification is **refuted** —
its quads span y −209…925 in screen space, the swoosh is a band at y 126…360.
What stands: the game *does* submit rotated parallelograms this compositor
cannot draw, and vertex colours are white. What does not: which element they
belong to.
⚠️ **The rotated draw is NOT the swoosh.** It was identified as the swoosh by
elimination; that is **refuted** — its quads span y −209…925 in screen space,
the swoosh is a band at y 126…360. ✅ **It is the two `ptloop` sweeps**
(`ptloop01.rat` → `pteff03.t32`, `ptloop02.rat` → `pteff03a.t32`), confirmed by
edge length: 400 × 1076 and 400 × 1444 against 399×180 at the two elements'
**different** declared scales, 600 % (= 1080) and 800 % (= 1440). What stands
from the old bullet: the game *does* submit rotated quads this compositor
cannot draw, and vertex colours are white.
✅ **SOLVED 2026-08-28 by draw capture — it is the GEOMETRY.** The game submits
the swoosh as **two rotated parallelograms** (edges `(0.54,−0.56)` and
`(0.44,0.79)`, ~45° and ~61°, extending to `y=±1.81` NDC).
@@ -124,8 +126,33 @@ authored version can be deleted.
defect.**
🔴 Vertex colour is refuted with it: every colour in the capture is
`<alpha>FFFFFF`, white RGB.
❔ **Format gap:** the decoded keyframe has `fade`/`scale`/`tint`/`x`/`y`/`time`
and **no rotation**, so where the rotation comes from is not decoded.
✅ **CLOSED 2026-08-28 — the rotation IS on the disc, at keyframe `+12`.**
It is a signed angle in **degrees**, clockwise-positive in screen space
(Y down), and `ui_layout::Keyframe` now carries it as `rotation_deg`.
Confirmed against the framebuffer, not against our own renderer: the two
`ptloop` records declare `+12` = **30** and **−45**, and the GPU capture
submits their quads at **+30.26°** and **−45.28°** — magnitude and sign, on two
different values. Corroborated separately by shape: `GP_BUNK` entry `117ca14f`
holds a group whose `+12` ramps **0 → 360** with position, scale and alpha all
constant — a spin in place. Disc-wide `+12` is non-zero in **14.50 %** of
83 862 keyframe blocks.
⚠️ **Two things the port must know about it.**
(1) 🟡 **Rotation appears to live only in NESTED `.rat` leaf records.** Every
instance found so far is nested, and the top-level table is empty of it across
`GP_TITLE`, `GP_BUNK` and `GP_CHALLENGE` — a pattern over three archives, not a
finished disc-wide sweep. Either way it is the actionable half: a composer that
reads only the declaration table will see zero rotation everywhere and be wrong
on exactly the elements that move.
(2) **`sylpheed-cli screen render` still does not rotate.** The field is
decoded, not rendered; `ui_layout::blit` is axis-aligned only. So the reference
renderer and the port will *both* draw these upright until a rotating blit
exists — and per your own rule, the two of them agreeing about it means
nothing.
🟡 The neighbouring words `+4` and `+8` are still unexplained: signed, non-zero
in ~4.8 % / 4.6 % of blocks, almost entirely `±180`/`±90`. That distribution
looks like a flip flag rather than a free angle, but nothing observed turns on
them — **do not transcribe them as X/Y rotation.**
[`ui-keyframe-rotation.md`](../re/structures/ui-keyframe-rotation.md)
⚠️ The earlier "pink versus white" reading compared two differently-shaped
renderings and should be re-checked after geometry, not carried as a separate
defect.