From 293040b61f73581b774d90d90018d0f7035b3903 Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Mon, 31 Aug 2026 04:03:32 +0000 Subject: [PATCH] re: verify that static records' declared cycles are visually inert in GP_TITLE sylpheed-port turned my point -- that a static record still declares a cycle, so a nonzero +0x08 against a largest time of 0 is a real disagreement -- into a check on the screens they ship. Re-derived from my reader and it reproduces exactly: 65 nested records in GP_TITLE, 20 declaring a cycle with every pose at t == 0, and 0 of those with any element carrying more than one pose. A record whose elements each hold a single pose renders identically looped or held, so holding them still is correct and now measured. It includes ptbtn11/12/13, EXTRAS' buttons in both language entries, each declaring 120 units with one pose per element. Had any carried two poses, a menu button the disc says animates would have been held still on the one submenu the port's P5 gate walks. Reach: GP_TITLE only; 1530 static records exist disc-wide against the 20 here. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v --- .../examples/static_cycle_inert.rs | 52 +++++++++++++++++++ docs/re/data/static-cycles-are-inert.txt | 28 ++++++++++ 2 files changed, 80 insertions(+) create mode 100644 crates/sylpheed-formats/examples/static_cycle_inert.rs create mode 100644 docs/re/data/static-cycles-are-inert.txt diff --git a/crates/sylpheed-formats/examples/static_cycle_inert.rs b/crates/sylpheed-formats/examples/static_cycle_inert.rs new file mode 100644 index 00000000..c74c44f8 --- /dev/null +++ b/crates/sylpheed-formats/examples/static_cycle_inert.rs @@ -0,0 +1,52 @@ +//! Do `GP_TITLE` records that declare a cycle while sitting at t = 0 actually move? +//! +//! A static record still declares a cycle length, so a nonzero `+0x08` against a +//! largest keyframe time of 0 is a real disagreement. `sylpheed-port` turned that +//! into a check on the screens they ship: 20 such records in GP_TITLE, and none +//! with any element carrying more than one pose — so the declared cycle is +//! visually inert and holding them still is correct. +//! +//! This re-derives it. If any record had a multi-pose element, the port would be +//! holding something the disc says animates. +//! +//! cargo run -p sylpheed-formats --example static_cycle_inert +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 ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE"); + let (mut total, mut static_cycle, mut multipose) = (0usize, 0usize, 0usize); + for e in ar.entries() { + let Ok(by) = ar.read(e) else { continue }; + let Some(b) = ui_layout::parse_build(&by) else { continue }; + for (name, (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 }; + total += 1; + let times: Vec = leaf + .elements + .iter() + .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) + .collect(); + let cycle = ui_layout::loop_length_units(rec).unwrap_or(0); + if times.is_empty() || times.iter().max() != Some(&0) || cycle == 0 { + continue; + } + static_cycle += 1; + let worst = leaf.elements.iter().map(|el| el.keyframes.len()).max().unwrap_or(0); + if worst > 1 { + multipose += 1; + println!(" 🔴 {name} declares {cycle} units and has an element with {worst} poses"); + } else if name.starts_with("ptbtn1") { + println!(" {name:16} declares {cycle} units, max poses/element {worst}"); + } + } + } + println!("\nnested records in GP_TITLE : {total}"); + println!("declaring a cycle with every pose at t == 0 : {static_cycle}"); + println!("...of those, any element with MORE THAN ONE pose : {multipose}"); +} diff --git a/docs/re/data/static-cycles-are-inert.txt b/docs/re/data/static-cycles-are-inert.txt new file mode 100644 index 00000000..244205fc --- /dev/null +++ b/docs/re/data/static-cycles-are-inert.txt @@ -0,0 +1,28 @@ +# Do GP_TITLE records that declare a cycle while sitting at t = 0 actually move? +# ✅ NO -- the declared cycle is visually inert on all 20. 2026-08-31. +# +# WHY THIS EXISTS. A static record still declares a cycle length, so a nonzero +# +0x08 against a largest keyframe time of 0 is a REAL disagreement, not an absent +# one (ui-record-loop-length.md). sylpheed-port turned that into a check on the +# screens they ship; this re-derives it from my reader. +# +# nested records in GP_TITLE : 65 +# declaring a cycle with every pose at t == 0 : 20 +# ...of those, any element with MORE THAN ONE pose : 0 +# +# ✅ A record whose elements each hold a SINGLE pose renders identically looped or +# held -- there is nothing to move between. So holding them still is correct, and +# it is now measured rather than assumed. +# +# ⚠️ IT INCLUDES ptbtn11 / ptbtn12 / ptbtn13 -- EXTRAS' own buttons, in both +# language entries (6 and 9) -- each declaring a 120-unit cycle with one pose per +# element. Had any carried two poses, a menu button the disc says animates would +# have been held still, on the one submenu the port's P5 gate walks. +# +# 📌 The check was one scan and the answer could have gone the other way. It came +# out of asking what the static records MEAN rather than how they are counted -- +# the arithmetic thread that produced it corrected itself three times and none of +# those rounds touched anything shipped. +# +# ⚠️ REACH: GP_TITLE only. The other 32 archives are not checked, and 1 530 static +# records exist disc-wide against the 20 here.