//! Does `T8aD +0x04` bit `0x02` predict the blend the GAME uses? //! //! ⚠️ **`REFUTED.md` kills this claim**: *"`T8aD +0x04` bit `0x02` selects an //! additive blend" → mine, and refuted. Blending those sprites additively //! worsens every measure against the capture.* That refutation rests entirely on //! **our renderer** — it is a claim about our renderer, and the corpus's own rule //! says so. Since it was written, the blend has been measured off the GPU per //! draw on three screens (`structures/ui-blend-mode-measured.md`), so the claim //! can now be tested against the oracle instead of against a render. //! //! The labels below are **not** from a render. Every one is a //! `RB_BLENDCONTROL0` value read out of the guest command stream and attributed //! to an element by quad size: //! `data/ui-blend-mode-measured.txt`, `data/ui-blend-title-and-replication.txt`, //! `data/ui-blend-extras-complete.txt`. //! //! cargo run -p sylpheed-formats --example blend_vs_t8ad_bit use std::collections::BTreeMap; use std::path::PathBuf; use sylpheed_formats::{pak::PakArchive, ui_layout}; /// (build entry, sprite, measured additive?) — the oracle's verdicts, verbatim. const MEASURED: &[(usize, &str, bool)] = &[ // --- GP_TITLE entry 4 + 2, the live title ------------------------------- (4, "ptbase2.t32", false), (4, "ptlogo1.t32", false), (4, "ptlogo2.t32", false), (4, "ptlogo_tm.t32", false), (4, "ptcopyright.t32", false), (4, "ptlogo_back2.t32", false), (4, "ptlogo_back2eff.t32", false), (2, "ptbtn00.t32", false), (2, "ptbtn00f.t32", true), // --- entry 5, the main menu --------------------------------------------- (5, "ptbase.t32", false), (5, "ptmsg.t32", false), (5, "ptbtn01f.t32", false), (5, "ptbtneff01.t32", false), (5, "pteff10.t32", true), (5, "pteff12.t32", true), (5, "ptframe1.t32", true), (5, "ptframe2.t32", true), (5, "pteff03.t32", true), // the rotated sweep strips, via ptloop01/02 (5, "pteff03a.t32", true), // --- entry 6, EXTRAS ------------------------------------------------------ (6, "ptbase.t32", false), (6, "ptmsg2.t32", false), (6, "pttitle.t32", false), (6, "ptbtn11f.t32", false), (6, "ptbtn12.t32", false), (6, "ptbtn13.t32", false), (6, "ptbtneff02.t32", false), (6, "pteff10.t32", true), (6, "pteff20.t32", true), (6, "pteff21.t32", true), (6, "pteff22.t32", true), (6, "pteff23.t32", true), (6, "ptframe3.t32", true), (6, "ptframe4.t32", true), (6, "pteff03.t32", true), (6, "pteff03a.t32", true), ]; fn main() { if std::env::args().any(|a| a == "decl") { decl_rivals(); return; } 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 hdr: BTreeMap<(usize, String), u32> = BTreeMap::new(); for e in [2usize, 4, 5, 6] { let by = ar.read(&ar.entries()[e]).expect("entry"); let b = ui_layout::parse_build(&by).expect("build"); for (n, &(off, size)) in &b.sprites { let s = &by[off..(off + size).min(by.len())]; if s.len() < 8 || &s[0..4] != b"T8aD" { continue; } hdr.insert((e, n.clone()), u32::from_be_bytes([s[4], s[5], s[6], s[7]])); } } println!( "{:<10} {:<22} {:<10} {:>10} measured blend", "entry", "sprite", "+0x04", "bit 0x02" ); let (mut tp, mut tn, mut fp, mut fnn, mut missing) = (0, 0, 0, 0, 0); for &(e, n, additive) in MEASURED { let Some(&w) = hdr.get(&(e, n.to_string())) else { println!( "{e:<10} {n:<22} {:<10} {:>10} {}", "MISSING", "-", if additive { "ADDITIVE" } else { "alpha-over" } ); missing += 1; continue; }; let bit = w & 0x02 != 0; match (bit, additive) { (true, true) => tp += 1, (false, false) => tn += 1, (true, false) => fp += 1, (false, true) => fnn += 1, } println!( "{e:<10} {n:<22} {:08X} {:>10} {}{}", w, bit, if additive { "ADDITIVE" } else { "alpha-over" }, if bit == additive { "" } else { " <== DISAGREES" } ); } println!("\nbit set & additive {tp}"); println!("bit clear & alpha-over {tn}"); println!("bit set & alpha-over {fp} <- false positives"); println!("bit clear & additive {fnn} <- false negatives"); println!("sprite not found {missing}"); println!( "\n{}", if fp == 0 && fnn == 0 && missing == 0 { "PERFECT PARTITION on every element whose blend was measured." } else { "THE BIT DOES NOT PREDICT THE MEASURED BLEND." } ); // ── THE CONTROL THAT MATTERS ──────────────────────────────────────────── // A perfect partition is worthless if half the header partitions equally // well: then the sample is too small to single out a field, and picking // `+0x04` bit 0x02 out of the tie is the same mistake as picking `+0x08` // 0x8050 was. So: how many OTHER bits of the first 12 header words separate // the same 35 elements without error? let mut rivals: Vec = Vec::new(); let mut words: BTreeMap<(usize, String), Vec> = BTreeMap::new(); for e in [2usize, 4, 5, 6] { let by = ar.read(&ar.entries()[e]).expect("entry"); let b = ui_layout::parse_build(&by).expect("build"); for (n, &(off, size)) in &b.sprites { let s = &by[off..(off + size).min(by.len())]; if s.len() < 48 || &s[0..4] != b"T8aD" { continue; } words.insert( (e, n.clone()), (0..12) .map(|k| { u32::from_be_bytes([s[k * 4], s[k * 4 + 1], s[k * 4 + 2], s[k * 4 + 3]]) }) .collect(), ); } } for w in 0..12 { for bit in 0..32 { let mut ok = true; let mut set_seen = false; let mut clear_seen = false; for &(e, n, additive) in MEASURED { let Some(v) = words.get(&(e, n.to_string())) else { ok = false; break; }; let on = (v[w] >> bit) & 1 == 1; if on { set_seen = true } else { clear_seen = true } if on != additive { ok = false; break; } } // A constant bit trivially "agrees" with nothing; require both sides. if ok && set_seen && clear_seen { rivals.push(format!("+0x{:02X} bit {bit} (0x{:X})", w * 4, 1u32 << bit)); } } } println!("\nRIVAL FIELDS — other bits of the first 12 header words that separate"); println!("the same 35 elements with zero errors: {}", rivals.len()); for r in &rivals { println!(" {r}"); } if rivals.len() == 1 { println!(" -> the sample singles out ONE field. Nothing else in the header does it."); } else { println!( " -> the sample does NOT single out a field; {} candidates tie.", rivals.len() ); } } // ── An integrity check the published decode did NOT do ────────────────────── // The rival sweep above covers the 48-byte T8aD header. It does NOT cover the // 60-byte DECLARATION entry, and the earlier declaration hunt was run with // labels taken from the port's RENDER -- which put pteff10, pteff12, pteff20 and // pteff21..23 on the alpha-over side, where the oracle says all six are // additive. So the declaration has never been swept with correct labels, and if // one of its words also partitions the 35 without error, "the field is the T8aD // bit" is underdetermined. // // Run as: cargo run -p sylpheed-formats --example blend_vs_t8ad_bit -- decl #[allow(dead_code)] fn decl_rivals() { use std::collections::BTreeMap; 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"); const AT: usize = 0x20; const STRIDE: usize = 60; let mut decl: BTreeMap<(usize, String), Vec> = BTreeMap::new(); for e in [2usize, 4, 5, 6] { let Ok(by) = ar.read(&ar.entries()[e]) else { continue; }; if by.len() < 0x18 { continue; } let count = u32::from_be_bytes([by[0x14], by[0x15], by[0x16], by[0x17]]) as usize; for i in 0..count { let at = AT + i * STRIDE; if at + STRIDE > by.len() { break; } let end = by[at..at + 12].iter().position(|&c| c == 0).unwrap_or(12); let name = String::from_utf8_lossy(&by[at..at + end]).to_string(); decl.insert( (e, name), (0..15) .map(|k| { u32::from_be_bytes([ by[at + k * 4], by[at + k * 4 + 1], by[at + k * 4 + 2], by[at + k * 4 + 3], ]) }) .collect(), ); } } let mut missing: Vec = Vec::new(); for &(e, n, _) in MEASURED { if !decl.contains_key(&(e, n.to_string())) { missing.push(format!("entry {e} {n}")); } } println!("\n=== DECLARATION-ENTRY RIVAL SWEEP ==="); println!( "measured elements with NO declaration entry of their own: {} of {}", missing.len(), MEASURED.len() ); for m in &missing { println!(" {m}"); } if !missing.is_empty() { println!(" -> no declaration field can select the blend for these, because they"); println!(" have no declaration entry. The header is the only per-sprite home."); } let labelled: Vec<&(usize, &str, bool)> = MEASURED .iter() .filter(|(e, n, _)| decl.contains_key(&(*e, n.to_string()))) .collect(); let mut rivals = 0; #[allow(clippy::needless_range_loop)] for w in 0..15 { for bit in 0..32 { let (mut ok, mut s, mut c) = (true, false, false); for &&(e, n, additive) in &labelled { let on = (decl[&(e, n.to_string())][w] >> bit) & 1 == 1; if on { s = true } else { c = true } if on != additive { ok = false; break; } } if ok && s && c { println!(" RIVAL: declaration +0x{:02X} bit {bit}", w * 4); rivals += 1; } } } println!( "declaration bits that separate the {} labellable elements: {rivals}", labelled.len() ); }