formats: what opt links, measured - a record-to-record reference, focus is one use of it
Two readings were on record and both were wrong in different directions: the structure doc called it "normal state -> focused state" from a single example, and the backlog called it "refuted as focus; unexplained otherwise". Classified every link reachable from a declaration table: 1467 links, and ALL 1467 resolve to a RATC child of their own bundle, ALL are .rat -> .rat, none dangle and none self-link. 1076 (73%) match the <stem>f focus pattern; the other 391 are chains between effect records - px_bunk_eff01 -> pjex_eff -> pjex_eff07, pveff01 -> pjeff02 -> pjeff21 - which also explains why only 227 targets are themselves declared elements: the middle of a chain is, the end is not. So `opt ` is a record-to-record reference within the bundle, and focus is its commonest use rather than its meaning. Coverage is stated rather than glossed: the bundles hold 18718 raw `opt ` tags against the 1467 classified, because opt_link reads the first tag of a DECLARED element's record. Roughly 92% of occurrences sit deeper in the chains (or are byte coincidences - the scan is unaligned) and are untested. The numbers are asserted so the answer cannot drift back into an anecdote. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
155
crates/sylpheed-formats/tests/ui_opt_link_disc.rs
Normal file
155
crates/sylpheed-formats/tests/ui_opt_link_disc.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
//! What does the `.rat` record's `opt ` link actually point at?
|
||||
//!
|
||||
//! `ui-rat-layout.md` reads it as *normal state → focused state*, from one
|
||||
//! example: `pgpbtn00.rat` links to `pgpbtn00f.rat`. The backlog carries the
|
||||
//! opposite verdict — "refuted as focus; unexplained otherwise" — with no number
|
||||
//! behind either. This settles it by classifying **every** `opt ` link on the
|
||||
//! disc, so the answer is a distribution rather than an anecdote.
|
||||
//!
|
||||
//! Four questions per link, in order of how much they would explain:
|
||||
//! 1. is the target `<source stem>f.<ext>` — the focus pattern?
|
||||
//! 2. is the target another element of the SAME build?
|
||||
//! 3. is it a RATC child of the bundle (a resource rather than an element)?
|
||||
//! 4. otherwise: what do the leftovers look like?
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use sylpheed_formats::{pak::PakArchive, ratc, ui_layout};
|
||||
|
||||
fn disc_root() -> Option<PathBuf> {
|
||||
if let Ok(p) = std::env::var("SYLPHEED_DISC") {
|
||||
let p = PathBuf::from(p);
|
||||
if p.join("dat").is_dir() {
|
||||
return Some(p);
|
||||
}
|
||||
}
|
||||
let default = Path::new(
|
||||
"/home/fabi/RE - Project Sylpheed/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja)",
|
||||
);
|
||||
if default.join("dat").is_dir() {
|
||||
return Some(default.to_path_buf());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn for_each_build(root: &Path, mut f: impl FnMut(&str, &[u8])) {
|
||||
let mut paks: Vec<PathBuf> = std::fs::read_dir(root.join("dat"))
|
||||
.expect("dat/")
|
||||
.flatten()
|
||||
.map(|e| e.path())
|
||||
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak"))
|
||||
.collect();
|
||||
paks.sort();
|
||||
for p in &paks {
|
||||
let name = p.file_name().unwrap().to_string_lossy().to_string();
|
||||
let Ok(arc) = PakArchive::open(p) else { continue };
|
||||
for e in arc.entries() {
|
||||
let Ok(bytes) = arc.read(e) else { continue };
|
||||
if ratc::is_ratc(&bytes) {
|
||||
f(&name, &bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_opt_link_on_the_disc_is_classified() {
|
||||
let Some(root) = disc_root() else {
|
||||
eprintln!("SKIP: extracted disc not found (set SYLPHEED_DISC to enable)");
|
||||
return;
|
||||
};
|
||||
|
||||
let (mut links, mut focus_pattern, mut same_build, mut ratc_child) = (0usize, 0, 0, 0);
|
||||
// `opt_link` returns the FIRST `opt ` tag in a record. If records can carry
|
||||
// several, every count here understates - so count the tags themselves in
|
||||
// the raw bundle rather than trusting the parser's one-per-record view.
|
||||
let mut opt_tags = 0usize;
|
||||
let mut self_link = 0usize;
|
||||
let mut ext_pairs: HashMap<(String, String), usize> = HashMap::new();
|
||||
let mut leftovers: Vec<String> = Vec::new();
|
||||
|
||||
for_each_build(&root, |pak, bytes| {
|
||||
let Some(build) = ui_layout::parse_build(bytes) else {
|
||||
return;
|
||||
};
|
||||
if build.from_fallback {
|
||||
return;
|
||||
}
|
||||
let names: HashSet<String> = build
|
||||
.elements
|
||||
.iter()
|
||||
.map(|e| e.name.to_ascii_lowercase())
|
||||
.collect();
|
||||
let kids: HashSet<String> = ratc::parse(bytes)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|c| c.name.to_ascii_lowercase())
|
||||
.collect();
|
||||
opt_tags += bytes.windows(4).filter(|w| *w == b"opt ").count();
|
||||
for el in &build.elements {
|
||||
let Some(target) = el.focus_link.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
links += 1;
|
||||
let src = el.name.to_ascii_lowercase();
|
||||
let tgt = target.to_ascii_lowercase();
|
||||
let se = src.rsplit_once('.').map(|(_, e)| e.to_string()).unwrap_or_default();
|
||||
let te = tgt.rsplit_once('.').map(|(_, e)| e.to_string()).unwrap_or_default();
|
||||
*ext_pairs.entry((se, te)).or_default() += 1;
|
||||
if tgt == src {
|
||||
self_link += 1;
|
||||
}
|
||||
let is_focus = src
|
||||
.rsplit_once('.')
|
||||
.map(|(stem, ext)| format!("{stem}f.{ext}") == tgt)
|
||||
.unwrap_or(false);
|
||||
if is_focus {
|
||||
focus_pattern += 1;
|
||||
}
|
||||
if names.contains(&tgt) {
|
||||
same_build += 1;
|
||||
}
|
||||
if kids.contains(&tgt) {
|
||||
ratc_child += 1;
|
||||
}
|
||||
if !is_focus && leftovers.len() < 14 {
|
||||
leftovers.push(format!(
|
||||
"{pak}: {} -> {}{}",
|
||||
el.name,
|
||||
target,
|
||||
if names.contains(&tgt) { " [declared element]" } else { "" }
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
eprintln!("opt links on the disc: {links} raw `opt ` tags in the bundles: {opt_tags}");
|
||||
eprintln!(" target is <stem>f.<ext> (the focus pattern): {focus_pattern}");
|
||||
eprintln!(" target is another element of the same build: {same_build}");
|
||||
eprintln!(" target is a RATC child of the bundle: {ratc_child}");
|
||||
eprintln!(" target is the element ITSELF: {self_link}");
|
||||
let mut ep: Vec<_> = ext_pairs.iter().collect();
|
||||
ep.sort_by_key(|(_, n)| std::cmp::Reverse(**n));
|
||||
eprintln!(" source-ext -> target-ext: {:?}", &ep[..ep.len().min(8)]);
|
||||
for l in &leftovers {
|
||||
eprintln!(" NOT the f-pattern: {l}");
|
||||
}
|
||||
|
||||
assert!(links > 0, "no opt links found — the sweep is broken");
|
||||
|
||||
// MEASURED 2026-08-24 and asserted, because both prior readings were wrong
|
||||
// in different directions: `opt ` is not "the focused state" (that fits 73 %
|
||||
// of links, not all of them) and it is not "unexplained" either — EVERY
|
||||
// link resolves to a RATC child of its own bundle, and every one is
|
||||
// .rat -> .rat.
|
||||
assert_eq!(links, 1467, "the disc has 1467 opt links");
|
||||
assert_eq!(ratc_child, links, "an opt link does not resolve to a RATC child");
|
||||
assert_eq!(
|
||||
ext_pairs.get(&("rat".to_string(), "rat".to_string())).copied(),
|
||||
Some(links),
|
||||
"an opt link points at something other than a .rat record"
|
||||
);
|
||||
assert_eq!(self_link, 0, "a record links to itself");
|
||||
assert_eq!(focus_pattern, 1076, "the f-pattern share moved");
|
||||
}
|
||||
@@ -452,3 +452,47 @@ that a menu draws for its buttons.
|
||||
**Still open:** whether the focused state is marked anywhere *else* — the `.rat`
|
||||
record, the RATC child stream, or (as with the paint order) only in the game's
|
||||
code. This closes the declaration table, not the question.
|
||||
|
||||
## 🟡 What `opt ` links — measured over the whole disc (2026-08-24)
|
||||
|
||||
Two readings were on record and both were wrong in different directions: this
|
||||
file called it *normal state → focused state* from a single example
|
||||
(`pgpbtn00.rat → pgpbtn00f.rat`), and the backlog called it "refuted as focus;
|
||||
unexplained otherwise". Classifying **every** link reachable from a declaration
|
||||
table (`tests/ui_opt_link_disc.rs`) gives a distribution instead:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `opt ` links classified | **1 467** |
|
||||
| resolve to a **RATC child of the same bundle** | **1 467 — all of them** |
|
||||
| `.rat` → `.rat` | **1 467 — all of them** |
|
||||
| match the focus pattern `<stem>f.<ext>` | **1 076 (73 %)** |
|
||||
| whose target is *also* a declared element | 227 |
|
||||
| link to themselves | **0** |
|
||||
|
||||
So `opt ` is a **record→record reference within the bundle**: a record naming
|
||||
another record it uses. It never dangles, never points at a sprite, and never
|
||||
points at itself. Focus is the commonest *use* of that mechanism, not its
|
||||
meaning.
|
||||
|
||||
**What the other 27 % are: chains.** The non-`f` targets are effect records
|
||||
referring to further effect records, and following them shows depth:
|
||||
|
||||
```
|
||||
px_bunk_eff01.rat → pjex_eff.rat → pjex_eff07.rat
|
||||
px_bunk_eff01.rat → pjnet_bg.rat → pjnet_loop1.rat
|
||||
pveff01.rat → pjeff02.rat → pjeff21.rat
|
||||
pjeff03.rat → pjeff03_sub.rat
|
||||
```
|
||||
|
||||
Note the middle name in each chain *is* a declared element while the third is
|
||||
not — which is exactly why only 227 targets are elements.
|
||||
|
||||
⚠️ **Coverage, stated because it bounds all of the above.** The bundles contain
|
||||
**18 718** raw `opt ` byte-tags against the **1 467** links classified here.
|
||||
`opt_link()` reads the **first** tag of the record belonging to a **declared
|
||||
element**, so roughly 92 % of occurrences sit on records deeper in the chain (or
|
||||
are byte coincidences in binary data — the scan is unaligned). What those say is
|
||||
untested. The claims above are about the links a screen's element table can
|
||||
reach, which is what a compositor follows; they are not a statement about every
|
||||
`opt ` in the file.
|
||||
|
||||
Reference in New Issue
Block a user