mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 15:22:53 -05:00
119 lines
3.2 KiB
Rust
119 lines
3.2 KiB
Rust
use std::io::{Read, Seek};
|
|
use byteorder::{LittleEndian, ReadBytesExt};
|
|
use crate::common::CzError;
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct ChunkInfo {
|
|
pub size_compressed: usize,
|
|
pub size_raw: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct CompressionInfo {
|
|
pub chunk_count: usize,
|
|
pub total_size_compressed: usize,
|
|
pub total_size_raw: usize,
|
|
pub chunks: Vec<ChunkInfo>,
|
|
|
|
/// Length of the compression chunk info
|
|
pub length: usize,
|
|
}
|
|
|
|
/// Get info about the compression chunks
|
|
pub fn parse_chunk_info<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<CompressionInfo, CzError> {
|
|
let parts_count = bytes.read_u32::<LittleEndian>()?;
|
|
|
|
let mut part_sizes = vec![];
|
|
let mut total_size = 0;
|
|
let mut total_size_raw = 0;
|
|
|
|
// Loop over the compressed bytes
|
|
for _ in 0..parts_count {
|
|
let compressed_size = bytes.read_u32::<LittleEndian>()? * 2;
|
|
total_size += compressed_size;
|
|
|
|
let raw_size = bytes.read_u32::<LittleEndian>()?;
|
|
total_size_raw += raw_size;
|
|
|
|
part_sizes.push(ChunkInfo {
|
|
size_compressed: compressed_size as usize,
|
|
size_raw: raw_size as usize,
|
|
});
|
|
}
|
|
|
|
Ok(CompressionInfo {
|
|
chunk_count: parts_count as usize,
|
|
total_size_compressed: total_size as usize,
|
|
total_size_raw: total_size_raw as usize,
|
|
chunks: part_sizes,
|
|
length: bytes.stream_position()? as usize,
|
|
})
|
|
}
|
|
|
|
/// Decompress an LZW compressed stream, like CZ1
|
|
pub fn decompress<T: Seek + ReadBytesExt + Read>(
|
|
input: &mut T,
|
|
chunk_info: &CompressionInfo,
|
|
) -> Result<Vec<u8>, CzError> {
|
|
let mut m_dst = 0;
|
|
let mut bitmap = vec![0; chunk_info.total_size_raw];
|
|
for chunk in &chunk_info.chunks {
|
|
let mut part = vec![0u8; chunk.size_compressed];
|
|
input.read_exact(&mut part)?;
|
|
|
|
for j in (0..part.len()).step_by(2) {
|
|
let ctl = part[j + 1];
|
|
|
|
if ctl == 0 {
|
|
bitmap[m_dst] = part[j];
|
|
m_dst += 1;
|
|
} else {
|
|
m_dst += copy_range(&mut bitmap, &part, get_offset(&part, j), m_dst);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(bitmap)
|
|
}
|
|
|
|
fn get_offset(input: &[u8], src: usize) -> usize {
|
|
(((input[src] as usize) | (input[src + 1] as usize) << 8) - 0x101) * 2
|
|
}
|
|
|
|
fn copy_range(bitmap: &mut Vec<u8>, input: &[u8], src: usize, dst: usize) -> usize {
|
|
let mut dst = dst;
|
|
let start_pos = dst;
|
|
|
|
if input[src + 1] == 0 {
|
|
bitmap[dst] = input[src];
|
|
dst += 1;
|
|
} else if get_offset(input, src) == src {
|
|
bitmap[dst] = 0;
|
|
dst += 1;
|
|
} else {
|
|
dst += copy_range(bitmap, input, get_offset(input, src), dst);
|
|
}
|
|
|
|
if input[src + 3] == 0 {
|
|
bitmap[dst] = input[src + 2];
|
|
dst += 1;
|
|
} else if get_offset(input, src + 2) == src {
|
|
bitmap[dst] = bitmap[start_pos];
|
|
dst += 1;
|
|
} else {
|
|
bitmap[dst] = copy_one(input, get_offset(input, src + 2));
|
|
dst += 1;
|
|
}
|
|
|
|
dst - start_pos
|
|
}
|
|
|
|
fn copy_one(input: &[u8], src: usize) -> u8 {
|
|
if input[src + 1] == 0 {
|
|
input[src]
|
|
} else if get_offset(input, src) == src {
|
|
0
|
|
} else {
|
|
copy_one(input, get_offset(input, src))
|
|
}
|
|
}
|