diff --git a/crates/sylpheed-formats/examples/static_record_census.rs b/crates/sylpheed-formats/examples/static_record_census.rs new file mode 100644 index 00000000..de76cf0c --- /dev/null +++ b/crates/sylpheed-formats/examples/static_record_census.rs @@ -0,0 +1,51 @@ +//! Are the records with `max t == 0` UNTIMED, or timed with every pose at 0? +//! +//! I wrote that 1 530 records are "questions never asked" — no timed keyframe, so +//! `max t` is 0 by absence. `sylpheed-port` now says zero records on this disc are +//! like that: all 1 530 are timed, every pose at t = 0, so the question is +//! well-formed and "not exact" is a real answer. This checks it. +//! +//! cargo run -p sylpheed-formats --example static_record_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 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(); + let (mut untimed, mut all_zero, mut nonzero) = (0usize, 0usize, 0usize); + for p in &paks { + let Ok(ar) = PakArchive::open(p) else { continue }; + for e in ar.entries() { + let Ok(by) = ar.read(e) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { continue }; + for (_, (off, size)) in &b.records { + let rec = &by[*off..(*off + *size).min(by.len())]; + if rec.len() < 0x10 || &rec[0..4] != b"RATC" { + continue; + } + let Some(leaf) = ui_layout::parse_build(rec) else { continue }; + let times: Vec = leaf + .elements + .iter() + .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) + .collect(); + if times.is_empty() { + untimed += 1; + } else if times.iter().max() == Some(&0) { + all_zero += 1; + } else { + nonzero += 1; + } + } + } + } + println!("nested RATC records that parse: {}", untimed + all_zero + nonzero); + println!(" NO timed keyframe at all : {untimed}"); + println!(" timed, every pose at t == 0 : {all_zero}"); + println!(" timed, largest t > 0 : {nonzero}"); +} diff --git a/docs/re/METHOD.md b/docs/re/METHOD.md index 8f3011df..81b32810 100644 --- a/docs/re/METHOD.md +++ b/docs/re/METHOD.md @@ -3242,11 +3242,15 @@ Two agents, one statistic, and the disagreement was **entirely in the denominato `1643/1781 = 92.3 %` against `1643/3311 = 49.6 %`. **Neither of us was wrong about the disc.** -🔴 **My explanation of the gap was wrong too.** I said my scan "requires a timed -keyframe". It does not — `.max()` over the keyframe times returns `Some(0)`, not -`None`, for a record whose keyframes all sit at time 0, so those never left my -denominator. 1 530 records where *"does the word equal the largest keyframe time?"* -has **no content** were counted as failures **by construction**. +🔴 **Two wrong explanations were offered for the gap before the right one.** Mine +first: I said my scan "requires a timed keyframe" — it does not, because `.max()` +returns `Some(0)` rather than `None`. Then `sylpheed-port`'s reconciliation, which +I adopted: that the extra 1 530 are *"questions never asked"* with no content. +**Also wrong, and they corrected it themselves.** Measured: **0** records on this +disc lack a timed keyframe; all 1 530 are **static** — timed, every pose at t = 0. +A static record still declares a cycle length, so a nonzero `+0x08` against a +largest time of 0 is a **real disagreement, not an absent one**, and the 49.6 % is +a defensible statistic over a different population rather than an artefact. 📌 **`sylpheed-port`'s diagnosis of why it stayed invisible is the transferable part: the numerator agreed to the unit.** We both looked at **1 643** and neither @@ -3258,3 +3262,20 @@ where the question is meaningful*, not *of nested records* — quoted bare on bo sides for two days, including into a shipped doc comment. **A population-scoped statistic reported without its population is the same shape as a negative reported without its reach**, and this corpus already had a rule for the second. + +### Three rounds, one number, and every disagreement about interpretation + +The sequence is worth keeping as a shape. I corrected an argument; they corrected +my denominator; they then corrected their own characterisation of what was in it. +**Every step was checkable in one scan**, and each of us stated an interpretation +confidently while only the number had been measured. + +📌 **The numbers never disagreed** — 1 643, 3 311, 1 781, 0 % — through all three +rounds. Every disagreement was about **what they were counting**. That is the +cheaper failure and I suspect the more common one: agreeing figures feel like +agreement, and neither party re-examines a quantity they both accept. + +⚠️ Nothing the port depends on moved at any point: `+0x08` matches exactly wherever +the largest time is nonzero, `+0x04` is 0 % under either denominator, and the offset +stands on both scans. **Three rounds of correction over an interpretation that was +never load-bearing** — which is also why it was safe to keep pulling. diff --git a/docs/re/structures/ui-record-loop-length.md b/docs/re/structures/ui-record-loop-length.md index 9bcfc707..253f69e8 100644 --- a/docs/re/structures/ui-record-loop-length.md +++ b/docs/re/structures/ui-record-loop-length.md @@ -22,27 +22,36 @@ disc-wide (1 781 records, 0 violations). > a secondary statistic**: `+0x08` equals the largest keyframe time *exactly* in > half the records and `+0x04` in **none**. No unrelated word reproduces that. > -> 🔴 **AND MY EXPLANATION OF THE 49.6 % WAS WRONG.** I wrote that my scan "requires -> a timed keyframe", so the population differed. It does not: `.max()` over the -> keyframe times returns `Some(0)` — not `None` — for a record whose keyframes are -> all at time 0, so those never left my denominator. `sylpheed-port` reconciled it -> exactly and I reproduced it: +> 📌 **THE TWO PERCENTAGES ARE TWO POPULATIONS, and neither corrects the other.** +> Settled after three rounds, with every number reproduced from both readers: > > ``` -> 1643 / 1781 = 92.3 % records where max t > 0 — the question is meaningful -> 1643 / 3311 = 49.6 % plus 1 530 records where max t == 0 +> nested RATC records that parse : 3 311 +> NO timed keyframe at all : 0 +> timed, every pose at t == 0 : 1 530 <- STATIC records +> timed, largest t > 0 : 1 781 <- animated records > ``` > -> **Same numerator.** The 1 530 extra are records where *"does the word equal the -> largest keyframe time?"* has no content — `max t` is 0, so each counts as -> not-exact **by construction**. 49.6 % is not a weaker 92.3 %; it is 1 643 -> successes over a denominator containing 1 530 questions that were never asked. +> ``` +> 1643 / 1781 = 92.3 % of ANIMATED records +> 1643 / 3311 = 49.6 % of ALL nested records, static ones included +> ``` > -> ✅ **The conclusion is untouched**: `+0x04` is **0 %** under either denominator, -> so the offset stands on both scans. +> **Same numerator.** Both figures are defensible; they answer different questions, +> and each needs its population attached. > -> ⚠️ **And 92.3 % needs the qualifier it has never carried here**: it is *of the -> records where the question is meaningful*, not *of nested records*. The consequence for the `PRESS Ⓐ` plate +> 🔴 **Two wrong explanations were given for the gap before this one, and both were +> mine to carry.** First I wrote that my scan "requires a timed keyframe" — it does +> not, because `.max()` returns `Some(0)` rather than `None`. Then, adopting +> `sylpheed-port`'s reconciliation, I wrote that the 1 530 are *"questions never +> asked"* with *"no content"* — **also wrong, and they corrected it themselves**: +> **zero records on this disc lack a timed keyframe.** The 1 530 are **static +> records**, timed with every pose at 0. A static record still declares a cycle +> length, so a nonzero `+0x08` against a largest time of 0 is a **real +> disagreement, not an absent one**. +> +> ⚠️ **92.3 % needs the qualifier it has never carried here**: it is *of animated +> records*, not *of nested records*. is then tested against the corpus's existing measurements of the running game, which it passes and the previous reading fails.