111 lines
3.9 KiB
Rust
111 lines
3.9 KiB
Rust
use super::{bits::*, Error};
|
|
use byteorder::{ByteOrder, LittleEndian};
|
|
use lz4::{BlockMode, EncoderBuilder};
|
|
|
|
pub use lz4::BlockSize;
|
|
|
|
pub fn lz4(data: &Vec<u8>, block_size: BlockSize, compression_level: u32) -> Result<Vec<u8>, Error> {
|
|
let mut lz4_encoder = EncoderBuilder::new().level(compression_level).checksum(lz4::ContentChecksum::NoChecksum).block_size(block_size).block_mode(BlockMode::Linked).build(Vec::<u8>::new())?;
|
|
|
|
std::io::copy(&mut&data[..], &mut lz4_encoder)?;
|
|
let (output, result) = lz4_encoder.finish();
|
|
|
|
match result {
|
|
Ok(()) => Ok(output),
|
|
Err(error) => Err(Error::from_error(error))
|
|
}
|
|
}
|
|
|
|
pub fn strip_lz4(compressed_data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
|
fn read_header(raw_data: &[u8]) -> Result<(usize, bool, bool), Error> {
|
|
const FRAME_HEADER_MIN_SIZE:usize = 7;
|
|
const MAGIC_NUMBER:u32 = 0x184D2204u32;
|
|
const BLOCK_CHECKSUM_BIT:usize = 4;
|
|
const CONTENT_SIZE_BIT:usize = 3;
|
|
const CONTENT_CHECKSUM_BIT:usize = 2;
|
|
const DICT_ID_BIT:usize = 0;
|
|
|
|
let mut bytes_to_skip = FRAME_HEADER_MIN_SIZE;
|
|
if raw_data.len() >= FRAME_HEADER_MIN_SIZE {
|
|
let magic_number = LittleEndian::read_u32(raw_data);
|
|
if magic_number.to_le() == MAGIC_NUMBER {
|
|
let raw_data = &raw_data[std::mem::size_of::<u32>()..];
|
|
let flg = raw_data[0];
|
|
let _bd = raw_data[1];
|
|
|
|
if bit_set_u8(flg, CONTENT_SIZE_BIT) {
|
|
bytes_to_skip += std::mem::size_of::<u64>();
|
|
}
|
|
|
|
if bit_set_u8(flg, DICT_ID_BIT) {
|
|
bytes_to_skip += std::mem::size_of::<u32>();
|
|
}
|
|
|
|
if raw_data.len() < bytes_to_skip {
|
|
Err(Error::from_text(format!("Can't skip {} bytes of frame header because there are only {} bytes left", bytes_to_skip, raw_data.len())))
|
|
}
|
|
|
|
else {
|
|
Ok((bytes_to_skip, bit_set_u8(flg, BLOCK_CHECKSUM_BIT), bit_set_u8(flg, CONTENT_CHECKSUM_BIT)))
|
|
}
|
|
}
|
|
|
|
else {
|
|
Err(Error::from_text(format!("Magic number needs to be {:#08x} - but {:#08x} was found!", MAGIC_NUMBER, magic_number.to_le())))
|
|
}
|
|
}
|
|
|
|
else {
|
|
Err(Error::from_str("Not enough bytes for minimum header size"))
|
|
}
|
|
}
|
|
|
|
let (bytes_to_skip, has_block_checksum, has_content_checksum) = read_header(&compressed_data)?;
|
|
let mut raw_data = &compressed_data[bytes_to_skip..];
|
|
let end_len = {
|
|
if has_content_checksum {
|
|
std::mem::size_of::<u32>()*2
|
|
}
|
|
|
|
else {
|
|
std::mem::size_of::<u32>()
|
|
}
|
|
};
|
|
let mut new_data = Vec::new();
|
|
|
|
while raw_data.len() > end_len {
|
|
let block_size = LittleEndian::read_u32(raw_data);
|
|
raw_data = &raw_data[std::mem::size_of::<u32>()..];
|
|
|
|
if bit_set_u32(block_size, 31) {
|
|
return Err(Error::from_str("Stripping uncompressed data is not supported!"));
|
|
}
|
|
|
|
let block_size = block_size as usize;
|
|
new_data.extend_from_slice(&raw_data[0..block_size]);
|
|
raw_data = &raw_data[block_size..];
|
|
|
|
if has_block_checksum {
|
|
raw_data = &raw_data[std::mem::size_of::<u32>()..];
|
|
}
|
|
}
|
|
|
|
if LittleEndian::read_u32(raw_data) != 0 {
|
|
Err(Error::from_str("EOF is not zero!"))
|
|
}
|
|
|
|
else {
|
|
Ok(new_data)
|
|
}
|
|
}
|
|
|
|
pub mod psx_default {
|
|
use super::*;
|
|
|
|
const DEFAULT_COMPRESSION_LEVEL:u32 = 16;
|
|
const DEFAULT_BLOCK_SIZE:BlockSize = BlockSize::Max4MB;
|
|
|
|
pub fn lz4(data: &Vec<u8>) -> Result<Vec<u8>, Error> {
|
|
strip_lz4(super::lz4(data, DEFAULT_BLOCK_SIZE, DEFAULT_COMPRESSION_LEVEL)?)
|
|
}
|
|
} |