Change Oodle source and add compression wrapper

This commit is contained in:
Truman Kilen 2025-01-17 18:12:18 -06:00
parent cd349a95f5
commit 255486b962
6 changed files with 270 additions and 437 deletions

53
Cargo.lock generated
View file

@ -229,21 +229,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.4.2" version = "1.4.2"
@ -416,12 +401,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hex-literal"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
[[package]] [[package]]
name = "icu_collections" name = "icu_collections"
version = "1.5.0" version = "1.5.0"
@ -658,16 +637,6 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lzma-rs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
dependencies = [
"byteorder",
"crc",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"
@ -689,15 +658,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.20.2" version = "1.20.2"
@ -710,13 +670,8 @@ version = "0.2.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hex", "hex",
"hex-literal",
"libc",
"libloading", "libloading",
"lzma-rs", "sha2",
"object",
"seq-macro",
"sha1",
"ureq", "ureq",
] ]
@ -941,12 +896,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "seq-macro"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.217" version = "1.0.217"

View file

@ -6,18 +6,9 @@ license.workspace = true
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
[target.'cfg(windows)'.dependencies]
libloading = "0.8"
[target.'cfg(unix)'.dependencies]
object = { version = "0.36.7", default-features = false, features = ["std", "read"] }
libc = "0.2.169"
seq-macro = "0.3.5"
[dependencies] [dependencies]
sha1 = { workspace = true } libloading = "0.8"
ureq = "2.12" ureq = "2.12"
hex-literal = "0.4"
hex = { workspace = true } hex = { workspace = true }
anyhow = "1.0.95" anyhow = "1.0.95"
lzma-rs = "0.3.0" sha2 = "0.10.8"

View file

