From 7eeae3006a7bb92ae31ba8d2b402891f3a31f070 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Sat, 29 Aug 2026 08:46:53 +0000 Subject: [PATCH] re(ui): the focus ring SPINS, the game draws it, and the leaf owns the f record Three things, all from parsing ptbtn0Nf.rat as a build. 1. THE RING SPINS. Its two keyframes differ in exactly one field: rotation_deg ramps 0 -> 360 with position, scale, alpha and tint all constant. A spin in place, the same shape as the GP_BUNK example already recorded. 2. THE ORACLE CONFIRMS THE GAME RENDERS IT. In the OPTIONS-focused capture the ring's bright head sits in a completely different angular position from the sprite's own -- caught mid-spin. This is a SECOND independent confirmation that rotation_deg is drawn, now on a different screen and a different element from the ptloop sweeps, and it raises rotation's priority: it is not a title-only concern that sits off-screen at rest, it is the main menu's focus marker. NO ANGLE IS QUOTED. A brightest-region centroid says ~250 deg, but the control refuses that precision -- rotating the sprite by a known 30/90/180/270 and re-measuring gives errors up to 19.8 deg. What survives the error bar is that a <=20 deg error cannot manufacture a ~250 deg displacement. 3. WHICH PLACEMENT WINS -- correcting this page's own earlier caveat, which said to use the leaf only for elements the parent does not declare. Right for a BASE record, wrong for an f record: the parent declares NO element for ptbtn0Nf.rat at all (zero of build 5's 16), so the f record's placement comes from its leaf for BOTH elements, label included. The label's (-7,-7) is load-bearing -- the f sprite is 13px larger per axis and -7 keeps them concentric (535+96/2 = 583 vs 542+83/2 = 583.5). Corroborated against the oracle: the focused-minus-unfocused region is x 505..703, and the leaf predicts a right edge near 707 where the parent reading predicts 714. Also exposes UiBuild::records (name -> (offset, size) of a nested .rat leaf). Nested records were parsed into a PRIVATE map, so a consumer holding a UiBuild could not locate a leaf's bytes at all -- which is exactly what blocked the port from reaching the ring. --- .../examples/rat_leaf_placement.rs | 7 ++ crates/sylpheed-formats/src/ui_layout.rs | 15 ++++ .../ui-layout/focus-ring-oracle-vs-sprite.png | Bin 0 -> 14823 bytes docs/re/structures/ui-button-focus-record.md | 68 +++++++++++++++--- 4 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 docs/re/captures/ui-layout/focus-ring-oracle-vs-sprite.png diff --git a/crates/sylpheed-formats/examples/rat_leaf_placement.rs b/crates/sylpheed-formats/examples/rat_leaf_placement.rs index e550068..9bac6e2 100644 --- a/crates/sylpheed-formats/examples/rat_leaf_placement.rs +++ b/crates/sylpheed-formats/examples/rat_leaf_placement.rs @@ -55,6 +55,13 @@ fn main() { r.map(|k| k.y).unwrap_or(-1), el.keyframes.len() ); + for (i, k) in el.keyframes.iter().enumerate() { + println!( + " kf{i} t={:?} pos=({},{}) scale={}%,{}% a={} rot={} tint={:#010x}", + k.time, k.x, k.y, k.scale_x, k.scale_y, + k.fade >> 24, k.rotation_deg, k.tint + ); + } } } } diff --git a/crates/sylpheed-formats/src/ui_layout.rs b/crates/sylpheed-formats/src/ui_layout.rs index 194fca3..1154a09 100644 --- a/crates/sylpheed-formats/src/ui_layout.rs +++ b/crates/sylpheed-formats/src/ui_layout.rs @@ -322,6 +322,20 @@ pub struct UiBuild { pub elements: Vec, /// Sprite name → (offset, size) of its `T8aD` child within the bundle. pub sprites: HashMap, + /// Record name → (offset, size) of its nested `.rat` **leaf** within the + /// bundle, e.g. `ptbtn01f.rat`. + /// + /// Exposed because a leaf is where a focused button's extra elements live — + /// `ptbtn0Nf.rat` declares the focus ring `ptbtneff01.t32` **and** the bright + /// label, and the parent bundle declares no element for the `f` record at + /// all. A consumer that walks only top-level elements cannot see either. + /// + /// A leaf is itself readable by [`parse_build`]: its first 32 bytes have the + /// same shape as a bundle header, so + /// `parse_build(&bundle[off..off + size])` returns its elements with + /// absolute design-space placements. See + /// `docs/re/structures/ui-button-focus-record.md`. + pub records: HashMap, /// A guessed context from the sprite naming (e.g. `"tutorial"`), if any. pub context_hint: Option, /// True when the declaration table was unusable and the build was recovered @@ -599,6 +613,7 @@ pub fn parse_build(bundle: &[u8]) -> Option { design_h, elements, sprites, + records, context_hint, from_fallback, placement_order, diff --git a/docs/re/captures/ui-layout/focus-ring-oracle-vs-sprite.png b/docs/re/captures/ui-layout/focus-ring-oracle-vs-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..1c6c4fd6f43f461d0035f5127e4b2500d770c960 GIT binary patch literal 14823 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