#![allow(unused)]
use std::{ffi::c_void, path::Path, ptr::null_mut};
use lazy_static::lazy_static;
#[cfg(unix)]
use libloading::os::unix as ll_impl;
#[cfg(windows)]
use libloading::os::windows as ll_impl;
use libloading::Library;
use parking_lot::RwLock;
use tracing::info;
#[repr(u32)]
enum OodleLzFuzzSafe {
No = 0,
Yes = 1,
}
#[repr(u32)]
enum OodleLzCheckCRC {
No = 0,
Yes = 1,
}
#[repr(u32)]
enum OodleLzVerbosity {
None = 0,
Minimal = 1,
Some = 2,
Lots = 3,
}
#[repr(u32)]
enum OodleLzThreadPhase {
ThreadPhase1 = 1,
ThreadPhase2 = 2,
ThreadPhaseAll = 3,
}
type OodleLzDecompress = unsafe extern "C" fn(
comp_buf: *const u8,
comp_buf_size: i64,
raw_buf: *mut u8,
raw_len: i64,
fuzz_safe: OodleLzFuzzSafe,
check_crc: OodleLzCheckCRC,
verbosity: OodleLzVerbosity,
dec_buf_base: *mut c_void,
dec_buf_size: *mut c_void,
fp_callback: *mut c_void,
callback_user_data: *mut c_void,
decoder_memory: *mut c_void,
decoder_memory_size: *const c_void,
thread_phase: OodleLzThreadPhase,
) -> i64;
#[derive(Clone, Copy)]
pub enum OodleVersion {
V3 = 3,
V9 = 9,
}
impl OodleVersion {
pub fn num(self) -> u32 {
match self {
OodleVersion::V3 => 3,
OodleVersion::V9 => 9,
}
}
}
pub struct Oodle {
_lib: Library,
fn_decompress: ll_impl::Symbol<OodleLzDecompress>,
}
unsafe impl Send for Oodle {}
unsafe impl Sync for Oodle {}
impl Oodle {
pub fn new(version: OodleVersion) -> anyhow::Result<Oodle> {
#[cfg(target_os = "windows")]
let lib_path = format!("oo2core_{}_win64.dll", version.num());
#[cfg(target_os = "linux")]
let lib_path = match version {
OodleVersion::V3 => "liblinoodle3.so".to_string(),
OodleVersion::V9 => "liboodle-data-shared.so".to_string(),
};
#[cfg(target_os = "macos")]
compile_error!("macOS is not supported for Oodle decompression!");
let exe_dir = std::env::current_exe()?
.parent()
.ok_or_else(|| anyhow::anyhow!("Failed to get current exe directory"))?
.to_path_buf();
let oodle = Self::from_path(exe_dir.join(&lib_path))
.or_else(|_| Self::from_path(std::env::current_dir()?.join(&lib_path)))
.or_else(|_| Self::from_path(&lib_path))?;
info!("Successfully loaded Oodle {}", version.num());
Ok(oodle)
}
pub fn from_path<P: AsRef<Path>>(path: P) -> anyhow::Result<Oodle> {
let path = path.as_ref();
let lib = unsafe { Library::new(path)? };
let fn_decompress = unsafe {
lib.get::<OodleLzDecompress>(b"OodleLZ_Decompress")?
.into_raw()
};
info!(
"Successfully loaded Oodle from {}",
path.canonicalize()?.display()
);
Ok(Oodle {
_lib: lib,
fn_decompress,
})
}
pub fn decompress(&self, buffer: &[u8], output_buffer: &mut [u8]) -> i64 {
unsafe {
(self.fn_decompress)(
buffer.as_ptr() as *mut u8,
buffer.len() as i64,
output_buffer.as_mut_ptr(),
output_buffer.len() as i64,
OodleLzFuzzSafe::Yes,
OodleLzCheckCRC::No,
OodleLzVerbosity::Minimal,
null_mut(),
null_mut(),
null_mut(),
null_mut(),
null_mut(),
null_mut(),
OodleLzThreadPhase::ThreadPhaseAll,
)
}
}
}
lazy_static! {
pub static ref OODLE_3: RwLock<anyhow::Result<Oodle>> =
RwLock::new(Oodle::new(OodleVersion::V3));
pub static ref OODLE_9: RwLock<anyhow::Result<Oodle>> =
RwLock::new(Oodle::new(OodleVersion::V9));
}
pub fn decompress_3(buffer: &[u8], output_buffer: &mut [u8]) -> anyhow::Result<i64> {
OODLE_3
.read()
.as_ref()
.map(|o| o.decompress(buffer, output_buffer))
.map_err(|e| panic!("Oodle 3 failed to load: {e}"))
}
pub fn decompress_9(buffer: &[u8], output_buffer: &mut [u8]) -> anyhow::Result<i64> {
OODLE_9
.read()
.as_ref()
.map(|o| o.decompress(buffer, output_buffer))
.map_err(|e| panic!("Oodle 9 failed to load: {e}"))
}