The port listed palogo_gamearts_eff and palogo_seta_eff among GP_TITLE's four visible dwell-fallback fires; this census listed only palogo_sqex_eff and palogo_anima_eff. Checked, and the census is right: gamearts_eff and seta_eff hold a=255 at identical x, y and scale from t=15 to t=30, which is a plateau at pair index 1, so rest_plateau() handles them and t=15 is the CORRECT answer. They are not fallback cases. The distinction is not cosmetic -- a plateau is a pose the element genuinely holds, and only the dwell fallback is the unsound path. But the refutation makes the port's underlying point STRONGER. Its rest pose for those two really is the flash's peak, reached by the SOUND path. So 'a rest render is not a frame to score against a capture' does not follow from the fallback being unsound: a plateau can itself be the held peak of a transient. The rule covers both paths, and the fallback census understates the exposure rather than bounding it. Also records the port's oracle number for the rule -- publisher splash against the committed capture, timeline RMSE 2.17 / 0.01 % differing against --pose=rest 9.05 / 0.75 %, 75x the differing area. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
41 lines
2.2 KiB
Rust
41 lines
2.2 KiB
Rust
//! Are `palogo_gamearts_eff` / `palogo_seta_eff` dwell-FALLBACK cases, or PLATEAU
|
|
//! cases? The port agent lists them among `GP_TITLE`'s four visible fallback
|
|
//! fires; this census listed only `palogo_sqex_eff` and `palogo_anima_eff`.
|
|
//!
|
|
//! It matters because the two are different defects. A plateau is a pose the
|
|
//! element genuinely HOLDS, and `rest_plateau()` returning it is correct. Only the
|
|
//! fallback is the unsound path.
|
|
//! cargo run -p sylpheed-formats --example palogo_eff_check
|
|
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");
|
|
for (i, e) in ar.entries().iter().enumerate() {
|
|
let Ok(by) = ar.read(e) else { continue };
|
|
let Some(b) = ui_layout::parse_build(&by) else { continue };
|
|
for el in &b.elements {
|
|
if !el.name.starts_with("palogo") || !el.name.contains("eff") { continue }
|
|
// A single-keyframe element has no gap to maximise, so neither path
|
|
// applies and `rest()` trivially returns the only pose. Excluding it
|
|
// here matches the census, which filters `len < 2`.
|
|
if el.keyframes.len() < 2 { continue }
|
|
let plateau = el.keyframes.windows(2).position(|w| {
|
|
w[0].x == w[1].x && w[0].y == w[1].y && w[0].scale_x == w[1].scale_x
|
|
&& w[0].scale_y == w[1].scale_y && w[0].fade == w[1].fade
|
|
});
|
|
let ks: Vec<String> = el.keyframes.iter().map(|k| format!(
|
|
"{}:a{} {},{} {}%",
|
|
k.time.map(|v| v.to_string()).unwrap_or("-".into()),
|
|
(k.fade >> 24) & 0xff, k.x, k.y, k.scale_x)).collect();
|
|
let r = el.rest();
|
|
println!("e{i:<3} {:24} kf=[{}]", el.name, ks.join(" "));
|
|
println!(" plateau at pair {:?} -> path: {} rest a={} t={:?}",
|
|
plateau,
|
|
if plateau.is_some() { "PLATEAU (sound: the pose is held)" } else { "DWELL FALLBACK (unsound)" },
|
|
r.map(|k| (k.fade >> 24) & 0xff).unwrap_or(0),
|
|
r.and_then(|k| k.time));
|
|
}
|
|
}
|
|
}
|