Add import psd support for Qlie tiled PNG image (.png)

This commit is contained in:
2026-02-04 17:49:36 +08:00
parent 33a513ef15
commit 1502dee4e9
6 changed files with 760 additions and 113 deletions

View File

@@ -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 = []

View File

@@ -218,7 +218,7 @@ msg-tool create -t <script-type> <input> <output>
|---|---|---|---|---|---|---|---|---|---|---|
| `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 |
|---|---|---|---|---|---|

View File

@@ -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<dyn WriteSeek + 'a>,
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>(

View File

@@ -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<u8> {
let start = 0;
let line_end = data.len();
let mut idx = start;
let mut literal: Vec<u8> = Vec::new();
let mut out_line: Vec<u8> = 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<Vec<u8>> {
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),
}
}

View File

@@ -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<AdditionalLayerInfo> {
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"</Layer group>".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<u8> = Vec::new();
let mut out_line: Vec<u8> = 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<usize>,
}
#[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<String> {
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<Vec<(i16, Vec<u8>)>> {
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<ImageData> {
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<Layer<'a>>,
is_closed: bool,
}
impl<'a> GroupLayer<'a> {
fn create<'b>(layer_idx: &'b mut usize, psd: &'a PsdReader) -> Result<Self> {
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<String> {
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<Layer<'a>>;
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<String> {
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<T: Read + Seek>(mut reader: T, encoding: Encoding) -> Result<Self> {
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<Vec<Layer<'a>>> {
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<Vec<NormalLayer<'a>>> {
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)
}
}

View File

@@ -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<GlobalLayerMaskInfo>,
pub tagged_blocks: Vec<u8>,
}
@@ -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<LayerRecord>,
pub channel_image_data: Vec<ChannelImageData>,
}
@@ -276,21 +298,22 @@ impl StructUnpack for LayerInfo {
) -> Result<Self> {
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<dyn Any>);
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<dyn Any>);
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<AdditionalLayerInfo>,
}
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<String> {
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<W: Write>(
&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<Box<dyn Any>>) -> Result<&LayerRecord> {
))
}
fn get_layer_info_with_channel_index(info: &Option<Box<dyn Any>>) -> 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<R: Read + Seek>(
reader: &mut R,
@@ -724,16 +781,17 @@ impl StructUnpack for ChannelImageData {
encoding: Encoding,
info: &Option<Box<dyn Any>>,
) -> Result<Self> {
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<Box<dyn Any>>,
) -> Result<Self> {
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<u8>,
@@ -850,18 +915,8 @@ impl StructUnpack for ImageDataSection {
info: &Option<Box<dyn Any>>,
) -> Result<Self> {
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<R: Read + Seek>(
reader: &mut R,
big: bool,
encoding: Encoding,
info: &Option<Box<dyn Any>>,
) -> Result<Self> {
let header = PsdHeader::unpack(reader, big, encoding, info)?;
let psd_info = Some(Box::new(header.clone()) as Box<dyn Any>);
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)]