Files
hcie-rust-v3.05/hcie-io/examples/compare_blocks.rs
2026-07-09 02:59:53 +03:00

220 lines
8.7 KiB
Rust

#![allow(dead_code, unused_imports, unused_variables, unused_macros, unreachable_patterns)]
fn main() {
let orig_bytes = std::fs::read("_images/_psd_stil_test/inner_shadow/black_triangle_green_inner_shadow_normal_blend.psd").unwrap();
let saved_bytes = std::fs::read("_tmp/roundtrip_output.psd").unwrap();
// Extract lfx2 block from Layer 1 (Black Triangle) of both files
println!("=== LFX2 BLOCK COMPARISON ===\n");
let orig_lfx2 = extract_block(&orig_bytes, "lfx2", 1);
let saved_lfx2 = extract_block(&saved_bytes, "lfx2", 1);
match (&orig_lfx2, &saved_lfx2) {
(Some(o), Some(s)) => {
println!("ORIG lfx2: {} bytes", o.len());
println!("SAVED lfx2: {} bytes", s.len());
println!("Diff: {} bytes", o.len() as i64 - s.len() as i64);
// Byte-by-byte diff
let min_len = o.len().min(s.len());
for i in 0..min_len {
if o[i] != s[i] {
println!(" FIRST DIFF at byte {}: orig=0x{:02X} saved=0x{:02X}", i, o[i], s[i]);
let start = i.saturating_sub(16);
let end = (i + 48).min(min_len);
println!(" ORIG : {}", hex_dump(&o[start..end]));
println!(" SAVED: {}", hex_dump(&s[start..end]));
// Also show as much descriptor text as possible
println!("\n ORIG context (text): {}", text_view(&o[start..end]));
println!(" SAVED context (text): {}", text_view(&s[start..end]));
break;
}
}
if o.len() != s.len() {
// Show last 32 bytes of each
println!("\n ORIG tail (last 32): {}", hex_dump(&o[o.len().saturating_sub(32)..]));
println!(" SAVED tail (last 32): {}", hex_dump(&s[s.len().saturating_sub(32)..]));
}
}
(Some(o), None) => println!("ORIG has lfx2 ({} bytes), SAVED missing", o.len()),
(None, Some(s)) => println!("SAVED has lfx2 ({} bytes), ORIG missing", s.len()),
(None, None) => println!("Neither has lfx2"),
}
// Extract shmd block from Layer 1
println!("\n=== SHMD BLOCK COMPARISON ===\n");
let orig_shmd = extract_block(&orig_bytes, "shmd", 1);
let saved_shmd = extract_block(&saved_bytes, "shmd", 1);
match (&orig_shmd, &saved_shmd) {
(Some(o), Some(s)) => {
println!("ORIG shmd: {} bytes", o.len());
println!("SAVED shmd: {} bytes", s.len());
let min_len = o.len().min(s.len());
for i in 0..min_len {
if o[i] != s[i] {
println!(" FIRST DIFF at byte {}: orig=0x{:02X} saved=0x{:02X}", i, o[i], s[i]);
let start = i.saturating_sub(16);
let end = (i + 48).min(min_len);
println!(" ORIG : {}", hex_dump(&o[start..end]));
println!(" SAVED: {}", hex_dump(&s[start..end]));
break;
}
}
}
_ => println!("Missing shmd in one or both"),
}
// Extract full Layer 1 extra data
println!("\n=== LAYER 1 FULL EXTRA DATA COMPARISON ===\n");
let orig_extra = extract_layer_extra(&orig_bytes, 1);
let saved_extra = extract_layer_extra(&saved_bytes, 1);
match (&orig_extra, &saved_extra) {
(Some(o), Some(s)) => {
println!("ORIG extra: {} bytes", o.len());
dump_extra_blocks("ORIG", o);
println!("\nSAVED extra: {} bytes", s.len());
dump_extra_blocks("SAVED", s);
}
_ => println!("Could not extract extra data"),
}
}
fn extract_block(bytes: &[u8], key: &str, _layer_idx: usize) -> Option<Vec<u8>> {
let lmi = get_lmi(bytes)?;
let li_len = u32::from_be_bytes([lmi[0], lmi[1], lmi[2], lmi[3]]) as usize;
let li = &lmi[4..4+li_len];
let layer_count = i16::from_be_bytes([li[0], li[1]]).unsigned_abs() as usize;
let mut pos = 2;
for _ in 0..layer_count {
if pos + 18 > li.len() { return None; }
let ch_count = u16::from_be_bytes([li[pos+16], li[pos+17]]) as usize;
pos += 18;
pos += ch_count * 6;
if pos + 12 > li.len() { return None; }
pos += 12; // blend+opacity+clipping+flags+filler
let extra_len = u32::from_be_bytes([li[pos], li[pos+1], li[pos+2], li[pos+3]]) as usize;
pos += 4;
let _extra_start = pos;
let extra_end = pos + extra_len;
// Skip mask and blending ranges
if pos + 4 > extra_end { return None; }
let mask_len = u32::from_be_bytes([li[pos], li[pos+1], li[pos+2], li[pos+3]]) as usize;
pos += 4 + mask_len;
if pos + 4 > extra_end { return None; }
let blend_len = u32::from_be_bytes([li[pos], li[pos+1], li[pos+2], li[pos+3]]) as usize;
pos += 4 + blend_len;
// Skip name
if pos >= extra_end { return None; }
let name_len = li[pos] as usize;
pos += 1 + name_len;
let bytes_mod_4 = (name_len + 1) % 4;
pos += (4 - bytes_mod_4) % 4;
// Scan for target block
while pos + 8 <= extra_end {
let sig = &li[pos..pos+4];
if sig != b"8BIM" && sig != b"8B64" { break; }
let block_key = String::from_utf8_lossy(&li[pos+4..pos+8]);
let block_len = u32::from_be_bytes([li[pos+8], li[pos+9], li[pos+10], li[pos+11]]) as usize;
if block_key == key {
return Some(li[pos+12..pos+12+block_len].to_vec());
}
pos += 12 + block_len;
if block_len % 2 != 0 { pos += 1; }
}
pos = extra_end;
}
None
}
fn extract_layer_extra(bytes: &[u8], layer_idx: usize) -> Option<Vec<u8>> {
let lmi = get_lmi(bytes)?;
let li_len = u32::from_be_bytes([lmi[0], lmi[1], lmi[2], lmi[3]]) as usize;
let li = &lmi[4..4+li_len];
let layer_count = i16::from_be_bytes([li[0], li[1]]).unsigned_abs() as usize;
let mut pos = 2;
for i in 0..layer_count {
if pos + 18 > li.len() { return None; }
let ch_count = u16::from_be_bytes([li[pos+16], li[pos+17]]) as usize;
pos += 18;
pos += ch_count * 6;
if pos + 12 > li.len() { return None; }
pos += 12;
let extra_len = u32::from_be_bytes([li[pos], li[pos+1], li[pos+2], li[pos+3]]) as usize;
pos += 4;
let extra_start = pos;
if i == layer_idx {
return Some(li[extra_start..extra_start+extra_len].to_vec());
}
pos += extra_len;
}
None
}
fn dump_extra_blocks(_label: &str, extra: &[u8]) {
let mut pos = 0;
// Skip mask
if pos + 4 > extra.len() { return; }
let mask_len = u32::from_be_bytes([extra[pos], extra[pos+1], extra[pos+2], extra[pos+3]]) as usize;
println!(" mask_data: {} bytes", mask_len);
pos += 4 + mask_len;
// Skip blending ranges
if pos + 4 > extra.len() { return; }
let blend_len = u32::from_be_bytes([extra[pos], extra[pos+1], extra[pos+2], extra[pos+3]]) as usize;
println!(" blend_ranges: {} bytes", blend_len);
pos += 4 + blend_len;
// Name
if pos >= extra.len() { return; }
let name_len = extra[pos] as usize;
pos += 1;
if pos + name_len > extra.len() { return; }
let name = String::from_utf8_lossy(&extra[pos..pos+name_len]);
println!(" name: '{}' ({} bytes + padding)", name, name_len);
pos += name_len;
let bytes_mod_4 = (name_len + 1) % 4;
pos += (4 - bytes_mod_4) % 4;
// Tagged blocks
while pos + 8 <= extra.len() {
let sig = &extra[pos..pos+4];
if sig != b"8BIM" && sig != b"8B64" {
println!(" BAD SIG at {}: {:02X?}", pos, &extra[pos..pos+4.min(extra.len()-pos)]);
break;
}
let key = String::from_utf8_lossy(&extra[pos+4..pos+8]);
let block_len = u32::from_be_bytes([extra[pos+8], extra[pos+9], extra[pos+10], extra[pos+11]]) as usize;
println!(" block '{}' len={} @{}", key, block_len, pos);
pos += 12 + block_len;
if block_len % 2 != 0 { pos += 1; }
}
}
fn get_lmi(bytes: &[u8]) -> Option<&[u8]> {
let cmd_len = u32::from_be_bytes([bytes[26], bytes[27], bytes[28], bytes[29]]) as usize;
let ir_start = 30 + cmd_len;
let ir_len = u32::from_be_bytes([bytes[ir_start], bytes[ir_start+1], bytes[ir_start+2], bytes[ir_start+3]]) as usize;
let lmi_start = ir_start + 4 + ir_len;
let lmi_len = u32::from_be_bytes([bytes[lmi_start], bytes[lmi_start+1], bytes[lmi_start+2], bytes[lmi_start+3]]) as usize;
Some(&bytes[lmi_start+4..lmi_start+4+lmi_len])
}
fn hex_dump(data: &[u8]) -> String {
data.iter().take(64).map(|b| format!("{:02X}", b)).collect::<Vec<_>>().join(" ")
}
fn text_view(data: &[u8]) -> String {
data.iter().map(|&b| if b >= 0x20 && b < 0x7F { b as char } else { '.' }).collect()
}