re(ui): a full-screen element is dropped on three port screens -- do not "fix" it

Audited what `screen render` silently omits on the port's five screens,
since an element the game draws but we skip is the one defect class the
port agent has actually hit. Everything is accounted for -- kind & 0x4
ghost instances, .prm primitives, loop* animations -- except pteff04.t32
on the title and pteff05.t32 on both menus. Those are kind 0x0, one
keyframe, rest a=255, pivot (640,360): full-screen and opaque.

Cause: the element declares pteff05.t32, but the T8aD behind its `opt `
link is registered under the name 8AX, so build.sprites.get() misses and
compose hits a silent continue. Bytes at 0x0e2035 of GP_TITLE entry 5:

  opt  ... 70 74 65 66 66 30 35 2e 74 33 32 00  38 41 58  54 38 61 44
           p  t  e  f  f  0  5  .  t  3  2  \0  8  A  X   T  8  a  D

8AX is 1280x720 and present in all six title-family bundles; it is a
sprite in builds 4/5/6 and never an element.

It does not currently show, and that is the useful half. ptbase.t32 is
640x360 drawn at 200% and carries THE SAME ARTWORK: its 2x upscale
differs from 8AX by mean abs diff 2.05 (max 80), and our rendered
background is pixel-identical to 8AX in every patch sampled. So resolving
the name and drawing it in addition would double-draw an opaque
full-screen layer -- invisible as a doubling, which is worse than a
visible bug. Written into HANDOFF as a do-not-do.

The free win, offered and not taken: use 8AX at 1:1 and drop ptbase
instead of upscaling a half-res copy. That is a rendering choice and
ptbase's element carries the keyframes, so it is the port's call.

Not established: which of the two the game actually draws. Both carry the
same art, so pixels cannot separate them; it needs a per-draw capture
recording texture base addresses, since the two differ in size.
This commit is contained in:
Sylpheed RE agent
2026-08-29 01:00:36 +00:00
parent 2624008c93
commit 1e82096519
3 changed files with 117 additions and 0 deletions

View File

@@ -470,3 +470,15 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the
never moved. The signal to stop is not "this is hard", it is "the thing this
unblocks is not blocking anything". Write down the recipe you built and leave
it for a session with a reason to spend the time.
* **Audit what a renderer silently SKIPS, not only what it draws.** `compose` has
half a dozen `continue` paths, and a sprite-name miss is one of them — an
element vanishes with no error, which is exactly how the port agent's missing
bracket happened. `screen render` already prints its omissions; reading that
list for the five screens that matter took one command and turned up a
full-screen opaque element dropped on three of them. Any composer with skip
conditions should be asked to name what it skipped before it is trusted.
* **An element's declared name is not necessarily its texture's name.** The
`opt ` link on `pteff05.t32` leads to a `T8aD` registered as `8AX`, so a
lookup by declared name misses. Before concluding a texture is absent, look at
the bytes the link actually points at — the name in the declaration table and
the name the sprite table keys on can differ.

View File

@@ -0,0 +1,84 @@
# 🟡 `pteff04` / `pteff05` are dropped — the texture is registered as `8AX`
**Status:** 🟡 a real name-resolution gap, **currently harmless to look at**, and
the obvious fix would make the render *worse*. Found by auditing what
`screen render` silently omits on the port's five screens.
## What `screen render` drops, and why
`sylpheed-cli screen render` reports its omissions. On the five screens the port
needs, every one is accounted for except two:
| screen | not drawn | reason |
|---|---|---|
| title (4) | `ptlogo1`×2, `ptlogo2`×2 | `kind & 0x4` ghost instances — the draw capture shows one quad per wordmark |
| | `pteff00.prm`, `pteff02.prm` | `.prm` primitives, off without `--primitives` |
| | `ptloop01.rat`, `ptloop02.rat` | `loop*` animations, off without `--animated` |
| | **`pteff04.t32`** | ⚠️ **unexplained — this page** |
| main menu (5), `EXTRAS` (6) | as above, plus **`pteff05.t32`** | ⚠️ same |
| splash (10, 11) | `palogo_eff0.prm` | primitive |
`pteff04`/`pteff05` are `kind = 0x0`, one keyframe, rest **`a = 255`**, and pivot
`(640,360)` — i.e. a **full-screen, fully opaque** element. Exactly the shape of
the defect the port agent reported (an element the game draws that we omit).
## The cause: the element's name and its texture's name differ
Both names occur twice per bundle, the same as `pteff03.t32`, which resolves
fine. The difference is what follows the `opt ` link:
```
pteff03.t32 @0x4661a4 opt → T8aD ← resolves
pteff05.t32 @0x0e2035 opt → 8AX T8aD ← the sprite is named 8AX
```
Hex at `0x0e2035`:
```
opt .... 70 74 65 66 66 30 35 2e 74 33 32 00 38 41 58 54 38 61 44
p t e f f 0 5 . t 3 2 \0 8 A X T 8 a D
```
So the declaration says `pteff05.t32` while the `T8aD` that follows is registered
under **`8AX`**. `compose` does `build.sprites.get(sprite)` and, on a miss, hits a
silent `continue`. `8AX` is a **1280×720** texture and appears in all six
title-family bundles (`a60fcb85`, `a715f485`, `a81c1d85` and the three JP twins);
it is a *sprite* in builds 4/5/6 and never an element.
## ✅ Why it does not currently show
The same artwork is already on screen. `ptbase.t32` is **640×360** and its
element draws at **200 %**, and the background our render produces is
pixel-identical to `8AX` in every patch sampled:
| patch | capture | our render | `8AX` |
|---|---|---|---|
| top-left | 4.5, 9.4, 19.5 | **17.7, 29.7, 46.7** | **17.7, 29.7, 46.7** |
| top-right | 4.4, 9.0, 18.2 | 17.2, 28.2, 43.8 | 17.1, 28.2, 43.8 |
| bottom-left | 3.5, 6.0, 11.1 | 13.6, 20.5, 31.2 | 13.6, 20.5, 31.2 |
Directly: `ptbase` upscaled 2× against `8AX` is **mean abs diff 2.05** (max 80),
not byte-identical. They are **the same art at two resolutions**`8AX` is the
full-res copy, `ptbase` the half-res one the element scales back up.
## ⚠️ So do not "fix" this by drawing it
Resolving `pteff05 → 8AX` and drawing it *in addition to* `ptbase` would
**double-draw the background** — an opaque full-screen layer over an identical
one. It would not be visible as a doubling, which is worse: it would silently
cost fill and hide any future change to either layer.
🟡 **The free win, if a port wants it:** use `8AX` at 1:1 and *drop* `ptbase`,
rather than upscaling a 640×360 copy 2×. That is a sharper background from a
texture already in the bundle. Not done here — it is a rendering choice, and
`ptbase`'s element carries the keyframes.
## ❔ Not established
* **Whether the game draws one, the other, or both.** The pixel evidence says
our background matches `8AX`'s art, which `ptbase` also carries — it cannot
separate them. A per-draw capture of the main menu recording texture *base
addresses* would, since the two textures are different sizes.
* The capture is ~4× darker than the render in these patches (4.5 vs 17.7), and
not by a constant ratio. That is a separate colour/gamma question, untouched
here.