This branch predates CI on `main`. `cargo fmt --all` only; no behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
98 lines
3.9 KiB
Rust
98 lines
3.9 KiB
Rust
//! Out-of-sample test of the `T8aD +0x04` blend bit on the **boot splashes**.
|
|
//!
|
|
//! `ui-blend-mode-decoded.md` established the field on 35 elements over three
|
|
//! screens (GP_TITLE entries 2, 4, 5, 6). The two splashes -- entries 10 and 11 --
|
|
//! were NOT in that sample, and they are the screens the 2026-09-01 play-test
|
|
//! says are wrong.
|
|
//!
|
|
//! The oracle for these two screens is `data/splash-draw-pass-census.txt`: over
|
|
//! **1048 draws of frames 4..226**, covering both splashes end to end, the only
|
|
//! blend states submitted are `0x00010001` (the clear) and `0x07010701`
|
|
//! (source-over). Additive, `0x01010101`, appears **zero** times.
|
|
//!
|
|
//! PRE-REGISTERED PREDICTION, written before reading the disc: if the bit is the
|
|
//! blend selector and it generalises off its training screens, then every sprite
|
|
//! in entries 10 and 11 must report `additive = false`. Any `true` is a
|
|
//! discrepancy the field has to answer for.
|
|
//!
|
|
//! cargo run --release -p sylpheed-formats --example splash_blend_check
|
|
use std::path::PathBuf;
|
|
use sylpheed_formats::{pak::PakArchive, ui_layout};
|
|
|
|
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 additive = 0usize;
|
|
let mut alpha_over = 0usize;
|
|
let mut no_header = 0usize;
|
|
|
|
for entry in [10usize, 11] {
|
|
let by = ar.read(&ar.entries()[entry]).expect("entry");
|
|
let Some(b) = ui_layout::parse_build(&by) else {
|
|
println!("entry {entry}: not a build");
|
|
continue;
|
|
};
|
|
println!(
|
|
"\n## GP_TITLE entry {entry} -- {} elements, {} sprites",
|
|
b.elements.len(),
|
|
b.sprites.len()
|
|
);
|
|
|
|
// Every sprite the bundle carries, not only those a top-level element
|
|
// names: a focused variant is reached through `focus_link` and would
|
|
// otherwise be invisible to this check.
|
|
let mut names: Vec<&String> = b.sprites.keys().collect();
|
|
names.sort();
|
|
for n in names {
|
|
match ui_layout::blend_additive_by_name(&b, &by, n) {
|
|
Some(true) => {
|
|
additive += 1;
|
|
println!(
|
|
" {n:<26} word04=0x{:08X} ADDITIVE",
|
|
ui_layout::header_word_04_by_name(&b, &by, n).unwrap()
|
|
);
|
|
}
|
|
Some(false) => {
|
|
alpha_over += 1;
|
|
println!(
|
|
" {n:<26} word04=0x{:08X} alpha-over",
|
|
ui_layout::header_word_04_by_name(&b, &by, n).unwrap()
|
|
);
|
|
}
|
|
None => {
|
|
no_header += 1;
|
|
println!(" {n:<26} (no T8aD header)");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
println!("\nadditive={additive} alpha-over={alpha_over} no-header={no_header}");
|
|
println!("oracle (splash-draw-pass-census.txt, 1048 draws): additive draws = 0");
|
|
if additive == 0 {
|
|
println!("PREDICTION HELD -- the bit agrees with the capture on both splashes");
|
|
} else {
|
|
println!(
|
|
"PREDICTION FAILED -- {additive} sprite(s) claim additive, \
|
|
the capture submits none"
|
|
);
|
|
}
|
|
|
|
// Control: this harness must be able to REPORT additive, or "additive=0"
|
|
// is a property of the harness and not of the splashes. Entry 6 is a screen
|
|
// the oracle measures as mixed.
|
|
let by6 = ar.read(&ar.entries()[6]).expect("entry 6");
|
|
let b6 = ui_layout::parse_build(&by6).expect("build 6");
|
|
let c_add = b6
|
|
.sprites
|
|
.keys()
|
|
.filter(|n| ui_layout::blend_additive_by_name(&b6, &by6, n) == Some(true))
|
|
.count();
|
|
println!(
|
|
"control -- entry 6 through the SAME code path reports additive={c_add} \
|
|
(must be > 0): {}",
|
|
if c_add > 0 { "PASS" } else { "FAIL" }
|
|
);
|
|
}
|