Answers the port's ask about ptframe1/ptframe2, whose residual is uniquely higher on flat pixels than edges and signed one direction -- a body-intensity difference. Prior work covers .prm primitives and a refuted T8aD +0x04 bit; neither covers a .t32 element. All 15 words of the 60-byte declaration entry are read: 3 are the name, 8 constant, the rest kind, focus index, position and pivot. The frames are kind 0, identical to every other plain sprite. One candidate found and refuted by myself: T8aD +0x08 is the only word where both frames agree uniquely on that screen, at 0x8050 -- but 38 sprites carry it disc-wide, only 8 named frame, and the high byte tracks the archive. It is an atlas/format word, not a mode. Also records a false positive of my own test: +0x00 and +0x08 first read as 'separating the frames' because those words are the name string. So any blend the port picks is authored. Reach: not looked at the executable's draw path, where a mode selected in code rather than data would live. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
51 lines
2.4 KiB
Rust
51 lines
2.4 KiB
Rust
//! Is T8aD `+0x08` a per-sprite MODE, or a texture FORMAT word?
|
|
//!
|
|
//! On the main menu the two frames share `+0x08 = 0x8050` and no other sprite has
|
|
//! it — a candidate for the blend/alpha mode `sylpheed-port` asked for. Before
|
|
//! offering it, refute it: if 0x8050 is common disc-wide on ordinary sprites, it
|
|
//! is not frame-specific and not a mode.
|
|
//!
|
|
//! cargo run -p sylpheed-formats --example t8ad_word8_census
|
|
use sylpheed_formats::{pak::PakArchive, ui_layout};
|
|
use std::collections::BTreeMap;
|
|
use std::path::PathBuf;
|
|
|
|
fn main() {
|
|
let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC"));
|
|
let mut paks: Vec<_> = std::fs::read_dir(root.join("dat"))
|
|
.expect("dat")
|
|
.filter_map(|e| e.ok().map(|e| e.path()))
|
|
.filter(|p| p.extension().map(|x| x == "pak").unwrap_or(false))
|
|
.collect();
|
|
paks.sort();
|
|
let mut hist: BTreeMap<u32, usize> = BTreeMap::new();
|
|
let mut frames_like: BTreeMap<u32, usize> = BTreeMap::new();
|
|
let mut examples: BTreeMap<u32, Vec<String>> = BTreeMap::new();
|
|
for p in &paks {
|
|
let Ok(ar) = PakArchive::open(p) else { continue };
|
|
for e in ar.entries() {
|
|
let Ok(by) = ar.read(e) else { continue };
|
|
let Some(b) = ui_layout::parse_build(&by) else { continue };
|
|
for (n, (off, size)) in &b.sprites {
|
|
let s = &by[*off..(*off + *size).min(by.len())];
|
|
if s.len() < 48 { continue }
|
|
let w = u32::from_be_bytes([s[8], s[9], s[10], s[11]]);
|
|
*hist.entry(w).or_default() += 1;
|
|
if n.contains("frame") { *frames_like.entry(w).or_default() += 1 }
|
|
let ex = examples.entry(w).or_default();
|
|
if ex.len() < 3 && !ex.contains(n) { ex.push(n.clone()) }
|
|
}
|
|
}
|
|
}
|
|
let total: usize = hist.values().sum();
|
|
println!("{total} sprites disc-wide; distinct +0x08 values: {}\n", hist.len());
|
|
println!("{:>10} {:>8} {:>10} examples", "value", "count", "of which 'frame'");
|
|
for (v, c) in hist.iter().filter(|(_, c)| **c >= 20) {
|
|
println!("{:>#10x} {c:>8} {:>10} {}", v, frames_like.get(v).copied().unwrap_or(0),
|
|
examples[v].join(", "));
|
|
}
|
|
println!("\n0x8050 specifically: {} sprites, {} of them named *frame*",
|
|
hist.get(&0x8050).copied().unwrap_or(0),
|
|
frames_like.get(&0x8050).copied().unwrap_or(0));
|
|
}
|