dtmt/lib/oodle-sys/src/library.rs
Lucas Schwiderski 9f84340b73
refactor: Extract Oodle into separate library
The library utilizes an internal global singleton to allow
using the functions without having to lug around an instance of
`libloading::Library`.
2023-02-08 14:33:47 +01:00

154 lines
4.2 KiB
Rust

use std::{ffi::OsStr, ptr};
use libloading::Symbol;
use super::Result;
use crate::{types::*, OodleError};
// Hardcoded chunk size of Bitsquid's bundle compression
pub const CHUNK_SIZE: usize = 512 * 1024;
pub const COMPRESSOR: OodleLZ_Compressor = OodleLZ_Compressor::Kraken;
pub const LEVEL: OodleLZ_CompressionLevel = OodleLZ_CompressionLevel::Optimal2;
#[cfg(target_os = "windows")]
const OODLE_LIB_NAME: &str = "oo2core_8_win64";
#[cfg(target_os = "linux")]
const OODLE_LIB_NAME: &str = "liboo2corelinux64.so";
pub struct Library {
inner: libloading::Library,
}
impl Library {
/// Load the Oodle library by its default name.
///
/// The default name is platform-specific:
/// - Windows: `oo2core_8_win64`
/// - Linux: `liboo2corelinux64.so`
///
/// # Safety
///
/// The safety concerns as described by [`libloading::Library::new`] apply.
pub unsafe fn new() -> Result<Self> {
Self::with_name(OODLE_LIB_NAME)
}
/// Load the Oodle library by the given name or path.
///
/// See [`libloading::Library::new`] for how the `name` parameter is handled.
///
/// # Safety
///
/// The safety concerns as described by [`libloading::Library::new`] apply.
pub unsafe fn with_name<P: AsRef<OsStr>>(name: P) -> Result<Self> {
let inner = libloading::Library::new(name)?;
Ok(Self { inner })
}
#[tracing::instrument(skip(self, data))]
pub fn decompress<I>(
&self,
data: I,
fuzz_safe: OodleLZ_FuzzSafe,
check_crc: OodleLZ_CheckCRC,
) -> Result<Vec<u8>>
where
I: AsRef<[u8]>,
{
let data = data.as_ref();
let mut out = vec![0; CHUNK_SIZE];
let verbosity = if tracing::enabled!(tracing::Level::INFO) {
OodleLZ_Verbosity::Minimal
} else if tracing::enabled!(tracing::Level::DEBUG) {
OodleLZ_Verbosity::Some
} else if tracing::enabled!(tracing::Level::TRACE) {
OodleLZ_Verbosity::Lots
} else {
OodleLZ_Verbosity::None
};
let ret = unsafe {
let decompress: Symbol<OodleLZ_Decompress> = self.inner.get(b"OodleLZ_Decompress\0")?;
decompress(
data.as_ptr() as *const _,
data.len(),
out.as_mut_ptr() as *mut _,
out.len(),
fuzz_safe,
check_crc,
verbosity,
ptr::null_mut(),
0,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
OodleLZ_Decode_ThreadPhase::UNTHREADED,
)
};
if ret == 0 {
let err = OodleError::Oodle(String::from("Decompression failed."));
return Err(err);
}
Ok(out)
}
#[tracing::instrument(name = "Oodle::compress", skip(self, data))]
pub fn compress<I>(&self, data: I) -> Result<Vec<u8>>
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 {
let compress: Symbol<OodleLZ_Compress> = self.inner.get(b"OodleLZ_Compress\0")?;
compress(
COMPRESSOR,
raw.as_ptr() as *const _,
raw.len(),
out.as_mut_ptr() as *mut _,
LEVEL,
ptr::null_mut(),
0,
ptr::null_mut(),
ptr::null_mut(),
0,
)
};
tracing::debug!(compressed_size = ret, "Compressed chunk");
if ret == 0 {
let err = OodleError::Oodle(String::from("Compression failed."));
return Err(err);
}
out.resize(ret as usize, 0);
Ok(out)
}
pub fn get_decode_buffer_size(
&self,
raw_size: usize,
corruption_possible: bool,
) -> Result<usize> {
unsafe {
let f: Symbol<OodleLZ_GetDecodeBufferSize> =
self.inner.get(b"OodleLZ_GetDecodeBufferSize\0")?;
let size = f(COMPRESSOR, raw_size, corruption_possible);
Ok(size)
}
}
}