tiger-pkg 0.20.1

Tiger engine package reading library for Destiny 1/2 and Marathon
Documentation
#![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));
}

/// Fails if the library isn't loaded
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}"))
}

/// Fails if the library isn't loaded
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}"))
}