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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
This commit is contained in:
sylph-decoder
2026-08-31 04:03:32 +00:00
parent dd03887228
commit d404b3084b
2 changed files with 80 additions and 0 deletions

View File

@@ -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<u32> = 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}");
}

View File

@@ -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.