From aa570b106ab289a21c21db026f04a0a0283a1aef Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Mon, 31 Aug 2026 05:37:43 +0000 Subject: [PATCH] re: no per-element field on the disc separates the four too-dark frames The port measures four elements as rendering too dark against the capture -- ptframe1/2 on the main menu, ptframe3/4 on EXTRAS -- with the shortfall correlating +0.77/+0.80 with the BACKGROUND and only +0.24 with the element's own contribution. That is the signature of a blend that scales what is already there. It asked whether the disc selects one. Three examples, one negative, wider than the one I gave last iteration: * frame_alpha_census -- every T8aD sprite on builds 5 and 6 by alpha. It REFUTES the port's own sharpener: 'neither frame has a single fully-opaque pixel, against ptbase's 99.1 %' is true, and pteff10 (max alpha 130, 100 % partial, no opaque pixel) is measured by the port as NEARLY EXACT. So being wholly semi-transparent is not what makes the frames special. * frame_vs_accurate_words -- all 12 T8aD header words for both screens, plus a per-BIT sweep of +0x04 and +0x08. NO word and NO bit puts the four frames on one side and pteff10 on the other. It also kills my own remaining candidate a second time: +0x08 = 0x8050 is shared with pteff21/22/23 on EXTRAS. * frame_keyframe_unknowns -- the keyframe record's fade, tint, rotation and its two unexplained signed words. unknown_4 and unknown_8 are ZERO on every keyframe of both screens, so they carry nothing here; no frame takes a value of any field that another element does not. Reach: the 60-byte declaration entry, the T8aD header word-wise and bit-wise, and the keyframe record. Four elements, two screens. Not the executable's draw path, which is the next commit. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v --- .../examples/frame_alpha_census.rs | 57 +++++++++++++++++ .../examples/frame_keyframe_unknowns.rs | 46 ++++++++++++++ .../examples/frame_vs_accurate_words.rs | 63 +++++++++++++++++++ docs/re/data/frame-blend-field-hunt.txt | 63 +++++++++++++++++++ docs/re/data/menu-sprite-alpha-census.txt | 55 ++++++++++++++++ 5 files changed, 284 insertions(+) create mode 100644 crates/sylpheed-formats/examples/frame_alpha_census.rs create mode 100644 crates/sylpheed-formats/examples/frame_keyframe_unknowns.rs create mode 100644 crates/sylpheed-formats/examples/frame_vs_accurate_words.rs create mode 100644 docs/re/data/frame-blend-field-hunt.txt create mode 100644 docs/re/data/menu-sprite-alpha-census.txt diff --git a/crates/sylpheed-formats/examples/frame_alpha_census.rs b/crates/sylpheed-formats/examples/frame_alpha_census.rs new file mode 100644 index 00000000..5a2bda9d --- /dev/null +++ b/crates/sylpheed-formats/examples/frame_alpha_census.rs @@ -0,0 +1,57 @@ +//! The main menu's sprites, by their ALPHA channel — is `ptframe1`/`ptframe2`'s +//! "no fully-opaque pixel" a property of the artwork, and does any alpha value +//! look like a scale the game expands (e.g. 0..128) rather than 0..255? +//! +//! The port measures both frames as rendering too DARK against the capture, with +//! the shortfall correlating with the BACKGROUND. Two different causes predict +//! that: a background-scaling blend selected in code, or an alpha that is too +//! LOW in our decode. This example tests the second, which is on the disc. +//! +//! cargo run -p sylpheed-formats --example frame_alpha_census +use sylpheed_formats::{pak::PakArchive, t8ad, ui_layout}; +use std::path::PathBuf; + +fn census(name: &str, img: &t8ad::T8adImage) { + let n = (img.width * img.height) as usize; + let mut hist = [0usize; 256]; + for p in 0..n { + hist[img.rgba[p * 4 + 3] as usize] += 1; + } + let zero = hist[0]; + let full = hist[255]; + let max = (0..256).rev().find(|&a| hist[a] > 0).unwrap_or(0); + let nonzero = n - zero; + // the top five alpha values that actually occur, by population + let mut top: Vec<(usize, usize)> = (1..256).map(|a| (hist[a], a)).filter(|&(c, _)| c > 0).collect(); + top.sort_unstable_by(|a, b| b.0.cmp(&a.0)); + let top5: Vec = top.iter().take(5).map(|&(c, a)| format!("{a}x{c}")).collect(); + println!( + "{name:<16} {}x{:<4} px={n:<8} a=0:{:5.1}% a=255:{:5.1}% max={max:<3} \ + partial(1..254)/nonzero={:5.1}% top:[{}]", + img.width, img.height, + 100.0 * zero as f64 / n as f64, + 100.0 * full as f64 / n as f64, + if nonzero > 0 { 100.0 * (nonzero - full) as f64 / nonzero as f64 } else { 0.0 }, + top5.join(" ") + ); +} + +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 build in [5usize, 6] { + let by = ar.read(&ar.entries()[build]).expect("entry"); + let b = ui_layout::parse_build(&by).expect("build"); + println!("=== GP_TITLE build {build} ==="); + let mut names: Vec<&String> = b.sprites.keys().collect(); + names.sort(); + for n in names { + let (off, size) = b.sprites[n]; + let s = &by[off..(off + size).min(by.len())]; + match t8ad::parse(s) { + Some(img) => census(n, &img), + None => println!("{n:<16} (not a T8aD / failed to parse)"), + } + } + } +} diff --git a/crates/sylpheed-formats/examples/frame_keyframe_unknowns.rs b/crates/sylpheed-formats/examples/frame_keyframe_unknowns.rs new file mode 100644 index 00000000..8e603af5 --- /dev/null +++ b/crates/sylpheed-formats/examples/frame_keyframe_unknowns.rs @@ -0,0 +1,46 @@ +//! The keyframe record's two unexplained words (`+4`, `+8`) and the fade/tint — +//! do any of them separate the four elements the port measures as rendering too +//! dark (`ptframe1`/`2`, `ptframe3`/`4`) from the ones it measures as accurate? +//! +//! The T8aD header does not: no word and no bit of `+0x04`/`+0x08` puts the four +//! frames on one side and `pteff10` (max alpha 130, wholly semi-transparent, and +//! rendered nearly exact) on the other. The keyframe is the other place a +//! per-element draw mode could live. +//! +//! cargo run -p sylpheed-formats --example frame_keyframe_unknowns +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"); + println!("{:<22} {:>5} {:>3} {:>10} {:>10} {:>8} {:>8} {:>8}", + "element", "build", "kf", "unknown_4", "unknown_8", "fade", "tint", "rot"); + let mut frame_sets: Vec<(String, i32, i32, u32, u32, i32)> = Vec::new(); + let mut other_sets: Vec<(String, i32, i32, u32, u32, i32)> = Vec::new(); + for build in [5usize, 6] { + let by = ar.read(&ar.entries()[build]).expect("entry"); + let b = ui_layout::parse_build(&by).expect("build"); + for e in &b.elements { + for (i, k) in e.keyframes.iter().enumerate() { + println!("{:<22} {build:>5} {i:>3} {:>10} {:>10} {:08X} {:08X} {:>8}", + e.name, k.unknown_4, k.unknown_8, k.fade, k.tint, k.rotation_deg); + let row = (e.name.clone(), k.unknown_4, k.unknown_8, k.fade, k.tint, k.rotation_deg); + if e.name.contains("frame") { frame_sets.push(row) } else { other_sets.push(row) } + } + } + } + println!("\nframe keyframes: {} other keyframes: {}", frame_sets.len(), other_sets.len()); + for (label, get) in [ + ("unknown_4", 0usize), ("unknown_8", 1), ("fade", 2), ("tint", 3), ("rotation", 4), + ] { + let val = |r: &(String, i32, i32, u32, u32, i32)| -> i64 { + match get { 0 => r.1 as i64, 1 => r.2 as i64, 2 => r.3 as i64, 3 => r.4 as i64, _ => r.5 as i64 } + }; + let fv: std::collections::BTreeSet = frame_sets.iter().map(val).collect(); + let ov: std::collections::BTreeSet = other_sets.iter().map(val).collect(); + let only_frames: Vec<&i64> = fv.iter().filter(|v| !ov.contains(v)).collect(); + println!("{label:<10} frames take {:?} others take {} distinct values; frame-only values: {:?}", + fv, ov.len(), only_frames); + } +} diff --git a/crates/sylpheed-formats/examples/frame_vs_accurate_words.rs b/crates/sylpheed-formats/examples/frame_vs_accurate_words.rs new file mode 100644 index 00000000..395c3be2 --- /dev/null +++ b/crates/sylpheed-formats/examples/frame_vs_accurate_words.rs @@ -0,0 +1,63 @@ +//! Which T8aD header word, if any, separates the FOUR elements the port measures +//! as rendering too dark (`ptframe1`/`2` on the main menu, `ptframe3`/`4` on +//! `EXTRAS`) from the elements on the same two screens it measures as accurate? +//! +//! The control that matters: `pteff10` has max alpha 130 and no fully-opaque +//! pixel — the same "wholly semi-transparent" property the port proposed as the +//! reason the frames are special — and it renders nearly exact. So the separator +//! must put `pteff10` on the ACCURATE side. +//! +//! cargo run -p sylpheed-formats --example frame_vs_accurate_words +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"); + let mut rows: Vec<(String, usize, Vec)> = Vec::new(); + for build in [5usize, 6] { + let by = ar.read(&ar.entries()[build]).expect("entry"); + let b = ui_layout::parse_build(&by).expect("build"); + let mut names: Vec<&String> = b.sprites.keys().collect(); + names.sort(); + for n in names { + let (off, size) = b.sprites[n]; + let s = &by[off..(off + size).min(by.len())]; + if s.len() < 48 || &s[0..4] != b"T8aD" { continue } + let ws: Vec = (0..12) + .map(|k| u32::from_be_bytes([s[k*4], s[k*4+1], s[k*4+2], s[k*4+3]])) + .collect(); + rows.push((n.clone(), build, ws)); + } + } + let is_frame = |n: &str| n.starts_with("ptframe"); + println!("{:<16} {:>5} +0x04 +0x08 +0x1C +0x2C", "sprite", "build"); + for (n, b, w) in &rows { + println!("{n:<16} {b:>5} {:08X} {:08X} {:08X} {:08X}{}", + w[1], w[2], w[7], w[11], if is_frame(n) { " <- TOO DARK" } else { "" }); + } + println!("\nwords where every ptframe* agrees and NO other sprite takes that value:"); + let frames: Vec<&(String, usize, Vec)> = rows.iter().filter(|(n,_,_)| is_frame(n)).collect(); + let mut any = false; + for k in 0..12 { + let v = frames[0].2[k]; + if !frames.iter().all(|r| r.2[k] == v) { continue } + if rows.iter().any(|(n,_,w)| !is_frame(n) && w[k] == v) { continue } + println!(" word {k} (+0x{:02X}) = {v:08X}", k*4); any = true; + } + if !any { println!(" NONE — no header word separates the four frames from the rest"); } + println!("\nper-bit check on +0x04 and +0x08 (a bit that is 1 on all frames, 0 on all others):"); + let mut anyb = false; + for &k in &[1usize, 2] { + for bit in 0..32 { + let on = |v: u32| (v >> bit) & 1 == 1; + if frames.iter().all(|r| on(r.2[k])) && rows.iter().all(|(n,_,w)| is_frame(n) || !on(w[k])) { + println!(" +0x{:02X} bit {bit} (0x{:X})", k*4, 1u32 << bit); anyb = true; + } + if frames.iter().all(|r| !on(r.2[k])) && rows.iter().all(|(n,_,w)| is_frame(n) || on(w[k])) { + println!(" +0x{:02X} bit {bit} (0x{:X}) INVERTED", k*4, 1u32 << bit); anyb = true; + } + } + } + if !anyb { println!(" NONE"); } +} diff --git a/docs/re/data/frame-blend-field-hunt.txt b/docs/re/data/frame-blend-field-hunt.txt new file mode 100644 index 00000000..05d04b2c --- /dev/null +++ b/docs/re/data/frame-blend-field-hunt.txt @@ -0,0 +1,63 @@ +# Does ANY per-element field on the disc separate the four too-dark frames from +# the elements the port renders accurately, on the same two screens? +# cargo run -p sylpheed-formats --example frame_vs_accurate_words (SYLPHEED_DISC=/disc) +# and --example frame_keyframe_unknowns + +sprite build +0x04 +0x08 +0x1C +0x2C +ptbase.t32 5 00008830 00008000 00000006 00000044 +ptbtn01.t32 5 00008130 00008110 00000001 00000030 +ptbtn01f.t32 5 00008130 00008112 00000001 00000030 +ptbtn02.t32 5 00008130 00008110 00000001 00000030 +ptbtn02b.t32 5 00000810 00008110 00000001 00000030 +ptbtn02f.t32 5 00008130 00008112 00000001 00000030 +ptbtn03.t32 5 00008130 00008110 00000001 00000030 +ptbtn03f.t32 5 00008130 00008112 00000001 00000030 +ptbtn04.t32 5 00008130 00008110 00000001 00000030 +ptbtn04f.t32 5 00008130 00008112 00000001 00000030 +ptbtn05.t32 5 00008130 00008110 00000001 00000030 +ptbtn05f.t32 5 00008130 00008112 00000001 00000030 +ptbtneff01.t32 5 00008130 00008112 00000001 00000030 +pteff03.t32 5 00008832 00008010 00000002 00000034 +pteff03a.t32 5 00008832 00008010 00000002 00000034 +pteff05.t32 5 00008830 00008020 0000000F 00000068 +pteff10.t32 5 00008832 00008040 00000002 00000034 +pteff12.t32 5 00008832 00008041 00000004 0000003C +ptframe1.t32 5 00008832 00008050 00000002 00000034 <- TOO DARK +ptframe2.t32 5 00008832 00008050 00000004 0000003C <- TOO DARK +ptmsg.t32 5 00008830 00008100 00000001 00000030 +ptbase.t32 6 00008830 00008000 00000006 00000044 +ptbtn11.t32 6 00008130 00008110 00000001 00000030 +ptbtn11f.t32 6 00008130 00008112 00000002 00000034 +ptbtn12.t32 6 00008130 00008110 00000001 00000030 +ptbtn12f.t32 6 00008130 00008112 00000002 00000034 +ptbtn13.t32 6 00008130 00008110 00000001 00000030 +ptbtn13f.t32 6 00008130 00008112 00000001 00000030 +ptbtneff02.t32 6 00008130 00008112 00000001 00000030 +pteff03.t32 6 00008832 00008010 00000002 00000034 +pteff03a.t32 6 00008832 00008010 00000002 00000034 +pteff05.t32 6 00008830 00008020 0000000F 00000068 +pteff10.t32 6 00008832 00008040 00000002 00000034 +pteff20.t32 6 00008832 00008041 00000004 0000003C +pteff21.t32 6 00008832 00008050 00000002 00000034 +pteff22.t32 6 00008832 00008050 00000002 00000034 +pteff23.t32 6 00008832 00008050 00000002 00000034 +ptframe3.t32 6 00008832 00008050 00000001 00000030 <- TOO DARK +ptframe4.t32 6 00008832 00008050 00000001 00000030 <- TOO DARK +ptmsg2.t32 6 00008830 00008100 00000002 00000034 +pttitle.t32 6 00008830 00008100 00000001 00000030 + +words where every ptframe* agrees and NO other sprite takes that value: + NONE — no header word separates the four frames from the rest + +per-bit check on +0x04 and +0x08 (a bit that is 1 on all frames, 0 on all others): + NONE + +=== keyframe record: fade / tint / rotation / the two unexplained words === +pteff00.prm 6 3 0 0 FF000000 FFFFFFFF 0 + +frame keyframes: 28 other keyframes: 116 +unknown_4 frames take {0} others take 1 distinct values; frame-only values: [] +unknown_8 frames take {0} others take 1 distinct values; frame-only values: [] +fade frames take {16777215, 2164260863, 3238002687, 4294967295} others take 7 distinct values; frame-only values: [] +tint frames take {4294967295} others take 1 distinct values; frame-only values: [] +rotation frames take {0} others take 1 distinct values; frame-only values: [] diff --git a/docs/re/data/menu-sprite-alpha-census.txt b/docs/re/data/menu-sprite-alpha-census.txt new file mode 100644 index 00000000..304c6a17 --- /dev/null +++ b/docs/re/data/menu-sprite-alpha-census.txt @@ -0,0 +1,55 @@ +# Alpha census of every T8aD sprite on GP_TITLE builds 5 (main menu) and 6 (EXTRAS). +# cargo run -p sylpheed-formats --example frame_alpha_census (SYLPHEED_DISC=/disc) +# +# Why: the port measures ptframe1/2 (menu) and ptframe3/4 (EXTRAS) as the only +# elements whose render is too DARK against the capture, and proposed that being +# WHOLLY SEMI-TRANSPARENT -- 'neither frame has a single fully-opaque pixel, +# against ptbase's 99.1 %' -- is what makes them special. +# +# REFUTED by this census: pteff10 has max alpha 130, 100 % partial, no opaque +# pixel either, and the port measures it as NEARLY EXACT. pteff12/20/21/22/23 +# are the same. The property is real and it is not the discriminator. + +=== GP_TITLE build 5 === +ptbase.t32 640x360 px=230400 a=0: 0.0% a=255: 99.1% max=255 partial(1..254)/nonzero= 0.9% top:[255x228404 240x1988 225x4 244x4] +ptbtn01.t32 203x43 px=8729 a=0: 58.0% a=255: 21.7% max=255 partial(1..254)/nonzero= 48.4% top:[255x1893 128x273 187x204 34x158 162x126] +ptbtn01f.t32 216x56 px=12096 a=0: 13.5% a=255: 17.0% max=255 partial(1..254)/nonzero= 80.4% top:[255x2052 99x399 156x395 2x313 56x279] +ptbtn02.t32 217x43 px=9331 a=0: 60.1% a=255: 21.0% max=255 partial(1..254)/nonzero= 47.2% top:[255x1964 128x318 187x188 34x185 162x157] +ptbtn02b.t32 217x43 px=9331 a=0: 60.1% a=255: 21.0% max=255 partial(1..254)/nonzero= 47.2% top:[255x1964 128x318 187x188 34x185 162x157] +ptbtn02f.t32 230x56 px=12880 a=0: 14.2% a=255: 16.0% max=255 partial(1..254)/nonzero= 81.4% top:[255x2058 99x444 156x442 2x355 56x304] +ptbtn03.t32 178x43 px=7654 a=0: 61.4% a=255: 20.8% max=255 partial(1..254)/nonzero= 46.0% top:[255x1595 128x243 119x154 187x154 60x142] +ptbtn03f.t32 191x56 px=10696 a=0: 15.7% a=255: 15.4% max=255 partial(1..254)/nonzero= 81.8% top:[255x1643 156x397 99x385 2x332 56x292] +ptbtn04.t32 159x43 px=6837 a=0: 58.7% a=255: 22.5% max=255 partial(1..254)/nonzero= 45.7% top:[255x1535 128x221 119x165 187x152 60x147] +ptbtn04f.t32 172x56 px=9632 a=0: 13.1% a=255: 16.4% max=255 partial(1..254)/nonzero= 81.1% top:[255x1579 156x347 99x330 2x318 56x257] +ptbtn05.t32 150x43 px=6450 a=0: 59.2% a=255: 21.6% max=255 partial(1..254)/nonzero= 47.1% top:[255x1393 128x212 34x113 187x99 119x94] +ptbtn05f.t32 163x56 px=9128 a=0: 13.5% a=255: 15.8% max=255 partial(1..254)/nonzero= 81.8% top:[255x1441 156x323 99x321 2x310 9x241] +ptbtneff01.t32 42x46 px=1932 a=0: 24.1% a=255: 2.5% max=255 partial(1..254)/nonzero= 96.7% top:[1x98 2x59 255x48 5x35 3x33] +pteff03.t32 399x180 px=71820 a=0: 0.0% a=255: 0.3% max=255 partial(1..254)/nonzero= 99.7% top:[3x720 6x720 1x720 13x716 254x712] +pteff03a.t32 399x180 px=71820 a=0: 0.0% a=255: 0.3% max=255 partial(1..254)/nonzero= 99.7% top:[3x720 6x720 1x720 13x716 254x712] +pteff05.t32 1280x720 px=921600 a=0: 0.5% a=255: 82.4% max=255 partial(1..254)/nonzero= 17.2% top:[255x759007 254x16439 253x10207 248x7686 250x7584] +pteff10.t32 409x144 px=58896 a=0: 0.0% a=255: 0.0% max=130 partial(1..254)/nonzero=100.0% top:[2x864 3x864 5x864 1x864 125x852] +pteff12.t32 347x360 px=124920 a=0: 16.8% a=255: 0.0% max=142 partial(1..254)/nonzero=100.0% top:[1x14618 4x5325 6x4219 3x4093 8x3429] +ptframe1.t32 247x281 px=69407 a=0: 92.7% a=255: 0.0% max=173 partial(1..254)/nonzero=100.0% top:[7x555 1x217 8x191 99x130 86x116] +ptframe2.t32 257x311 px=79927 a=0: 93.2% a=255: 0.0% max=174 partial(1..254)/nonzero=100.0% top:[7x689 96x311 76x270 8x232 98x161] +ptmsg.t32 223x38 px=8474 a=0: 48.6% a=255: 19.7% max=255 partial(1..254)/nonzero= 61.8% top:[255x1667 230x256 248x62 76x54 231x48] +=== GP_TITLE build 6 === +ptbase.t32 640x360 px=230400 a=0: 0.0% a=255: 99.1% max=255 partial(1..254)/nonzero= 0.9% top:[255x228404 240x1988 225x4 244x4] +ptbtn11.t32 253x43 px=10879 a=0: 59.2% a=255: 21.4% max=255 partial(1..254)/nonzero= 47.5% top:[255x2330 187x362 128x327 60x275 34x122] +ptbtn11f.t32 266x56 px=14896 a=0: 13.0% a=255: 16.8% max=255 partial(1..254)/nonzero= 80.6% top:[255x2509 156x553 99x537 2x467 9x379] +ptbtn12.t32 248x43 px=10664 a=0: 59.1% a=255: 21.9% max=255 partial(1..254)/nonzero= 46.4% top:[255x2338 128x373 187x269 60x201 34x124] +ptbtn12f.t32 261x56 px=14616 a=0: 12.8% a=255: 17.2% max=255 partial(1..254)/nonzero= 80.3% top:[255x2508 156x536 99x521 2x457 56x366] +ptbtn13.t32 109x43 px=4687 a=0: 59.3% a=255: 22.3% max=255 partial(1..254)/nonzero= 45.3% top:[255x1044 128x189 119x75 34x69 187x61] +ptbtn13f.t32 122x56 px=6832 a=0: 14.8% a=255: 15.8% max=255 partial(1..254)/nonzero= 81.4% top:[255x1081 2x228 99x225 156x224 9x166] +ptbtneff02.t32 42x46 px=1932 a=0: 24.1% a=255: 2.5% max=255 partial(1..254)/nonzero= 96.7% top:[1x98 2x59 255x48 5x35 3x33] +pteff03.t32 399x180 px=71820 a=0: 0.0% a=255: 0.3% max=255 partial(1..254)/nonzero= 99.7% top:[3x720 6x720 1x720 13x716 254x712] +pteff03a.t32 399x180 px=71820 a=0: 0.0% a=255: 0.3% max=255 partial(1..254)/nonzero= 99.7% top:[3x720 6x720 1x720 13x716 254x712] +pteff05.t32 1280x720 px=921600 a=0: 0.5% a=255: 82.4% max=255 partial(1..254)/nonzero= 17.2% top:[255x759007 254x16439 253x10207 248x7686 250x7584] +pteff10.t32 409x144 px=58896 a=0: 0.0% a=255: 0.0% max=130 partial(1..254)/nonzero=100.0% top:[2x864 3x864 5x864 1x864 125x852] +pteff20.t32 309x259 px=80031 a=0: 16.9% a=255: 0.0% max=218 partial(1..254)/nonzero=100.0% top:[1x7899 4x3317 2x2390 6x2076 8x1215] +pteff21.t32 403x6 px=2418 a=0: 0.4% a=255: 0.0% max=200 partial(1..254)/nonzero=100.0% top:[16x522 136x502 200x502 2x32 15x30] +pteff22.t32 423x7 px=2961 a=0: 54.1% a=255: 0.0% max=200 partial(1..254)/nonzero=100.0% top:[4x80 16x58 2x54 1x54 3x52] +pteff23.t32 441x7 px=3087 a=0: 56.8% a=255: 0.0% max=200 partial(1..254)/nonzero=100.0% top:[4x75 2x59 1x59 3x56 16x47] +ptframe3.t32 246x220 px=54120 a=0: 93.3% a=255: 0.0% max=248 partial(1..254)/nonzero=100.0% top:[146x150 2x135 142x89 123x86 122x78] +ptframe4.t32 256x210 px=53760 a=0: 93.3% a=255: 0.0% max=247 partial(1..254)/nonzero=100.0% top:[2x135 146x124 142x109 108x77 121x68] +ptmsg2.t32 354x38 px=13452 a=0: 50.8% a=255: 15.7% max=255 partial(1..254)/nonzero= 68.1% top:[255x2112 230x448 248x116 76x89 231x88] +pttitle.t32 182x34 px=6188 a=0: 51.6% a=255: 32.5% max=255 partial(1..254)/nonzero= 32.8% top:[255x2013 68x372 187x130 119x80 238x66]