diff --git a/Cargo.toml b/Cargo.toml index aad1def..93b9957 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,7 @@ utils-crc32 = [] utils-escape = ["fancy-regex"] utils-mmx = [] utils-pcm = [] -utils-psd = ["image", "flate2"] +utils-psd = ["image", "flate2", "utils-bit-stream"] utils-rc4 = [] utils-serde-base64bytes = ["base64"] utils-str = [] diff --git a/README.md b/README.md index a66542d..94f413d 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ msg-tool create -t |---|---|---|---|---|---|---|---|---|---|---| | `qlie` | `qlie` | Qlie Engine Scenario script (.s) | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | | | `qlie-abmp10` / `qlie-abmp11` / `qlie-abmp12` | `qlie-img` | Qlie Abmp10/11/12 image (.b) | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | | -| `qlie-dpng` | `qlie-img` | Qlie tiled PNG image (.png) | ❌ | ❌ | ❌ | ❌ | ✔️ | ❌ | ❌ | `--qlie-dpng-psd` is required. | +| `qlie-dpng` | `qlie-img` | Qlie tiled PNG image (.png) | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ❌ | `--qlie-dpng-psd` is required. | | Archive Type | Feature Name | Name | Unpack | Pack | Remarks | |---|---|---|---|---|---| diff --git a/src/scripts/qlie/image/dpng.rs b/src/scripts/qlie/image/dpng.rs index e32a520..eae0c41 100644 --- a/src/scripts/qlie/image/dpng.rs +++ b/src/scripts/qlie/image/dpng.rs @@ -270,6 +270,50 @@ impl Script for DpngImage { psd.save(base, &mut writer)?; Ok(()) } + + fn custom_import<'a>( + &'a self, + custom_filename: &'a str, + mut file: Box, + encoding: Encoding, + output_encoding: Encoding, + ) -> Result<()> { + let rfile = std::fs::File::open(custom_filename)?; + let mut reader = std::io::BufReader::new(rfile); + let psd = PsdReader::new(&mut reader, output_encoding)?; + let width = psd.width(); + let height = psd.height(); + let layers = psd.read_normal_layers()?; + let header = DpngHeader { + magic: *b"DPNG", + _unk1: 1, + tile_count: layers.len() as u32, + image_width: width, + image_height: height, + }; + let mut tiles = Vec::new(); + for layer in layers { + let data = layer.image()?; + let width = data.width; + let height = data.height; + let mut png_data = MemWriter::new(); + encode_img_writer(data, ImageOutputType::Png, &mut png_data, &self.config)?; + let png_data = png_data.into_inner(); + let tile = Tile { + x: layer.left() as u32, + y: layer.top() as u32, + width, + height, + size: png_data.len() as u32, + _unk: 0, + png_data, + }; + tiles.push(tile); + } + let dpng = DpngFile { header, tiles }; + dpng.pack(&mut file, false, encoding, &None)?; + Ok(()) + } } fn create_raw_png_image<'a>( diff --git a/src/utils/psd/compression.rs b/src/utils/psd/compression.rs new file mode 100644 index 0000000..9f4066a --- /dev/null +++ b/src/utils/psd/compression.rs @@ -0,0 +1,175 @@ +use super::NormalLayer; +use super::types::*; +use crate::ext::io::*; +use anyhow::Result; +use std::io::Read; + +pub fn rle_compress(data: &[u8]) -> Vec { + let start = 0; + let line_end = data.len(); + let mut idx = start; + let mut literal: Vec = Vec::new(); + let mut out_line: Vec = Vec::new(); + while idx < line_end { + // detect run length at current position + let mut run_len = 1; + while idx + run_len < line_end && data[idx + run_len] == data[idx] && run_len < 128 { + run_len += 1; + } + + if run_len >= 3 { + // flush any pending literals + if !literal.is_empty() { + // header = literal_len - 1 (0..127) + let header = (literal.len() - 1) as i8; + out_line.push(header as u8); + out_line.extend_from_slice(&literal); + literal.clear(); + } + // write run: header = -(run_len - 1), then single byte value + let header = -(((run_len as u8) - 1) as i8); + out_line.push(header as u8); + out_line.push(data[idx]); + idx += run_len; + } else { + // collect literal bytes until a run of >=3 or 128 reached + literal.push(data[idx]); + idx += 1; + // if literal is full, flush it + if literal.len() == 128 { + let header = (literal.len() - 1) as i8; + out_line.push(header as u8); + out_line.extend_from_slice(&literal); + literal.clear(); + } else { + // peek ahead: if next starts a run >=3, flush literal now + if idx < line_end { + let mut look_run = 1; + while idx + look_run < line_end + && data[idx + look_run] == data[idx] + && look_run < 128 + { + look_run += 1; + } + if look_run >= 3 { + if !literal.is_empty() { + let header = (literal.len() - 1) as i8; + out_line.push(header as u8); + out_line.extend_from_slice(&literal); + literal.clear(); + } + } + } + } + } + } + // flush remaining literal + if !literal.is_empty() { + let header = (literal.len() - 1) as i8; + out_line.push(header as u8); + out_line.extend_from_slice(&literal); + literal.clear(); + } + out_line +} + +pub fn rle_decompress(data: &[u8]) -> Result> { + let mut reader = MemReaderRef::new(data); + let len = data.len(); + let mut out = Vec::new(); + while reader.pos < len { + let c = reader.read_i8()?; + if c >= 0 { + let rlen = (c as usize) + 1; + let old_len = out.len(); + out.resize(old_len + rlen, 0); + reader.read_exact(&mut out[old_len..old_len + rlen])?; + } else { + let rlen = (-(c as isize) as usize) + 1; + let val = reader.read_u8()?; + let old_len = out.len(); + out.resize(old_len + rlen, val); + } + } + Ok(out) +} + +pub fn decompress_channel_image_data( + data: &mut ChannelImageData, + layer: &NormalLayer, +) -> Result<()> { + match data.compression { + 0 => Ok(()), // no compression + 1 => { + let mut reader = MemReaderRef::new(&data.image_data); + let base = &layer.layer.base; + let height = (base.bottom - base.top) as u32; + let length_len = height as usize * 2; + let mut start = length_len; + let mut image = Vec::new(); + for _ in 0..height { + let len = reader.read_u16_be()? as usize; + let decompressed = rle_decompress(&data.image_data[start..start + len])?; + start += len; + image.extend(decompressed); + } + data.image_data = image; + data.compression = 0; + Ok(()) + } + 2 => { + let mut decoder = flate2::read::ZlibDecoder::new(&data.image_data[..]); + let mut decompressed = Vec::new(); + decoder.read_to_end(&mut decompressed)?; + data.image_data = decompressed; + data.compression = 0; + Ok(()) + } + 3 => { + let mut decoder = flate2::read::ZlibDecoder::new(&data.image_data[..]); + let mut decompressed = Vec::new(); + decoder.read_to_end(&mut decompressed)?; + let base = &layer.layer.base; + let height = (base.bottom - base.top) as u32; + let width = (base.right - base.left) as u32; + let bit_depth = layer.psd.bit_depth(); + if bit_depth == 1 { + anyhow::bail!("Decompression for 1-bit images is not implemented yet"); + } + let mut writer = MemWriterRef::new(&mut decompressed); + for _ in 0..height { + if bit_depth == 8 { + let mut pre = writer.read_u8()?; + for _ in 1..width { + let cur = writer.peek_u8()?; + let val = cur.wrapping_add(pre); + writer.write_u8(val)?; + pre = val; + } + } else if bit_depth == 16 { + let mut pre = writer.read_u16_be()?; + for _ in 1..width { + let cur = writer.peek_u16_be()?; + let val = cur.wrapping_add(pre); + writer.write_u16_be(val)?; + pre = val; + } + } else if bit_depth == 32 { + let mut pre = writer.read_u32_be()?; + for _ in 1..width { + let cur = writer.peek_u32_be()?; + let val = cur.wrapping_add(pre); + writer.write_u32_be(val)?; + pre = val; + } + } else { + anyhow::bail!("Unsupported bit depth for decompression: {}", bit_depth); + } + } + data.image_data = decompressed; + data.compression = 0; + Ok(()) + } + _ => anyhow::bail!("Unsupported compression type: {}", data.compression), + } +} diff --git a/src/utils/psd/mod.rs b/src/utils/psd/mod.rs index 97b1e77..e0c1181 100644 --- a/src/utils/psd/mod.rs +++ b/src/utils/psd/mod.rs @@ -1,13 +1,18 @@ -//! A simple PSD writer +//! A simple PSD reader/writer +mod compression; mod types; use crate::ext::io::*; use crate::types::*; +use crate::utils::bit_stream::*; use crate::utils::encoding::*; use crate::utils::img::*; use crate::utils::struct_pack::*; use anyhow::Result; -use std::io::Write; +use compression::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::ops::Deref; use types::*; pub use types::{ @@ -54,7 +59,7 @@ fn encode_unicode_layer(name: &str) -> Result { layer.pack(&mut data, true, Encoding::Utf16BE, &None)?; Ok(AdditionalLayerInfo { signature: *IMAGE_RESOURCE_SIGNATURE, - key: *b"luni", + key: *UNICODE_LAYER_KEY, data: data.into_inner(), }) } @@ -100,13 +105,13 @@ impl PsdWriter { layer_records: vec![], channel_image_data: vec![], }, - global_layer_mask_info: GlobalLayerMaskInfo { + global_layer_mask_info: Some(GlobalLayerMaskInfo { overlays_color_space: 0, overlays_color_components: [0; 4], opacity: 0, kind: 128, filler: vec![0], - }, + }), tagged_blocks: vec![], }, image_data: ImageDataSection { @@ -263,8 +268,7 @@ impl PsdWriter { .push(layer); // Update layer count - self.psd.layer_and_mask_info.layer_info.layer_count = - self.psd.layer_and_mask_info.layer_info.layer_records.len() as u16; + self.psd.layer_and_mask_info.layer_info.layer_count += 1; self.psd .layer_and_mask_info @@ -300,7 +304,7 @@ impl PsdWriter { let mut infos = vec![ AdditionalLayerInfo { signature: *IMAGE_RESOURCE_SIGNATURE, - key: *b"lsct", + key: *SECTION_DIVIDER_SETTING_KEY, data: data.into_inner(), }, encode_unicode_layer(name)?, @@ -370,7 +374,7 @@ impl PsdWriter { layer_name: PascalString4(b"".to_vec()), infos: vec![AdditionalLayerInfo { signature: *IMAGE_RESOURCE_SIGNATURE, - key: *b"lsct", + key: *SECTION_DIVIDER_SETTING_KEY, data: data.into_inner(), }], }; @@ -440,74 +444,7 @@ impl PsdWriter { for y in 0..height { let start = (c * width * height) + (y * width); let line_end = start + width; - let mut idx = start; - let mut literal: Vec = Vec::new(); - let mut out_line: Vec = Vec::new(); - - while idx < line_end { - // detect run length at current position - let mut run_len = 1; - while idx + run_len < line_end - && planar_data[idx + run_len] == planar_data[idx] - && run_len < 128 - { - run_len += 1; - } - - if run_len >= 3 { - // flush any pending literals - if !literal.is_empty() { - // header = literal_len - 1 (0..127) - let header = (literal.len() - 1) as i8; - out_line.push(header as u8); - out_line.extend_from_slice(&literal); - literal.clear(); - } - // write run: header = -(run_len - 1), then single byte value - let header = -(((run_len as u8) - 1) as i8); - out_line.push(header as u8); - out_line.push(planar_data[idx]); - idx += run_len; - } else { - // collect literal bytes until a run of >=3 or 128 reached - literal.push(planar_data[idx]); - idx += 1; - // if literal is full, flush it - if literal.len() == 128 { - let header = (literal.len() - 1) as i8; - out_line.push(header as u8); - out_line.extend_from_slice(&literal); - literal.clear(); - } else { - // peek ahead: if next starts a run >=3, flush literal now - if idx < line_end { - let mut look_run = 1; - while idx + look_run < line_end - && planar_data[idx + look_run] == planar_data[idx] - && look_run < 128 - { - look_run += 1; - } - if look_run >= 3 { - if !literal.is_empty() { - let header = (literal.len() - 1) as i8; - out_line.push(header as u8); - out_line.extend_from_slice(&literal); - literal.clear(); - } - } - } - } - } - } - - // flush remaining literal - if !literal.is_empty() { - let header = (literal.len() - 1) as i8; - out_line.push(header as u8); - out_line.extend_from_slice(&literal); - literal.clear(); - } + let out_line = rle_compress(&planar_data[start..line_end]); // write scanline length at reserved spot and append data compressed @@ -524,3 +461,409 @@ impl PsdWriter { Ok(()) } } + +#[derive(Debug)] +pub struct PsdReader { + psd: PsdFile, + encoding: Encoding, + channel_start_indices: Vec, +} + +#[derive(Debug)] +pub struct NormalLayer<'a> { + layer: &'a LayerRecord, + layer_idx: usize, + psd: &'a PsdReader, +} + +impl<'a> NormalLayer<'a> { + /// Returns the name of the layer. + pub fn layer_name(&self) -> Result { + self.layer.layer_name(self.psd.encoding) + } + + /// Returns the current layer's index in the PSD file. + pub fn layer_index(&self) -> usize { + self.layer_idx + } + + /// Returns the top position of the layer. + pub fn top(&self) -> i32 { + self.layer.base.top + } + + /// Returns the left position of the layer. + pub fn left(&self) -> i32 { + self.layer.base.left + } + + /// Returns the bottom position of the layer. + pub fn bottom(&self) -> i32 { + self.layer.base.bottom + } + + /// Returns the right position of the layer. + pub fn right(&self) -> i32 { + self.layer.base.right + } + + /// Returns the width of the layer. + pub fn width(&self) -> u32 { + (self.layer.base.right - self.layer.base.left) as u32 + } + + /// Returns the height of the layer. + pub fn height(&self) -> u32 { + (self.layer.base.bottom - self.layer.base.top) as u32 + } + + /// Returns the number of channels in the layer. + pub fn channels(&self) -> u16 { + self.layer.base.channels + } + + /// Reads and returns the raw channel id and data of the layer. + pub fn read_raw_data(&self) -> Result)>> { + let mut start_idx = self.psd.channel_start_indices[self.layer_idx]; + let mut channels_data = Vec::new(); + for cinfo in &self.layer.base.channel_infos { + let mut data = self + .psd + .psd + .layer_and_mask_info + .layer_info + .channel_image_data[start_idx] + .clone(); + start_idx += 1; + decompress_channel_image_data(&mut data, self)?; + channels_data.push((cinfo.channel_id, data.image_data)); + } + Ok(channels_data) + } + + /// Reads and returns the image data of the layer. + pub fn image(&self) -> Result { + let color_mode = self.psd.color_mode(); + if !matches!(color_mode, 1 | 3) { + anyhow::bail!( + "Unsupported PSD color mode for image extraction: {}", + self.psd.color_mode() + ); + } + let channels = self.layer.base.channels; + let width = (self.layer.base.right - self.layer.base.left) as u32; + let height = (self.layer.base.bottom - self.layer.base.top) as u32; + if channels < 1 { + anyhow::bail!("PSD layer has no channels"); + } + let channels_map = BTreeMap::from_iter(self.read_raw_data()?); + if color_mode == 1 { + let grayscale = channels_map + .get(&0) + .ok_or_else(|| anyhow::anyhow!("PSD grayscale layer missing channel 0"))?; + let alpha = channels_map.get(&-1); + let depth = self.psd.bit_depth() as u8; + if let Some(alpha) = alpha { + let mut g = MsbBitStream::new(MemReaderRef::new(&grayscale)); + let mut a = MsbBitStream::new(MemReaderRef::new(&alpha)); + let mut data = MemWriter::new(); + let mut o = MsbBitWriter::new(&mut data); + for _ in 0..height { + g.m_cached_bits = 0; + a.m_cached_bits = 0; + for _ in 0..width { + let gray = g.get_bits(depth as u32)?; + let alpha = a.get_bits(depth as u32)?; + o.put_bits(gray, depth)?; + o.put_bits(gray, depth)?; + o.put_bits(gray, depth)?; + o.put_bits(alpha, depth)?; + } + o.flush()?; + } + Ok(ImageData { + width, + height, + color_type: ImageColorType::Rgba, + depth, + data: data.into_inner(), + }) + } else { + Ok(ImageData { + width, + height, + color_type: ImageColorType::Grayscale, + depth, + data: grayscale.clone(), + }) + } + } else { + let red = channels_map + .get(&0) + .ok_or_else(|| anyhow::anyhow!("PSD RGB layer missing channel 0"))?; + let green = channels_map + .get(&1) + .ok_or_else(|| anyhow::anyhow!("PSD RGB layer missing channel 1"))?; + let blue = channels_map + .get(&2) + .ok_or_else(|| anyhow::anyhow!("PSD RGB layer missing channel 2"))?; + let mut a = channels_map + .get(&-1) + .map(|v| MsbBitStream::new(MemReaderRef::new(v))); + let depth = self.psd.bit_depth() as u8; + let mut r = MsbBitStream::new(MemReaderRef::new(&red)); + let mut g = MsbBitStream::new(MemReaderRef::new(&green)); + let mut b = MsbBitStream::new(MemReaderRef::new(&blue)); + let mut data = MemWriter::new(); + let mut o = MsbBitWriter::new(&mut data); + for _ in 0..height { + r.m_cached_bits = 0; + g.m_cached_bits = 0; + b.m_cached_bits = 0; + if let Some(alpha) = &mut a { + alpha.m_cached_bits = 0; + } + for _ in 0..width { + let red = r.get_bits(depth as u32)?; + let green = g.get_bits(depth as u32)?; + let blue = b.get_bits(depth as u32)?; + o.put_bits(red, depth)?; + o.put_bits(green, depth)?; + o.put_bits(blue, depth)?; + if let Some(alpha) = &mut a { + let alpha = alpha.get_bits(depth as u32)?; + o.put_bits(alpha, depth)?; + } + } + o.flush()?; + } + Ok(ImageData { + width, + height, + color_type: if a.is_some() { + ImageColorType::Rgba + } else { + ImageColorType::Rgb + }, + depth, + data: data.into_inner(), + }) + } + } +} + +#[derive(Debug)] +pub struct GroupLayer<'a> { + layer: &'a LayerRecord, + layer_idx: usize, + psd: &'a PsdReader, + pub childrens: Vec>, + is_closed: bool, +} + +impl<'a> GroupLayer<'a> { + fn create<'b>(layer_idx: &'b mut usize, psd: &'a PsdReader) -> Result { + let mut childrens = Vec::new(); + // skip the end marker + *layer_idx += 1; + let layer_count = psd.psd.layer_count(); + while *layer_idx < layer_count { + let layer = &psd.psd.layer_and_mask_info.layer_info.layer_records[*layer_idx]; + let layer_type = if let Some(lsct) = layer.get_info(SECTION_DIVIDER_SETTING_KEY) { + let type_info = SectionDividerSetting::unpack( + &mut MemReaderRef::new(lsct), + true, + psd.encoding, + &None, + )?; + type_info.typ + } else { + 0 + }; + if layer_type == 1 || layer_type == 2 { + let is_closed = layer_type == 2; + return Ok(GroupLayer { + layer, + layer_idx: *layer_idx, + psd, + childrens, + is_closed, + }); + } else if layer_type == 3 { + childrens.push(Layer::Group(GroupLayer::create(layer_idx, psd)?)); + } else if layer_type == 0 { + childrens.push(Layer::Normal(NormalLayer { + layer, + layer_idx: *layer_idx, + psd, + })); + } else { + anyhow::bail!("Unknown layer section divider type: {}", layer_type); + } + *layer_idx += 1; + } + anyhow::bail!("Layer group does not have a start marker"); + } + + /// Returns whether the layer group is closed. + pub fn is_closed(&self) -> bool { + self.is_closed + } + + /// Returns the name of the layer group. + pub fn layer_name(&self) -> Result { + self.layer.layer_name(self.psd.encoding) + } + + /// Returns the current layer's index in the PSD file. + pub fn layer_index(&self) -> usize { + self.layer_idx + } +} + +impl<'a> Deref for GroupLayer<'a> { + type Target = Vec>; + + fn deref(&self) -> &Self::Target { + &self.childrens + } +} + +#[derive(Debug)] +pub enum Layer<'a> { + Normal(NormalLayer<'a>), + Group(GroupLayer<'a>), +} + +impl<'a> Layer<'a> { + /// Returns the name of the layer. + pub fn layer_name(&self) -> Result { + match self { + Layer::Normal(n) => n.layer_name(), + Layer::Group(g) => g.layer_name(), + } + } + + /// Returns the current layer's index in the PSD file. + pub fn layer_index(&self) -> usize { + match self { + Layer::Normal(n) => n.layer_index(), + Layer::Group(g) => g.layer_index(), + } + } +} + +impl PsdReader { + pub fn new(mut reader: T, encoding: Encoding) -> Result { + let psd = PsdFile::unpack(&mut reader, true, encoding, &None)?; + if psd.header.signature != *PSD_SIGNATURE { + anyhow::bail!("Invalid PSD signature"); + } + if psd.header.version != 1 { + anyhow::bail!("Unsupported PSD version: {}", psd.header.version); + } + let mut channel_start_indices = Vec::new(); + let mut idx = 0; + for layer in &psd.layer_and_mask_info.layer_info.layer_records { + channel_start_indices.push(idx); + idx += layer.base.channels as usize; + } + Ok(Self { + psd, + encoding, + channel_start_indices, + }) + } + + /// Returns the width of the PSD image. + pub fn width(&self) -> u32 { + self.psd.header.width + } + + /// Returns the height of the PSD image. + pub fn height(&self) -> u32 { + self.psd.header.height + } + + /// Returns the color mode of the PSD file. + /// + /// 1 = Grayscale, 3 = RGB, etc. + pub fn color_mode(&self) -> u16 { + self.psd.header.color_mode + } + + /// Returns the number of channels in the PSD file. + pub fn channels(&self) -> u16 { + self.psd.header.channels + } + + /// Returns the bit depth of the PSD file. + pub fn bit_depth(&self) -> u16 { + self.psd.header.depth + } + + /// Reads and returns the layers in the PSD file. + pub fn read_layers<'a>(&'a self) -> Result>> { + let mut layers = Vec::new(); + let mut layer_idx = 0; + let count = self.psd.layer_count(); + while layer_idx < count { + let layer = &self.psd.layer_and_mask_info.layer_info.layer_records[layer_idx]; + let layer_type = if let Some(lsct) = layer.get_info(SECTION_DIVIDER_SETTING_KEY) { + let type_info = SectionDividerSetting::unpack( + &mut MemReaderRef::new(lsct), + true, + self.encoding, + &None, + )?; + type_info.typ + } else { + 0 + }; + if layer_type == 1 || layer_type == 2 { + anyhow::bail!("Layer group does not have an end marker"); + } + if layer_type == 0 { + layers.push(Layer::Normal(NormalLayer { + layer, + layer_idx, + psd: self, + })); + } else if layer_type == 3 { + layers.push(Layer::Group(GroupLayer::create(&mut layer_idx, self)?)); + } else { + anyhow::bail!("Unknown layer section divider type: {}", layer_type); + } + layer_idx += 1; + } + Ok(layers) + } + + /// Returns the normal layers in the PSD file. This function ignores layer groups. + pub fn read_normal_layers<'a>(&'a self) -> Result>> { + let mut layers = Vec::new(); + let count = self.psd.layer_count(); + for layer_idx in 0..count { + let layer = &self.psd.layer_and_mask_info.layer_info.layer_records[layer_idx]; + let layer_type = if let Some(lsct) = layer.get_info(SECTION_DIVIDER_SETTING_KEY) { + let type_info = SectionDividerSetting::unpack( + &mut MemReaderRef::new(lsct), + true, + self.encoding, + &None, + )?; + type_info.typ + } else { + 0 + }; + if layer_type == 0 { + layers.push(NormalLayer { + layer, + layer_idx, + psd: self, + }); + } + } + Ok(layers) + } +} diff --git a/src/utils/psd/types.rs b/src/utils/psd/types.rs index d4b2913..b08e9d4 100644 --- a/src/utils/psd/types.rs +++ b/src/utils/psd/types.rs @@ -1,5 +1,6 @@ use crate::ext::io::*; use crate::types::*; +use crate::utils::encoding::*; use crate::utils::struct_pack::*; use anyhow::Result; use msg_tool_macro::*; @@ -10,6 +11,8 @@ pub const PSD_SIGNATURE: &[u8; 4] = b"8BPS"; pub const IMAGE_RESOURCE_SIGNATURE: &[u8; 4] = b"8BIM"; pub const LAYER_NAME_SOURCE_SETTING_KEY: &[u8; 4] = b"lnsr"; pub const LAYER_ID_KEY: &[u8; 4] = b"lyid"; +pub const SECTION_DIVIDER_SETTING_KEY: &[u8; 4] = b"lsct"; +pub const UNICODE_LAYER_KEY: &[u8; 4] = b"luni"; #[derive(Debug, Clone)] pub struct UnicodeString(pub String); @@ -176,6 +179,11 @@ impl StructUnpack for ImageResourceSection { while stream_region.cur_pos() < length as u64 { let resource = ImageResourceBlock::unpack(&mut stream_region, big, encoding, info)?; resources.push(resource); + if let Ok(d) = stream_region.peek_u8() { + if d == 0 { + stream_region.read_u8()?; // padding byte + } + } } Ok(ImageResourceSection { resources }) } @@ -192,6 +200,7 @@ impl StructPack for ImageResourceSection { let mut mem = MemWriter::new(); for resource in &self.resources { resource.pack(&mut mem, big, encoding, info)?; + // #TODO: check if padding byte is needed } let data = mem.into_inner(); let length = data.len() as u32; @@ -213,7 +222,7 @@ pub struct ImageResourceBlock { #[derive(Debug, Clone)] pub struct LayerAndMaskInfo { pub layer_info: LayerInfo, - pub global_layer_mask_info: GlobalLayerMaskInfo, + pub global_layer_mask_info: Option, pub tagged_blocks: Vec, } @@ -227,8 +236,18 @@ impl StructUnpack for LayerAndMaskInfo { let length = u32::unpack(reader, big, encoding, info)?; let mut stream_region = StreamRegion::with_size(reader, length as u64)?; let layer_info = LayerInfo::unpack(&mut stream_region, big, encoding, info)?; - let global_layer_mask_info = - GlobalLayerMaskInfo::unpack(&mut stream_region, big, encoding, info)?; + let length = u32::unpack(&mut stream_region, big, encoding, info)?; + let global_layer_mask_info = if length > 0 { + stream_region.seek_relative(-4)?; + Some(GlobalLayerMaskInfo::unpack( + &mut stream_region, + big, + encoding, + info, + )?) + } else { + None + }; let mut tagged_blocks = Vec::new(); stream_region.read_to_end(&mut tagged_blocks)?; Ok(LayerAndMaskInfo { @@ -249,8 +268,11 @@ impl StructPack for LayerAndMaskInfo { ) -> Result<()> { let mut mem = MemWriter::new(); self.layer_info.pack(&mut mem, big, encoding, info)?; - self.global_layer_mask_info - .pack(&mut mem, big, encoding, info)?; + if let Some(global_layer_mask_info) = &self.global_layer_mask_info { + global_layer_mask_info.pack(&mut mem, big, encoding, info)?; + } else { + 0u32.pack(&mut mem, big, encoding, info)?; // no global layer mask info + } mem.write_all(&self.tagged_blocks)?; let data = mem.into_inner(); let length = data.len() as u32; @@ -262,7 +284,7 @@ impl StructPack for LayerAndMaskInfo { #[derive(Debug, Clone)] pub struct LayerInfo { - pub layer_count: u16, + pub layer_count: i16, pub layer_records: Vec, pub channel_image_data: Vec, } @@ -276,21 +298,22 @@ impl StructUnpack for LayerInfo { ) -> Result { let length = u32::unpack(reader, big, encoding, info)?; let mut stream_region = StreamRegion::with_size(reader, length as u64)?; - let layer_count = u16::unpack(&mut stream_region, big, encoding, info)?; + let layer_count = i16::unpack(&mut stream_region, big, encoding, info)?; let mut layer_records = Vec::new(); - for _ in 0..layer_count { + for _ in 0..layer_count.abs() { let layer_record = LayerRecord::unpack(&mut stream_region, big, encoding, info)?; layer_records.push(layer_record); } let mut channel_image_data = Vec::new(); - for i in 0..layer_count { + for i in 0..layer_count.abs() { let layer = &layer_records[i as usize]; - let info = Some(Box::new(layer.clone()) as Box); - for _ in 0..layer.base.channels { + for j in 0..layer.base.channels { + let info = Some(Box::new((layer.clone(), j as usize)) as Box); let data = ChannelImageData::unpack(&mut stream_region, big, encoding, &info)?; channel_image_data.push(data); } } + stream_region.seek_to_end()?; Ok(LayerInfo { layer_count, layer_records, @@ -366,6 +389,27 @@ pub struct LayerRecord { pub infos: Vec, } +impl LayerRecord { + pub fn get_info<'a>(&'a self, key: &[u8; 4]) -> Option<&'a [u8]> { + for info in &self.infos { + if &info.key == key { + return Some(&info.data); + } + } + None + } + + pub fn layer_name(&self, encoding: Encoding) -> Result { + if let Some(uni) = self.get_info(UNICODE_LAYER_KEY) { + let data = UnicodeLayer::unpack(&mut MemReaderRef::new(uni), true, encoding, &None)?; + Ok(data.name.0) + } else { + let s = decode_to_string(encoding, &self.layer_name.0, true)?; + Ok(s) + } + } +} + impl StructPack for LayerRecord { fn pack( &self, @@ -548,6 +592,7 @@ impl StructUnpack for LayerMask { let mask_left = i32::unpack(&mut stream_region, big, encoding, info)?; let mask_bottom = i32::unpack(&mut stream_region, big, encoding, info)?; let mask_right = i32::unpack(&mut stream_region, big, encoding, info)?; + stream_region.seek_to_end()?; Ok(LayerMask { top, left, @@ -565,6 +610,7 @@ impl StructUnpack for LayerMask { mask_right: Some(mask_right), }) } else { + stream_region.seek_to_end()?; Ok(LayerMask { top, left, @@ -717,6 +763,17 @@ fn get_layer_info(info: &Option>) -> Result<&LayerRecord> { )) } +fn get_layer_info_with_channel_index(info: &Option>) -> Result<&(LayerRecord, usize)> { + if let Some(boxed) = info { + if let Some(layer_info) = boxed.downcast_ref::<(LayerRecord, usize)>() { + return Ok(layer_info); + } + } + Err(anyhow::anyhow!( + "LayerRecord and channel index info is required for ChannelImageData unpacking" + )) +} + impl StructUnpack for ChannelImageData { fn unpack( reader: &mut R, @@ -724,16 +781,17 @@ impl StructUnpack for ChannelImageData { encoding: Encoding, info: &Option>, ) -> Result { - let compression = u16::unpack(reader, big, encoding, info)?; - if compression != 0 { - return Err(anyhow::anyhow!( - "Only raw (compression == 0) channel image data is supported" - )); - } - let info = get_layer_info(info)?; - let len = (info.base.bottom - info.base.top) as usize - * (info.base.right - info.base.left) as usize; - let image_data = reader.read_exact_vec(len)?; + let (layer, idx) = get_layer_info_with_channel_index(info)?; + let layer_len = layer + .base + .channel_infos + .get(*idx) + .ok_or_else(|| anyhow::anyhow!("Channel index {} out of bounds for layer", idx))? + .length; + let mut stream_region = StreamRegion::with_size(reader, layer_len as u64)?; + let compression = u16::unpack(&mut stream_region, big, encoding, info)?; + let mut image_data = Vec::new(); + stream_region.read_to_end(&mut image_data)?; Ok(ChannelImageData { compression, image_data, @@ -782,6 +840,11 @@ impl StructUnpack for GlobalLayerMaskInfo { info: &Option>, ) -> Result { let length = u32::unpack(reader, big, encoding, info)?; + println!( + "GlobalLayerMaskInfo length = {}, stream position = {}", + length, + reader.stream_position()? + ); let mut stream_region = StreamRegion::with_size(reader, length as u64)?; let overlays_color_space = u16::unpack(&mut stream_region, big, encoding, info)?; let mut overlays_color_components = [0u16; 4]; @@ -793,6 +856,7 @@ impl StructUnpack for GlobalLayerMaskInfo { let filler_length = length as usize - 2 - (4 * 2) - 2 - 1; let mut filler = vec![0u8; filler_length]; stream_region.read_exact(&mut filler)?; + stream_region.seek_to_end()?; Ok(GlobalLayerMaskInfo { overlays_color_space, overlays_color_components, @@ -828,6 +892,7 @@ impl StructPack for GlobalLayerMaskInfo { } } +#[derive(Debug, Clone)] pub struct ImageDataSection { pub compression: u16, pub image_data: Vec, @@ -850,18 +915,8 @@ impl StructUnpack for ImageDataSection { info: &Option>, ) -> Result { let compression = u16::unpack(reader, big, encoding, info)?; - if compression != 0 { - return Err(anyhow::anyhow!( - "Only raw (compression == 0) image data is supported" - )); - } - let psd_header = get_psd_header(info)?; - let len = psd_header.channels as usize - * psd_header.height as usize - * psd_header.width as usize - * psd_header.depth as usize - / 8; - let image_data = reader.read_exact_vec(len)?; + let mut image_data = Vec::new(); + reader.read_to_end(&mut image_data)?; Ok(ImageDataSection { compression, image_data, @@ -896,6 +951,7 @@ impl StructPack for ImageDataSection { } } +#[derive(Debug)] pub struct PsdFile { pub header: PsdHeader, pub color_mode_data: ColorModeData, @@ -924,6 +980,35 @@ impl StructPack for PsdFile { } } +impl StructUnpack for PsdFile { + fn unpack( + reader: &mut R, + big: bool, + encoding: Encoding, + info: &Option>, + ) -> Result { + let header = PsdHeader::unpack(reader, big, encoding, info)?; + let psd_info = Some(Box::new(header.clone()) as Box); + let color_mode_data = ColorModeData::unpack(reader, big, encoding, &psd_info)?; + let image_resource = ImageResourceSection::unpack(reader, big, encoding, &psd_info)?; + let layer_and_mask_info = LayerAndMaskInfo::unpack(reader, big, encoding, &psd_info)?; + let image_data = ImageDataSection::unpack(reader, big, encoding, &psd_info)?; + Ok(PsdFile { + header, + color_mode_data, + image_resource, + layer_and_mask_info, + image_data, + }) + } +} + +impl PsdFile { + pub fn layer_count(&self) -> usize { + self.layer_and_mask_info.layer_info.layer_count.abs() as usize + } +} + #[derive(Debug, Clone, StructPack, StructUnpack)] pub struct SectionDividerSetting { pub typ: u32, @@ -938,7 +1023,7 @@ pub struct UnicodeLayer { #[derive(Debug, Clone, StructPack, StructUnpack)] pub struct LayerID { /// ID for the layer - pub id: i32, + pub id: u32, } #[derive(Debug, Clone, StructPack, StructUnpack)]