@ -1,405 +1,298 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{bail, Result};
use std::sync::OnceLock; use std::{
io::{Read, Write},
sync::OnceLock,
};
type OodleDecompress = fn(comp_buf: &[u8], raw_buf: &mut [u8]) -> i32; pub use oodle_lz::{CompressionLevel, Compressor};
#[allow(non_camel_case_types)] mod oodle_lz {
type OodleLZ_Decompress = unsafe extern "win64" fn( #[derive(Debug, Clone, Copy)]
compBuf: *const u8, #[repr(i32)]
compBufSize: usize, pub enum Compressor {
rawBuf: *mut u8, /// None = memcpy, pass through uncompressed bytes
rawLen: usize, None = 3,
fuzzSafe: u32,
checkCRC: u32,
verbosity: u32,
decBufBase: u64,
decBufSize: usize,
fpCallback: u64,
callbackUserData: u64,
decoderMemory: *mut u8,
decoderMemorySize: usize,
threadPhase: u32,
) -> i32;
pub fn decompress() -> Result<OodleDecompress, Box<dyn std::error::Error>> { /// Fast decompression and high compression ratios, amazing!
#[cfg(windows)] Kraken = 8,
return Ok(windows_oodle::decompress_wrapper_windows); /// Leviathan = Kraken's big brother with higher compression, slightly slower decompression.
#[cfg(unix)] Leviathan = 13,
return Ok(linux_oodle::oodle_loader_linux()); /// Mermaid is between Kraken & Selkie - crazy fast, still decent compression.
} Mermaid = 9,
/// Selkie is a super-fast relative of Mermaid. For maximum decode speed.
fn call_decompress(comp_buf: &[u8], raw_buf: &mut [u8], decompress: OodleLZ_Decompress) -> i32 { Selkie = 11,
unsafe { /// Hydra, the many-headed beast = Leviathan, Kraken, Mermaid, or Selkie (see $OodleLZ_About_Hydra)
decompress( Hydra = 12,
comp_buf.as_ptr(),
comp_buf.len(),
raw_buf.as_mut_ptr(),
raw_buf.len(),
1,
1,
0,
0,
0,
0,
0,
std::ptr::null_mut(),
0,
3,
)
}
}
static OODLE_HASH: [u8; 20] = hex_literal::hex!("4bcc73614cb8fd2b0bce8d0f91ee5f3202d9d624");
static OODLE_DLL_NAME: &str = "oo2core_9_win64.dll";
fn fetch_oodle() -> Result<std::path::PathBuf> {
use sha1::{Digest, Sha1};
let oodle_path = std::env::current_exe()?.with_file_name(OODLE_DLL_NAME);
if !oodle_path.exists() {
let mut compressed = vec![];
ureq::get("https://origin.warframe.com/origin/50F7040A/index.txt.lzma")
.call()?
.into_reader()
.read_to_end(&mut compressed)?;
let mut decompressed = vec![];
lzma_rs::lzma_decompress(&mut std::io::Cursor::new(compressed), &mut decompressed).unwrap();
let index = String::from_utf8(decompressed)?;
let line = index
.lines()
.find(|l| l.contains(OODLE_DLL_NAME))
.with_context(|| format!("{OODLE_DLL_NAME} not found in index"))?;
let path = line.split_once(',').context("failed to parse index")?.0;
let mut compressed = vec![];
ureq::get(&format!("https://content.warframe.com{path}"))
.call()?
.into_reader()
.read_to_end(&mut compressed)?;
let mut decompressed = vec![];
lzma_rs::lzma_decompress(&mut std::io::Cursor::new(compressed), &mut decompressed).unwrap();
std::fs::write(&oodle_path, decompressed)?;
} }
let mut hasher = Sha1::new(); #[derive(Debug, Clone, Copy)]
hasher.update(std::fs::read(&oodle_path)?); #[repr(i32)]
let hash = hasher.finalize(); pub enum CompressionLevel {
(hash[..] == OODLE_HASH).then_some(()).ok_or_else(|| { /// don't compress, just copy raw bytes
anyhow!( None = 0,
"oodle hash mismatch expected: {} got: {} ", /// super fast mode, lower compression ratio
hex::encode(OODLE_HASH), SuperFast = 1,
hex::encode(hash) /// fastest LZ mode with still decent compression ratio
) VeryFast = 2,
})?; /// fast - good for daily use
Fast = 3,
/// standard medium speed LZ mode
Normal = 4,
Ok(oodle_path) /// optimal parse level 1 (faster optimal encoder)
Optimal1 = 5,
/// optimal parse level 2 (recommended baseline optimal encoder)
Optimal2 = 6,
/// optimal parse level 3 (slower optimal encoder)
Optimal3 = 7,
/// optimal parse level 4 (very slow optimal encoder)
Optimal4 = 8,
/// optimal parse level 5 (don't care about encode speed, maximum compression)
Optimal5 = 9,
/// faster than SuperFast, less compression
HyperFast1 = -1,
/// faster than HyperFast1, less compression
HyperFast2 = -2,
/// faster than HyperFast2, less compression
HyperFast3 = -3,
/// fastest, less compression
HyperFast4 = -4,
}
pub type Compress = unsafe extern "system" fn(
compressor: Compressor,
rawBuf: *const u8,
rawLen: usize,
compBuf: *mut u8,
level: CompressionLevel,
pOptions: *const (),
dictionaryBase: *const (),
lrm: *const (),
scratchMem: *mut u8,
scratchSize: usize,
) -> isize;
pub type Decompress = unsafe extern "system" fn(
compBuf: *const u8,
compBufSize: usize,
rawBuf: *mut u8,
rawLen: usize,
fuzzSafe: u32,
checkCRC: u32,
verbosity: u32,
decBufBase: u64,
decBufSize: usize,
fpCallback: u64,
callbackUserData: u64,
decoderMemory: *mut u8,
decoderMemorySize: usize,
threadPhase: u32,
) -> isize;
pub type GetCompressedBufferSizeNeeded =
unsafe extern "system" fn(compressor: Compressor, rawSize: usize) -> usize;
} }
#[cfg(windows)] static OODLE_VERSION: &str = "2.9.10";
mod windows_oodle { static OODLE_BASE_URL: &str = "https://github.com/WorkingRobot/OodleUE/raw/refs/heads/main/Engine/Source/Programs/Shared/EpicGames.Oodle/Sdk/";
use super::*;
static DECOMPRESS: OnceLock<(OodleLZ_Decompress, libloading::Library)> = OnceLock::new(); struct OodlePlatform {
path: &'static str,
pub fn decompress_wrapper_windows(comp_buf: &[u8], raw_buf: &mut [u8]) -> i32 { name: &'static str,
let decompress = DECOMPRESS.get_or_init(|| { hash: &'static str,
let path = fetch_oodle().context("failed to fetch oodle").unwrap();
let lib = unsafe { libloading::Library::new(path) }
.context("failed to load oodle")
.unwrap();
(*unsafe { lib.get(b"OodleLZ_Decompress") }.unwrap(), lib)
});
call_decompress(comp_buf, raw_buf, decompress.0)
}
} }
#[cfg(unix)] #[cfg(unix)]
mod linux_oodle { static OODLE_PLATFORM: OodlePlatform = OodlePlatform {
use super::*; path: "linux/lib",
name: "liboo2corelinux64.so.9",
hash: "ed7e98f70be1254a80644efd3ae442ff61f854a2fe9debb0b978b95289884e9c",
};
use object::pe::{ #[cfg(windows)]
ImageNtHeaders64, IMAGE_REL_BASED_DIR64, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, static OODLE_PLATFORM: OodlePlatform = OodlePlatform {
IMAGE_SCN_MEM_WRITE, path: "win/redist",
}; name: "oo2core_9_win64.dll",
use object::read::pe::{ImageOptionalHeader, ImageThunkData, PeFile64}; hash: "6f5d41a7892ea6b2db420f2458dad2f84a63901c9a93ce9497337b16c195f457",
};
use object::{LittleEndian as LE, Object, ObjectSection}; fn url() -> String {
use std::collections::HashMap; format!(
use std::ffi::{c_void, CStr}; "{OODLE_BASE_URL}/{}/{}/{}",
OODLE_VERSION, OODLE_PLATFORM.path, OODLE_PLATFORM.name
)
}
#[repr(C)] fn check_hash(buffer: &[u8]) -> Result<()> {
struct ThreadInformationBlock { use sha2::{Digest, Sha256};
exception_list: *const c_void,
stack_base: *const c_void, let mut hasher = Sha256::new();
stack_limit: *const c_void, hasher.update(buffer);
sub_system_tib: *const c_void, let hash = hex::encode(hasher.finalize());
fiber_data: *const c_void, if hash != OODLE_PLATFORM.hash {
arbitrary_user_pointer: *const c_void, anyhow::bail!(
teb: *const c_void, "Oodle library hash mismatch: expected {} got {}",
OODLE_PLATFORM.hash,
hash
);
} }
const TIB: ThreadInformationBlock = ThreadInformationBlock { Ok(())
exception_list: std::ptr::null(), }
stack_base: std::ptr::null(),
stack_limit: std::ptr::null(),
sub_system_tib: std::ptr::null(),
fiber_data: std::ptr::null(),
arbitrary_user_pointer: std::ptr::null(),
teb: std::ptr::null(),
};
static DECOMPRESS: OnceLock<OodleLZ_Decompress> = OnceLock::new(); fn fetch_oodle() -> Result<std::path::PathBuf> {
let oodle_path = std::env::current_exe()?.with_file_name(OODLE_PLATFORM.name);
if !oodle_path.exists() {
let mut buffer = vec![];
ureq::get(&url())
.call()?
.into_reader()
.read_to_end(&mut buffer)?;
check_hash(&buffer)?;
std::fs::write(&oodle_path, buffer)?;
}
check_hash(&std::fs::read(&oodle_path)?)?;
Ok(oodle_path)
}
fn decompress_wrapper(comp_buf: &[u8], raw_buf: &mut [u8]) -> i32 { pub struct Oodle {
_library: libloading::Library,
compress: oodle_lz::Compress,
decompress: oodle_lz::Decompress,
get_compressed_buffer_size_needed: oodle_lz::GetCompressedBufferSizeNeeded,
}
impl Oodle {
pub fn compress<S: Write>(
&self,
input: &[u8],
mut output: S,
compressor: Compressor,
compression_level: CompressionLevel,
) -> Result<usize> {
unsafe { unsafe {
// Set GS register in calling thread let buffer_size = self.get_compressed_buffer_size_needed(compressor, input.len());
const ARCH_SET_GS: i32 = 0x1001; let mut buffer = vec![0; buffer_size];
libc::syscall(libc::SYS_arch_prctl, ARCH_SET_GS, &TIB);
// Call actual decompress function let len = (self.compress)(
call_decompress(comp_buf, raw_buf, *DECOMPRESS.get().unwrap()) compressor,
} input.as_ptr(),
} input.len(),
buffer.as_mut_ptr(),
#[allow(non_snake_case)] compression_level,
mod imports { std::ptr::null(),
use super::*; std::ptr::null(),
std::ptr::null(),
pub unsafe extern "win64" fn OutputDebugStringA(string: *const std::ffi::c_char) { std::ptr::null_mut(),
print!("[OODLE] {}", CStr::from_ptr(string).to_string_lossy());
}
pub unsafe extern "win64" fn GetProcessHeap() -> *const c_void {
0x12345678 as *const c_void
}
pub unsafe extern "win64" fn HeapAlloc(
_heap: *const c_void,
flags: i32,
size: usize,
) -> *const c_void {
assert_eq!(0, flags);
libc::malloc(size)
}
pub unsafe extern "win64" fn HeapFree(
_heap: *const c_void,
_flags: i32,
ptr: *mut c_void,
) -> bool {
libc::free(ptr);
true
}
pub unsafe extern "win64" fn memset(
ptr: *mut c_void,
value: i32,
num: usize,
) -> *const c_void {
libc::memset(ptr, value, num)
}
pub unsafe extern "win64" fn memmove(
destination: *mut c_void,
source: *const c_void,
num: usize,
) -> *const c_void {
libc::memmove(destination, source, num)
}
pub unsafe extern "win64" fn memcpy(
destination: *mut c_void,
source: *const c_void,
num: usize,
) -> *const c_void {
libc::memcpy(destination, source, num)
}
}
// Create some unique function pointers to use for unimplemented imports
const DEBUG_FNS: [*const fn(); 100] = gen_debug_fns();
static mut DEBUG_NAMES: [&str; 100] = [""; 100];
const fn gen_debug_fns() -> [*const fn(); 100] {
fn log<const I: usize>() {
unimplemented!("import {:?}", unsafe { DEBUG_NAMES[I] });
}
let mut array = [std::ptr::null(); 100];
seq_macro::seq!(N in 0..100 {
array[N] = log::<N> as *const fn();
});
array
}
pub fn oodle_loader_linux() -> OodleDecompress {
DECOMPRESS.get_or_init(|| get_decompress_inner().unwrap());
decompress_wrapper
}
fn get_decompress_inner() -> Result<OodleLZ_Decompress> {
fetch_oodle()?;
let oodle = std::env::current_exe()
.unwrap()
.with_file_name(OODLE_DLL_NAME);
let dll = std::fs::read(oodle)?;
let obj_file = PeFile64::parse(&*dll)?;
let size = obj_file.nt_headers().optional_header.size_of_image() as usize;
let header_size = obj_file.nt_headers().optional_header.size_of_headers() as usize;
let image_base = obj_file.relative_address_base() as usize;
// Create map
let mmap = unsafe {
std::slice::from_raw_parts_mut(
libc::mmap(
std::ptr::null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
) as *mut u8,
size,
)
};
let map_base = mmap.as_ptr();
// Copy header to map
mmap[0..header_size].copy_from_slice(&dll[0..header_size]);
unsafe {
assert_eq!(
0, 0,
libc::mprotect(
mmap.as_mut_ptr() as *mut c_void,
header_size,
libc::PROT_READ
)
); );
}
// Copy section data to map if len == -1 {
for section in obj_file.sections() { bail!("Oodle compression failed");
let address = section.address() as usize;
let data = section.data()?;
mmap[(address - image_base)..(address - image_base + data.len())]
.copy_from_slice(section.data()?);
}
// Apply relocations
let sections = obj_file.section_table();
let mut blocks = obj_file
.data_directories()
.relocation_blocks(&*dll, &sections)?
.unwrap();
while let Some(block) = blocks.next()? {
let block_address = block.virtual_address();
let block_data = sections.pe_data_at(&*dll, block_address).map(object::Bytes);
for reloc in block {
let offset = (reloc.virtual_address - block_address) as usize;
match reloc.typ {
IMAGE_REL_BASED_DIR64 => {
let addend = block_data
.and_then(|data| data.read_at::<object::U64Bytes<LE>>(offset).ok())
.map(|addend| addend.get(LE));
if let Some(addend) = addend {
mmap[reloc.virtual_address as usize
..reloc.virtual_address as usize + 8]
.copy_from_slice(&u64::to_le_bytes(
addend - image_base as u64 + map_base as u64,
));
}
}
_ => unimplemented!(),
}
} }
let len = len as usize;
output.write_all(&buffer[..len])?;
Ok(len)
} }
}
// Fix up imports pub fn decompress(&self, input: &[u8], output: &mut [u8]) -> isize {
let import_table = obj_file.import_table()?.unwrap(); unsafe {
let mut import_descs = import_table.descriptors()?; (self.decompress)(
input.as_ptr(),
let mut i = 0; input.len(),
while let Some(import_desc) = import_descs.next()? { output.as_mut_ptr(),
let mut thunks = import_table.thunks(import_desc.original_first_thunk.get(LE))?; output.len(),
1,
let mut address = import_desc.first_thunk.get(LE) as usize; 1,
while let Some(thunk) = thunks.next::<ImageNtHeaders64>()? { 0,
let (_hint, name) = import_table.hint_name(thunk.address())?; 0,
let name = String::from_utf8_lossy(name).to_string(); 0,
0,
use imports::*; 0,
std::ptr::null_mut(),
let fn_addr = match name.as_str() { 0,
"OutputDebugStringA" => OutputDebugStringA as usize, 3,
"GetProcessHeap" => GetProcessHeap as usize, )
"HeapAlloc" => HeapAlloc as usize,
"HeapFree" => HeapFree as usize,
"memset" => memset as usize,
"memcpy" => memcpy as usize,
"memmove" => memmove as usize,
_ => {
unsafe { DEBUG_NAMES[i] = name.leak() }
let a = DEBUG_FNS[i] as usize;
i += 1;
a
}
};
mmap[address..address + 8].copy_from_slice(&usize::to_le_bytes(fn_addr));
address += 8;
}
} }
}
fn get_compressed_buffer_size_needed(
&self,
compressor: oodle_lz::Compressor,
raw_buffer: usize,
) -> usize {
unsafe { (self.get_compressed_buffer_size_needed)(compressor, raw_buffer) }
}
}
// Build export table static OODLE: OnceLock<Option<Oodle>> = OnceLock::new();
let mut exports = HashMap::new();
for export in obj_file.exports()? {
let name = String::from_utf8_lossy(export.name());
let address = export.address() - image_base as u64 + map_base as u64;
exports.insert(name, address as *const c_void);
}
// Fix section permissions fn load_oodle() -> Result<Oodle> {
for section in obj_file.sections() { let path = fetch_oodle()?;
let address = section.address() as usize; unsafe {
let data = section.data()?; let library = libloading::Library::new(path)?;
let size = data.len();
let mut permissions = 0; Ok(Oodle {
compress: *library.get(b"OodleLZ_Compress")?,
let flags = match section.flags() { decompress: *library.get(b"OodleLZ_Decompress")?,
object::SectionFlags::Coff { characteristics } => characteristics, get_compressed_buffer_size_needed: *library
_ => unreachable!(), .get(b"OodleLZ_GetCompressedBufferSizeNeeded")?,
}; _library: library,
if 0 != flags & IMAGE_SCN_MEM_READ {
permissions |= libc::PROT_READ;
}
if 0 != flags & IMAGE_SCN_MEM_WRITE {
permissions |= libc::PROT_WRITE;
}
if 0 != flags & IMAGE_SCN_MEM_EXECUTE {
permissions |= libc::PROT_EXEC;
}
unsafe {
assert_eq!(
0,
libc::mprotect(
mmap.as_mut_ptr().add(address - image_base) as *mut c_void,
size,
permissions
)
);
}
}
Ok(unsafe {
std::mem::transmute::<*const c_void, OodleLZ_Decompress>(exports["OodleLZ_Decompress"])
}) })
} }
} }
pub fn oodle() -> Result<&'static Oodle, Box<dyn std::error::Error>> {
let mut result = None;
let oodle = OODLE.get_or_init(|| match load_oodle() {
Err(err) => {
result = Some(Err(err));
None
}
Ok(oodle) => Some(oodle),
});
match (result, oodle) {
// oodle initialized so return
(_, Some(oodle)) => Ok(oodle),
// error during initialization
(Some(result), _) => result?,
// no error because initialization was tried and failed before
_ => Err(anyhow::anyhow!("oodle failed to initialized previously").into()),
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_oodle() {
let oodle = oodle().unwrap();
let data = b"In tools and when compressing large inputs in one call, consider using
$OodleXLZ_Compress_AsyncAndWait (in the Oodle2 Ext lib) instead to get parallelism. Alternatively,
chop the data into small fixed size chunks (we recommend at least 256KiB, i.e. 262144 bytes) and
call compress on each of them, which decreases compression ratio but makes for trivial parallel
compression and decompression.";
let mut buffer = vec![];
oodle
.compress(
data,
&mut buffer,
Compressor::Mermaid,
CompressionLevel::Optimal5,
)
.unwrap();
std::fs::write("comp.bin", &buffer).unwrap();
dbg!((data.len(), buffer.len()));
let mut uncomp = vec![0; data.len()];
oodle.decompress(&buffer, &mut uncomp);
assert_eq!(data[..], uncomp[..]);
}
}

