# ✅ A focused button is a **two-element record**, and the second element is the ring **Status:** 🟡 `DECODED for GP_TITLE`, disc-wide check **not yet run**; the sprite contents and the ring are ✅ `MEASURED` against a live capture. Answers the port's *"focus: drawn over the base element, or instead of it?"* ⚠️ The element-count field at `+20` is read here on **`GP_TITLE`'s ten button records only** (five buttons × two language bundles). A disc-wide check — "does `+20` equal the number of resource names the leaf embeds, for every `.rat` leaf on the disc?" — is written and was still running when this was committed. Until it lands, treat `+20` as a **strong local reading, not a decoded field**: the *observable* claim below (the focus record carries a second sprite, `ptbtneff01.t32`, and the port must draw it) rests on the embedded names and the capture, not on that word. **Short answer: it does not matter, and that is not the bug.** The focused sprite covers the base completely, so over-vs-instead is worth at most 12/255 on ~25 pixels. What *is* missing is the focus record's **second element** — a glowing ring that only exists when focused. ## The record A button's base and focused variants are both `.rat` leaves, and they are not the same shape: ```text ptbtn02.rat 164 B strings: RATC, ptbtn02.t32, opt ptbtn02f.rat ptbtn02f.rat 312 B strings: RATC, ptbtneff01.t32, ptbtn02f.t32, opt ptbtn02b.rat ``` The header says so explicitly. A `.rat` leaf begins: ```text +0 "RATC" +16 u32 flags 0x00008110 (base) 0x00008112 (focused) +20 u32 ELEMENT COUNT 1 2 +32 first element name ``` So the focused record declares **two** elements — `ptbtneff01.t32` first, then `ptbtn0Nf.t32` — where the base declares one. Both `f` records of both language bundles read the same way, and the flag word differs only in bit `0x02` alongside the count. ⚠️ The `opt ` link on a `.rat` leaf is **not** simply "my focused variant". The chain runs `ptbtn01.rat → ptbtn01f.rat → ptbtn02.rat → ptbtn02f.rat → ptbtn02b.rat`, i.e. it threads base and focused records together in order. The corpus calls it the focus link and for a *base* record that reading works; do not generalise it to the `f` records. ## The three sprites Decoded with `sylpheed-cli pak textures`, shown over a checkerboard in [`button-base-focus-ring-sprites.png`](../captures/ui-layout/button-base-focus-ring-sprites.png): | sprite | size | what it is | |---|---|---| | `ptbtn02.t32` | 117×43 | the **dim** label, its underline, and a small dot-in-circle at the underline's left end | | `ptbtn02f.t32` | 130×56 | the **same label, bright and glowing** — a complete replacement, 13 px larger in each axis | | `ptbtneff01.t32` | 42×46 | a **glowing ring**, focus only | The small dot-in-circle is on **every** button all the time; the large ring is the focus marker. They are different things and sit side by side on the focused row — visible in [`live-main-menu-options-focused.png`](../captures/title-builds/live-main-menu-options-focused.png) against [`live-main-menu.png`](../captures/title-builds/live-main-menu.png). ## Over or instead? Measured, and the answer is "unobservable" Aligning base and focused by normalised cross-correlation of their alpha masks — the true offset is **(7,7)**, not the (6,6) that centring predicts — | pair | base-visible px | `f` alpha ≥ base alpha there | |---|---|---| | `ptbtn02` / `f` (bundle `b58a0fe6`) | 1 444 | **100.0 %** | | `ptbtn01` / `f` (bundle `b58a0fe6`) | 1 898 | **100.0 %** | | `ptbtn02` / `f` (bundle `a715f485`) | 3 720 | **100.0 %** | ⚠️ **At the centre alignment it reads 78–84 %, and that number is an artefact.** A 1 px shift on strokes this thin manufactures a fifth of a sprite's worth of "the focused art is thinner here". Solve the alignment before trusting a coverage figure. Coverage is not the same as hiding, though, because `f` is not fully opaque everywhere. Compositing both ways over the menu's own background colour: | pair | max channel difference | px > 8/255 | RMSE over the button rect | |---|---|---|---| | `ptbtn02` | 12.5 | 23 | 1.09 | | `ptbtn01` | 12.2 | 27 | 1.08 | | `ptbtn02` (JP) | 12.2 | 27 | 1.05 | So the two hypotheses differ by **~1.1 RMSE inside the button rectangle**, on a couple of dozen pixels — below the ≈ γ 1.4 tone gap ([tone curve](ui-render-tone-curve.md)) and far below any frame-level RMSE this corpus can resolve. **Either choice is defensible; neither is measurable.** Replacing is the cheaper one and is what the file's structure suggests, since `ptbtn0Nf.t32` is a whole label rather than an overlay. ## ✅ Where the ring is placed — DECODED 2026-08-29, no authoring needed This page previously said the per-element placement inside a `.rat` leaf was not decoded and that a consumer should eyeball it off a capture. **That was wrong by omission**: a leaf needs no new reader. Its first 32 bytes have the same shape as a bundle header — `"RATC"`, `0x3c` declaration-entry size at `+4`, element count at `+20`, design size `1280x720` at `+24`/`+28` — so `ui_layout::parse_build` reads it **unchanged**. **The control is the base record**, whose position is known independently: the parent screen reports `ptbtn01.rat` resting at `(542,162)`, and parsing the leaf on its own returns `ptbtn01.t32` at `(542,162)`. It reproduces all five. Positions are **absolute design-space top-left**, not offsets ([`examples/rat_leaf_placement.rs`](../../../crates/sylpheed-formats/examples/rat_leaf_placement.rs)): | button | base | ring `ptbtneff01.t32` | Δ | label `ptbtn0Nf.t32` | Δ | |---|---|---|---|---|---| | `ptbtn01` | (542,162) | **(500,156)** | (−42,−6) | (535,155) | (−7,−7) | | `ptbtn02` | (542,242) | **(500,236)** | (−42,−6) | (535,235) | (−7,−7) | | `ptbtn03` | (542,322) | **(500,316)** | (−42,−6) | (535,315) | (−7,−7) | | `ptbtn04` | (542,402) | **(500,396)** | (−42,−6) | (535,395) | (−7,−7) | | `ptbtn05` | (542,482) | **(500,476)** | (−42,−6) | (535,475) | (−7,−7) | The offset is **uniform**: `(−42,−6)` for the ring and `(−7,−7)` for the label on every button, and identical in the Japanese bundle (pak entry 8). ⚠️ **One 1-unit disagreement, and it is real.** `ptbtn04`'s parent element rests at y **401** while its own leaf says y **402** (and in the JP bundle the leaf says 401 against a parent 401). So a leaf's placement **duplicates** the parent's rather than being relative to it, and the two copies are not always byte-equal. The parent's is what `compose` honours; treat the leaf's as the source only for elements the parent does not declare — which is exactly the ring's case. 🟡 The ring carries **2 keyframes** where the label carries 1, so it animates. What it does between them is not decoded here. ## ⚠️ `screen render --focus` is blind to the ring, and so was this page `el.focused` is name-based on **top-level** elements, and a screen's buttons are `.rat` records whose focused twin is not itself a top-level element — so rendering build 5 with and without `--focus` produces an identical image. The reference renderer has the same blind spot the port reported, for the same reason: neither walks into the leaf. Fixing it is a renderer change, not a format question; the format is decoded above. ## ❔ Not established * **`ptbtn02b.t32`** — a third variant, `b`, exists for button 02 only, same size as the base. Not seen on any capture. Not chased. * Whether a **non-title** archive uses the same two-element convention. Checked on `GP_TITLE` only.