Files

109 lines
4.6 KiB
Rust
Raw Permalink Normal View History

2026-07-09 02:59:53 +03:00
#![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;
}
}