re: enumerate an element's records; focus_link is a misnomer
Adds examples/element_records.rs, which lists leaf AND focus_link records for an element, plus a disc-wide census. Built because I claimed alpha 80 was undeclared after reading one of ptbtn00's two records -- and focus_link was already parsed, with ui_layout.rs:424 already documenting the focus record. The format was known and I did not consult it. Census: 1467 of 15493 elements (9.5%) across 815 builds carry a second record whose keyframes are invisible to a by-name leaf lookup. Refutes our own parser's description of the field. It is documented as "the focused state of a button", but GP_TITLE has pgloading_loop1 -> loop3 -> loop4, a chain of three loop animations, and ptloop01 -> ptloop02, the two sweeps. Neither is a focused state. Naming defect only -- behaviour is right where it is read -- so not renamed here. 🟡 Notes a better candidate for why the two sweeps share one indices=8 draw: they are linked, not merely co-textured. Testable on the pgloading chain, which needs a loading-screen capture I do not have. Named, not claimed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
This commit is contained in:
73
crates/sylpheed-formats/examples/element_records.rs
Normal file
73
crates/sylpheed-formats/examples/element_records.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
//! Every record reachable from an element — leaf **and** `focus_link` — plus a
|
||||
//! disc-wide census of how many elements have more than one.
|
||||
//!
|
||||
//! ⚠️ WHY. I claimed `ptbtn00f`'s peak alpha of 80 was undeclared, having read
|
||||
//! `ptbtn00.rat` (the leaf, flat 255) and stopped. The pulse is in
|
||||
//! `ptbtn00f.rat`, the focus record. `focus_link` was already parsed and
|
||||
//! `ui_layout.rs` already documented it: the format was known and I did not
|
||||
//! consult it. An absence claim is only as good as its enumeration, so this
|
||||
//! enumerates rather than asking the reader to remember.
|
||||
//!
|
||||
//! cargo run -p sylpheed-formats --example element_records -- GP_TITLE ptbtn00
|
||||
//! cargo run -p sylpheed-formats --example element_records -- --census
|
||||
use sylpheed_formats::{pak::PakArchive, ui_layout};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC"));
|
||||
let argv: Vec<String> = std::env::args().skip(1).collect();
|
||||
|
||||
if argv.iter().any(|a| a == "--census") {
|
||||
let (mut els, mut linked, mut screens_with) = (0usize, 0usize, 0usize);
|
||||
let mut paks: Vec<_> = std::fs::read_dir(root.join("dat")).expect("dat")
|
||||
.filter_map(|e| e.ok()).map(|e| e.path())
|
||||
.filter(|p| p.extension().map(|x| x == "pak").unwrap_or(false)).collect();
|
||||
paks.sort();
|
||||
for p in &paks {
|
||||
let Ok(ar) = PakArchive::open(p) else { continue };
|
||||
for ent in ar.entries() {
|
||||
let Ok(by) = ar.read(ent) else { continue };
|
||||
let Some(b) = ui_layout::parse_build(&by) else { continue };
|
||||
let n = b.elements.iter().filter(|e| e.focus_link.is_some()).count();
|
||||
els += b.elements.len(); linked += n;
|
||||
if n > 0 { screens_with += 1 }
|
||||
}
|
||||
}
|
||||
println!("elements disc-wide : {els}");
|
||||
println!("with a focus_link record : {linked} ({:.1}%)", 100.0*linked as f64/els as f64);
|
||||
println!("builds containing at least 1: {screens_with}");
|
||||
println!("\nEach of those carries a SECOND record whose keyframes are invisible");
|
||||
println!("to anyone who looks up the leaf by name and stops.");
|
||||
return;
|
||||
}
|
||||
|
||||
let pak = argv.first().cloned().unwrap_or_else(|| "GP_TITLE".into());
|
||||
let want = argv.get(1).cloned();
|
||||
let ar = PakArchive::open(root.join(format!("dat/{pak}.pak"))).expect("pak");
|
||||
for (i, ent) in ar.entries().iter().enumerate() {
|
||||
let Ok(by) = ar.read(ent) else { continue };
|
||||
let Some(b) = ui_layout::parse_build(&by) else { continue };
|
||||
for el in &b.elements {
|
||||
if let Some(w) = &want { if !el.name.starts_with(w.as_str()) { continue } }
|
||||
if el.focus_link.is_none() && want.is_none() { continue }
|
||||
println!("entry {i}: {}", el.name);
|
||||
let stem = el.name.trim_end_matches(".rat");
|
||||
for (tag, rec) in [("leaf", format!("{stem}.rat")),
|
||||
("focus", el.focus_link.clone().unwrap_or_default())] {
|
||||
if rec.is_empty() { continue }
|
||||
match b.records.get(rec.as_str()) {
|
||||
Some(&(lo, ls)) => {
|
||||
let bytes = &by[lo..(lo + ls).min(by.len())];
|
||||
let loop_u = ui_layout::loop_length_units(bytes);
|
||||
let kf: Vec<String> = ui_layout::parse_build(bytes).map(|lb| lb.elements.iter()
|
||||
.map(|e| format!("{} [{} keys, peak a{}]", e.name, e.keyframes.len(),
|
||||
e.keyframes.iter().map(|k| k.fade >> 24).max().unwrap_or(0))).collect())
|
||||
.unwrap_or_default();
|
||||
println!(" {tag:<6} {rec:<20} loop {:?} {}", loop_u, kf.join(", "));
|
||||
}
|
||||
None => println!(" {tag:<6} {rec:<20} (no such record)"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
docs/re/element-records-enumerated.md
Normal file
75
docs/re/element-records-enumerated.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Enumerating an element's records — and `focus_link` is a misnomer
|
||||
|
||||
**Question:** how many records does a UI element actually have, and does my
|
||||
tooling see them all?
|
||||
|
||||
**What the human looks at:** run
|
||||
`cargo run -p sylpheed-formats --example element_records -- GP_TITLE ptbtn00`.
|
||||
Pass = **two** records listed. Fail = one.
|
||||
|
||||
**What this does NOT cover:** F5's code route, the `0x70000` bits.
|
||||
|
||||
**Instrument:** ⟨disc⟩, every `.pak`.
|
||||
|
||||
## Why
|
||||
|
||||
I claimed `ptbtn00f`'s α80 was undeclared, having read `ptbtn00.rat` — the leaf,
|
||||
flat α255 — and stopped. The pulse is in `ptbtn00f.rat`, reached by `focus_link`.
|
||||
**`focus_link` was already parsed, and `ui_layout.rs:424` already documented that
|
||||
`ptbtn0Nf.rat` carries the focus ring.** The format was known; I did not consult
|
||||
it. So this enumerates rather than asking the next reader to remember:
|
||||
|
||||
```
|
||||
entry 2: ptbtn00.rat
|
||||
leaf ptbtn00.rat loop 120 ptbtn00.t32 [1 keys, peak a255]
|
||||
focus ptbtn00f.rat loop 120 ptbtn00f.t32 [8 keys, peak a80]
|
||||
```
|
||||
|
||||
The fact I missed is the second line.
|
||||
|
||||
## ✅ Census — how much of the corpus this exposes
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| elements disc-wide | **15 493** |
|
||||
| carrying a second record | **1 467 (9.5 %)** |
|
||||
| builds containing at least one | **815** |
|
||||
|
||||
Every one of those has keyframes invisible to anyone who looks a leaf up by name
|
||||
and stops.
|
||||
|
||||
## 🔴 `focus_link` is the wrong name for at least some of its uses
|
||||
|
||||
The parser calls it *"`opt ` link to another record — the focused state of a
|
||||
button"*. On `GP_TITLE` that description fails twice:
|
||||
|
||||
```
|
||||
pgloading_loop1 --> pgloading_loop3 --> pgloading_loop4
|
||||
(300 units) (120 units) (360 units)
|
||||
ptloop01 --> ptloop02
|
||||
(600 units) (720 units)
|
||||
```
|
||||
|
||||
A **chain of three loop animations**, and **the two sweeps**. Neither is a
|
||||
focused button state. The field links records; "focus" is one thing it is used
|
||||
for, not what it means.
|
||||
|
||||
Recorded as a naming defect in our own parser, not corrected here — the field's
|
||||
*behaviour* is right everywhere it is read, and renaming it touches every caller.
|
||||
|
||||
## 🟡 A candidate mechanism for the batched draw
|
||||
|
||||
The two sweeps arrive in a **single `indices=8` draw**
|
||||
([`f6-unit11`](f6-unit11-pteff03a-IS-drawn.md)), which I have been treating as
|
||||
"they share a texture page". They are also **linked** — `ptloop01 → ptloop02`.
|
||||
That is a better candidate explanation, and it is testable: another linked pair
|
||||
should batch too.
|
||||
|
||||
**Not tested.** The obvious subject is the `pgloading` chain, and I have no
|
||||
loading-screen capture. Recorded as 🟡 with the experiment named, not as a
|
||||
finding.
|
||||
|
||||
## Reach
|
||||
|
||||
The census counts `focus_link` only. If a record can be reached by any *other*
|
||||
route, this enumeration is still incomplete and the census is a lower bound.
|
||||
Reference in New Issue
Block a user