#![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] use std::ptr; use color_eyre::{eyre, Result}; #[allow(dead_code)] #[allow(clippy::identity_op)] mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } // Hardcoded chunk size of Bitsquid's bundle compression pub const CHUNK_SIZE: usize = 512 * 1024; pub const COMPRESSOR: bindings::OodleLZ_Compressor = bindings::OodleLZ_Compressor_OodleLZ_Compressor_Kraken; pub const LEVEL: bindings::OodleLZ_CompressionLevel = bindings::OodleLZ_CompressionLevel_OodleLZ_CompressionLevel_Optimal2; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum OodleLZ_FuzzSafe { Yes, No, } impl From for bindings::OodleLZ_FuzzSafe { fn from(value: OodleLZ_FuzzSafe) -> Self { match value { OodleLZ_FuzzSafe::Yes => bindings::OodleLZ_FuzzSafe_OodleLZ_FuzzSafe_Yes, OodleLZ_FuzzSafe::No => bindings::OodleLZ_FuzzSafe_OodleLZ_FuzzSafe_No, } } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum OodleLZ_CheckCRC { Yes, No, } impl From for bindings::OodleLZ_CheckCRC { fn from(value: OodleLZ_CheckCRC) -> Self { match value { OodleLZ_CheckCRC::Yes => bindings::OodleLZ_CheckCRC_OodleLZ_CheckCRC_Yes, OodleLZ_CheckCRC::No => bindings::OodleLZ_CheckCRC_OodleLZ_CheckCRC_No, } } } #[tracing::instrument(skip(data))] pub fn decompress( data: I, fuzz_safe: OodleLZ_FuzzSafe, check_crc: OodleLZ_CheckCRC, ) -> Result> where I: AsRef<[u8]>, { let data = data.as_ref(); let mut out = vec![0; CHUNK_SIZE]; let verbosity = if tracing::enabled!(tracing::Level::INFO) { bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Minimal } else if tracing::enabled!(tracing::Level::DEBUG) { bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Some } else if tracing::enabled!(tracing::Level::TRACE) { bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Lots } else { bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_None }; let ret = unsafe { bindings::OodleLZ_Decompress( data.as_ptr() as *const _, data.len() as isize, out.as_mut_ptr() as *mut _, out.len() as isize, fuzz_safe.into(), check_crc.into(), verbosity, ptr::null_mut(), 0, None, ptr::null_mut(), ptr::null_mut(), 0, bindings::OodleLZ_Decode_ThreadPhase_OodleLZ_Decode_Unthreaded, ) }; if ret == 0 { eyre::bail!("Decompression failed"); } Ok(out) } #[tracing::instrument(skip(data))] pub fn compress(data: I) -> Result> where I: AsRef<[u8]>, { let mut raw = Vec::from(data.as_ref()); raw.resize(CHUNK_SIZE, 0); // TODO: Query oodle for buffer size let mut out = vec![0u8; CHUNK_SIZE]; let ret = unsafe { bindings::OodleLZ_Compress( COMPRESSOR, raw.as_ptr() as *const _, raw.len() as isize, out.as_mut_ptr() as *mut _, LEVEL, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), 0, ) }; tracing::debug!(compressed_size = ret, "Compressed chunk"); if ret == 0 { eyre::bail!("Compression failed"); } out.resize(ret as usize, 0); Ok(out) } pub fn get_decode_buffer_size(raw_size: usize, corruption_possible: bool) -> Result { let size = unsafe { bindings::OodleLZ_GetDecodeBufferSize( COMPRESSOR, raw_size as isize, if corruption_possible { 1 } else { 0 }, ) }; Ok(size as usize) }