View file

@ -535,7 +535,7 @@ impl Entry {
.min(self.uncompressed as usize - compress_offset) .min(self.uncompressed as usize - compress_offset)
}; };
let buffer = &mut data[range]; let buffer = &mut data[range];
let out = oodle( let out = oodle.decompress(
buffer, buffer,
&mut decompressed[decompress_offset..decompress_offset + decomp], &mut decompressed[decompress_offset..decompress_offset + decomp],
); );

View file

@ -11,7 +11,7 @@ pub const MAGIC: u32 = 0x5A6F12E1;
#[cfg(feature = "oodle")] #[cfg(feature = "oodle")]
mod oodle { mod oodle {
pub type OodleGetter = fn() -> Result<OodleDecompress, Box<dyn std::error::Error>>; pub type OodleGetter = fn() -> Result<&'static oodle_loader::Oodle, Box<dyn std::error::Error>>;
pub type OodleDecompress = fn(comp_buf: &[u8], raw_buf: &mut [u8]) -> i32; pub type OodleDecompress = fn(comp_buf: &[u8], raw_buf: &mut [u8]) -> i32;
} }

View file

@ -27,7 +27,7 @@ impl PakBuilder {
#[cfg(not(feature = "oodle_implicit_dynamic"))] #[cfg(not(feature = "oodle_implicit_dynamic"))]
oodle: super::Oodle::None, oodle: super::Oodle::None,
#[cfg(feature = "oodle_implicit_dynamic")] #[cfg(feature = "oodle_implicit_dynamic")]
oodle: super::Oodle::Some(oodle_loader::decompress), oodle: super::Oodle::Some(oodle_loader::oodle),
allowed_compression: Default::default(), allowed_compression: Default::default(),
} }
} }