#![allow(dead_code, unused_imports, unused_variables, unused_macros, unreachable_patterns, unused_assignments, clippy::cfg)] fn main() { let path = "/home/hc/Pictures/Forest Text Effect/Forest-Text-mini.psd"; let bytes = std::fs::read(path).unwrap(); let mut pos = 26; let color_mode_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize; pos += 4 + color_mode_len; let img_res_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize; pos += 4 + img_res_len; let _layer_mask_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize; pos += 4; let _layer_info_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize; pos += 4; let layer_count = i16::from_be_bytes([bytes[pos], bytes[pos+1]]).abs() as usize; pos += 2; for _ in 0..layer_count { pos += 16; // bounds let channel_count = u16::from_be_bytes([bytes[pos], bytes[pos+1]]) as usize; pos += 2 + channel_count * 6; pos += 4; // blend sig pos += 4; // blend key pos += 1; // opacity pos += 1; // clipping pos += 1; // flags pos += 1; // filler let extra_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize; pos += 4; let extra_start = pos; let mask_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize; pos += 4 + mask_len; let blend_range_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize; pos += 4 + blend_range_len; let name_len = bytes[pos] as usize; pos += 1 + name_len; let pad = (4 - ((name_len + 1) % 4)) % 4; pos += pad; let mut layer_name = String::new(); let mut has_lfx2 = false; let mut has_lrfx = false; let mut lfx2_offset = 0usize; let mut lfx2_len = 0usize; while pos < extra_start + extra_len { let sig = &bytes[pos..pos+4]; if sig != b"8BIM" && sig != b"8B64" { break; } pos += 4; let key = &bytes[pos..pos+4]; pos += 4; let block_len = u32::from_be_bytes([bytes[pos], bytes[pos+1], bytes[pos+2], bytes[pos+3]]) as usize; pos += 4; let block_start = pos; let block_data = &bytes[pos..pos+block_len]; pos += block_len; if block_len % 2 != 0 { pos += 1; } let key_str = String::from_utf8_lossy(key); if key_str == "lfx2" { has_lfx2 = true; lfx2_offset = block_start; lfx2_len = block_len; } else if key_str == "lrFX" { has_lrfx = true; } else if key_str == "TySh" || key_str == "luni" { // unicode name if block_data.len() >= 4 { let char_count = u32::from_be_bytes([block_data[0], block_data[1], block_data[2], block_data[3]]) as usize; if block_data.len() >= 4 + char_count * 2 { let mut s = String::new(); for i in 0..char_count { let c = u16::from_be_bytes([block_data[4 + i*2], block_data[5 + i*2]]); if let Some(ch) = std::char::from_u32(c as u32) { s.push(ch); } } layer_name = s; } } } } if has_lfx2 && (layer_name == "text" || layer_name.contains("text")) { // Search for GrFl in the lfx2 block let lfx2_data = &bytes[lfx2_offset..lfx2_offset+lfx2_len]; println!("\n=== Layer '{}' lfx2 block (len={}) ===", layer_name, lfx2_len); let keys = [b"DrSh", b"IrSh", b"OrGl", b"IrGl", b"ebbl", b"lagl", b"SoFi", b"GrFl", b"PtFl", b"FrFX", b"ChFX"]; for key in &keys { let mut found = false; let mut offsets = Vec::new(); for i in 0..lfx2_data.len().saturating_sub(3) { if &lfx2_data[i..i+4] == *key { found = true; offsets.push(i); if offsets.len() > 5 { break; } } } if found { println!(" {}: found at offsets {:?}", std::str::from_utf8(*key).unwrap(), offsets); } } } // Skip channel data (can't easily without dims, break) break; } }