re: the port's two extra elements are PLATEAU cases -- refutation succeeds, and its point gets bigger

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
This commit is contained in:
sylph-decoder
2026-08-30 11:54:43 +00:00
parent 28b06f9a31
commit ad9cd948c6
3 changed files with 108 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
//! 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));
}
}
}

View File

@@ -0,0 +1,40 @@
# Are palogo_gamearts_eff / palogo_seta_eff dwell-FALLBACK cases? No -- PLATEAU.
#
# 2026-08-30. The port agent listed them among GP_TITLE's four visible
# fallback fires. This census listed only palogo_sqex_eff and
# palogo_anima_eff, so one of us was wrong.
#
# The distinction is not cosmetic: a plateau is a pose the element genuinely
# HOLDS, and rest_plateau() returning it is CORRECT. Only the dwell fallback
# is the unsound path.
#
e10 palogo_sqex_eff.t32 kf=[0:a0 299,319 100% 15:a255 299,319 100% 30:a212 299,319 100% 45:a0 299,319 100%]
plateau at pair None -> path: DWELL FALLBACK (unsound) rest a=212 t=Some(30)
e11 palogo_gamearts_eff.t32 kf=[0:a0 379,154 100% 15:a255 379,154 100% 30:a255 379,154 100% 45:a0 379,154 100%]
plateau at pair Some(1) -> path: PLATEAU (sound: the pose is held) rest a=255 t=Some(15)
e11 palogo_seta_eff.t32 kf=[0:a0 511,305 100% 15:a255 511,305 100% 30:a255 511,305 100% 45:a0 511,305 100%]
plateau at pair Some(1) -> path: PLATEAU (sound: the pose is held) rest a=255 t=Some(15)
e11 palogo_anima_eff.t32 kf=[0:a0 435,440 100% 15:a255 435,440 100% 30:a212 435,440 100% 45:a0 435,440 100%]
plateau at pair None -> path: DWELL FALLBACK (unsound) rest a=212 t=Some(30)
e13 palogo_sqex_eff.t32 kf=[0:a0 299,319 100% 15:a255 299,319 100% 30:a212 299,319 100% 45:a0 299,319 100%]
plateau at pair None -> path: DWELL FALLBACK (unsound) rest a=212 t=Some(30)
e14 palogo_gamearts_eff.t32 kf=[0:a0 379,154 100% 15:a255 379,154 100% 30:a255 379,154 100% 45:a0 379,154 100%]
plateau at pair Some(1) -> path: PLATEAU (sound: the pose is held) rest a=255 t=Some(15)
e14 palogo_seta_eff.t32 kf=[0:a0 511,305 100% 15:a255 511,305 100% 30:a255 511,305 100% 45:a0 511,305 100%]
plateau at pair Some(1) -> path: PLATEAU (sound: the pose is held) rest a=255 t=Some(15)
e14 palogo_anima_eff.t32 kf=[0:a0 435,440 100% 15:a255 435,440 100% 30:a212 435,440 100% 45:a0 435,440 100%]
plateau at pair None -> path: DWELL FALLBACK (unsound) rest a=212 t=Some(30)
#
# ✅ REFUTATION SUCCEEDS. gamearts_eff and seta_eff hold a=255 at the SAME
# x, y and scale from t=15 to t=30 -- that is a plateau at pair index 1, so
# rest_plateau() handles them and returns t=15, a=255 correctly. They are not
# fallback cases. The census's four (sqex_eff x2, anima_eff x2) stand.
#
# 🔴 BUT THE PORT'S UNDERLYING POINT SURVIVES AND GETS BIGGER. Its rest pose
# for those two really is a=255, the flash's peak -- reached by the SOUND
# path. So 'rest is not a frame to score against a capture' is NOT a
# consequence of the fallback being unsound. A plateau can itself be the held
# peak of a transient, and here four elements hold full-alpha flashes.
#
# The rule therefore covers BOTH paths, and the fallback census understates
# the exposure rather than bounding it.

View File

@@ -345,6 +345,34 @@ a frame the game never shows.
render posed at `rest` is a legitimate **common reference for comparing two
decoders**, and is **not** a frame to score against a capture of the game.
**Now an oracle number rather than an argument.** The port measured its publisher
splash against the committed capture in both poses: **timeline RMSE 2.17 / 0.01 %
differing**, against **`--pose=rest` RMSE 9.05 / 0.75 %** — **75× the differing
area**, on a screen it ships. Nothing it ships is wrong; its settled pose evaluates
`pose_at(hold)` and skips the flashes.
### 🔴 …and the rule is NOT a consequence of the fallback being unsound
The port also listed `palogo_gamearts_eff` and `palogo_seta_eff` among the four
visible fallback fires. **They are not** — refutation attempt, and it succeeds
([`../data/palogo-eff-plateau-vs-fallback.txt`](../data/palogo-eff-plateau-vs-fallback.txt)):
| element | keyframes | path | `rest` |
|---|---|---|---|
| `palogo_sqex_eff`, `palogo_anima_eff` | `0:a0 15:a255 30:a212 45:a0` | **dwell fallback** (unsound) | t=30, a=212 |
| `palogo_gamearts_eff`, `palogo_seta_eff` | `0:a0 15:a255 **30:a255** 45:a0` | **plateau** (sound — the pose is held) | t=15, **a=255** |
The second pair holds `a=255` at identical x, y and scale from t=15 to t=30. That
**is** a plateau, `rest_plateau()` handles it, and t=15 is the *correct* answer. The
census's four stand.
🔴 **But that makes the port's point stronger, not weaker.** Its rest pose for those
two 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 (2 305 / 1 697) *understates* the exposure
rather than bounding it.
### 🔴 What this retracts
Last iteration I reported the build 7 render difference as evidence **against**