re(ui): the focus ring's position is decoded -- a .rat leaf parses as a build
The port needed ptbtneff01.t32's placement and was about to author it from an eyeballed PNG measurement. It does not have to: a `.rat` leaf needs no new reader. Its first 32 bytes have a bundle header's shape -- "RATC", 0x3c declaration-entry size at +4, element count at +20, design 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 alone returns ptbtn01.t32 at (542,162). It reproduces all five buttons. Positions are absolute design-space top-left. The ring rests at (500, 156/236/ 316/396/476) for buttons 1-5 -- a uniform (-42,-6) from each button's own rest, identical in the Japanese bundle. The bright label is a uniform (-7,-7). Two things recorded rather than smoothed over: a leaf's placement DUPLICATES the parent's rather than being relative to it, and the two copies are not always byte-equal (ptbtn04's parent says y=401, its leaf says 402) -- the parent is what compose honours, so the leaf is the source only for elements the parent does not declare, which is exactly the ring. And `screen render --focus` is blind to the ring for the same reason the port's exporter was: el.focused is name-based on top-level elements and neither walks into the leaf.
This commit is contained in:
64
crates/sylpheed-formats/examples/rat_leaf_placement.rs
Normal file
64
crates/sylpheed-formats/examples/rat_leaf_placement.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
//! Does a `.rat` leaf record decode with the same reader as a whole bundle?
|
||||
//!
|
||||
//! The port needs the position of `ptbtneff01.t32`, the focus ring, which is
|
||||
//! declared *inside* the nested `ptbtn0Nf.rat` leaf and is therefore invisible
|
||||
//! to anything that walks only a bundle's top-level elements.
|
||||
//!
|
||||
//! The leaf's first 32 bytes have the same shape as a bundle header --
|
||||
//! `"RATC"`, `0x3c` declaration-entry size at `+4`, element count at `+20`,
|
||||
//! design size at `+24`/`+28` -- so the hypothesis is that `parse_build` reads
|
||||
//! it unchanged. The control is the BASE record `ptbtn0N.rat`, whose single
|
||||
//! element's position is already known independently: the parent screen's
|
||||
//! `screen info` reports `ptbtn01.rat` resting at (542,162).
|
||||
//!
|
||||
//! cargo run -p sylpheed-formats --example rat_leaf_placement -- <GP_TITLE.pak>
|
||||
use sylpheed_formats::{pak, ratc, ui_layout};
|
||||
|
||||
fn main() {
|
||||
let path = std::env::args().nth(1).expect("usage: … <pak>");
|
||||
let ar = pak::PakArchive::open(&path).expect("open pak");
|
||||
let entries: Vec<_> = ar.entries().to_vec();
|
||||
|
||||
for (ei, e) in entries.iter().enumerate() {
|
||||
let Ok(bytes) = ar.read(e) else { continue };
|
||||
let Some(kids) = ratc::parse(&bytes) else { continue };
|
||||
// Only the title-family bundles carry ptbtn records.
|
||||
if !kids.iter().any(|c| c.name == "ptbtn01f.rat") {
|
||||
continue;
|
||||
}
|
||||
println!("=== pak entry {ei}");
|
||||
for c in &kids {
|
||||
if c.kind != "RATC" || !c.name.starts_with("ptbtn") {
|
||||
continue;
|
||||
}
|
||||
let leaf = &bytes[c.offset..c.offset + c.size];
|
||||
match ui_layout::parse_build(leaf) {
|
||||
None => println!(" {:16} …parse_build says no", c.name),
|
||||
Some(b) => {
|
||||
println!(
|
||||
" {:16} {}x{} {} element(s), fallback={}",
|
||||
c.name,
|
||||
b.design_w,
|
||||
b.design_h,
|
||||
b.elements.len(),
|
||||
b.from_fallback
|
||||
);
|
||||
for el in &b.elements {
|
||||
let r = el.rest();
|
||||
println!(
|
||||
" [{}] {:18} pivot ({:4},{:4}) rest ({:5},{:5}) kf {}",
|
||||
el.index,
|
||||
el.name,
|
||||
el.pivot_x,
|
||||
el.pivot_y,
|
||||
r.map(|k| k.x).unwrap_or(-1),
|
||||
r.map(|k| k.y).unwrap_or(-1),
|
||||
el.keyframes.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
@@ -98,13 +98,53 @@ 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.
|
||||
|
||||
## ❔ Not established
|
||||
## ✅ Where the ring is placed — DECODED 2026-08-29, no authoring needed
|
||||
|
||||
* **Where `ptbtneff01.t32` is placed.** The record declares two elements; the
|
||||
per-element placement inside a `.rat` leaf was not decoded here. The capture
|
||||
shows the ring **left of** the label, roughly on the underline's left end.
|
||||
A port should take the offset from the record, and until it is decoded, from
|
||||
the capture.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user