Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace oodle-sys with Oodle loaded at runtime #38

Merged
merged 4 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ harness = false

[workspace]
resolver = "2"
default-members = ["crates/cli", "crates/viewer"]
members = [
"crates/asset-server",
"crates/cli",
"crates/dvdbnd",
"crates/formats",
"crates/oodle-rt",
"crates/viewer",
]

Expand All @@ -56,6 +56,7 @@ fstools_formats = { path = "crates/formats", version = "0.1.0" }
fstools_dvdbnd = { path = "crates/dvdbnd", version = "0.1.0" }
fstools_asset_server = { path = "crates/asset-server", version = "0.1.0" }
fstools_elden_ring_support = { path = "crates/support/elden_ring", version = "0.1.0" }
fstools_oodle_rt = { path = "crates/oodle-rt", version = "0.1.0" }
memmap2 = "0.9.4"
rayon = "1"
serde = "1"
Expand Down
3 changes: 1 addition & 2 deletions crates/formats/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ name = "fstools_formats"
version.workspace = true
license.workspace = true
edition.workspace = true
build = "build.rs"

[features]
default = []
Expand All @@ -15,8 +14,8 @@ bytemuck = "1"
byteorder = "1"
dashu = "0.4"
flate2 = "1.0"
fstools_oodle_rt.workspace = true
num-modular = "0.6"
oodle-sys = "0.1"
rayon.workspace = true
rsa = "0.9"
thiserror.workspace = true
Expand Down
8 changes: 0 additions & 8 deletions crates/formats/build.rs

This file was deleted.

6 changes: 3 additions & 3 deletions crates/formats/src/dcx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use byteorder::BE;
use thiserror::Error;
use zerocopy::{FromBytes, FromZeroes, U32};

use self::{deflate::DeflateDecoder, oodle::OodleDecoder};
use self::{deflate::DeflateDecoder, oodle::OodleReader};

pub mod deflate;
pub mod oodle;
Expand Down Expand Up @@ -65,7 +65,7 @@ impl DcxHeader {
let algorithm = &self.compression_parameters.algorithm;
let decoder = match algorithm {
MAGIC_ALGORITHM_KRAKEN => Decoder::Kraken(
OodleDecoder::new(reader, self.sizes.uncompressed_size.get())
OodleReader::new(reader, self.sizes.uncompressed_size.get())
.ok_or(DcxError::DecoderError)?,
),
MAGIC_ALGORITHM_DEFLATE => Decoder::Deflate(DeflateDecoder::new(reader)),
Expand Down Expand Up @@ -109,7 +109,7 @@ impl Debug for DcxHeader {
}

pub enum Decoder<R: Read> {
Kraken(OodleDecoder<R>),
Kraken(OodleReader<R>),
Deflate(DeflateDecoder<R>),
}

Expand Down
85 changes: 17 additions & 68 deletions crates/formats/src/dcx/oodle.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
use std::{
cmp::min,
io::{Error, Read, Result},
ptr::{null_mut, NonNull},
};

use oodle_sys::{
OodleLZDecoder, OodleLZDecoder_Create, OodleLZDecoder_DecodeSome, OodleLZDecoder_Destroy,
OodleLZ_CheckCRC_OodleLZ_CheckCRC_Yes, OodleLZ_Compressor_OodleLZ_Compressor_Kraken,
OodleLZ_DecodeSome_Out, OodleLZ_Decode_ThreadPhase_OodleLZ_Decode_ThreadPhaseAll,
OodleLZ_FuzzSafe_OodleLZ_FuzzSafe_No, OodleLZ_Verbosity_OodleLZ_Verbosity_None,
OODLELZ_BLOCK_LEN,
};
use fstools_oodle_rt::{decoder::OodleDecoder, Compressor, Oodle, OODLELZ_BLOCK_LEN};

// SAFETY: `OodleLZDecoder` pointer is safe to use across several threads.
unsafe impl<R: Read> Sync for OodleDecoder<R> {}
unsafe impl<R: Read> Sync for OodleReader<R> {}

// SAFETY: See above.
unsafe impl<R: Read> Send for OodleDecoder<R> {}
unsafe impl<R: Read> Send for OodleReader<R> {}

pub struct OodleDecoder<R: Read> {
pub struct OodleReader<R: Read> {
reader: R,

/// The total size of the raw data expected to be read from the underlying stream.
uncompressed_size: u32,

/// The Oodle decoder instance created for this buffer.
decoder: NonNull<OodleLZDecoder>,
decoder: OodleDecoder,

/// A sliding window of bytes decoded by the compressor, large enough to keep the past block in
/// memory while the next block is decoded.
Expand All @@ -49,39 +39,31 @@ pub struct OodleDecoder<R: Read> {
io_buffer_reader_pos: usize,
}

impl<R: Read> OodleDecoder<R> {
impl<R: Read> OodleReader<R> {
pub fn new(reader: R, uncompressed_size: u32) -> Option<Self> {
let compressor = OodleLZ_Compressor_OodleLZ_Compressor_Kraken;

// SAFETY: Calling OodleLZDecoder_Create with any `uncompressed_size` is safe.
let decoder = unsafe {
OodleLZDecoder_Create(compressor, uncompressed_size as i64, null_mut(), 0isize)
};

// Oodle was unable to create a decoder for this compressor
if decoder.is_null() {
return None;
}

let oodle = Oodle::current()?;
let decoder = oodle.create_decoder(
Compressor::OodleLZ_Compressor_Kraken,
uncompressed_size as usize,
)?;
let decode_buffer = vec![0u8; 3 * 1024 * 1024].into_boxed_slice();
let io_buffer = vec![0u8; OODLELZ_BLOCK_LEN as usize * 2].into_boxed_slice();

Some(Self {
// SAFETY: Pointer is validated to be non-null above.
decoder: unsafe { NonNull::new_unchecked(decoder) },
decoder,
reader,
decode_buffer,
decode_buffer_writer_pos: 0,
decode_buffer_reader_lag: 0,
io_buffer,
io_buffer_reader_pos: 0,
io_buffer_writer_pos: 0,
uncompressed_size,
})
}
}

impl<R: Read> Read for OodleDecoder<R> {
impl<R: Read> Read for OodleReader<R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if buf.is_empty() {
return Ok(0);
Expand Down Expand Up @@ -120,37 +102,11 @@ impl<R: Read> Read for OodleDecoder<R> {
break; // EOF reached
}

let out = self
.decoder
.decode_some(&mut self.decode_buffer, wpos, data)
.ok_or(Error::other("Oodle decoder failed"))?;
// SAFETY: OodleLZ_DecodeSome_out is zero initialised by default.
let mut out: OodleLZ_DecodeSome_Out = unsafe { std::mem::zeroed() };

// SAFETY:
// - Signedness conversions of offsets are all valid, given that `sliding_window.len()
// <= i32::MAX` and `self.uncompressed_size < isize::MAX`.
// - Consumed `input_data_len` is caped at i32::MAX
let result = unsafe {
// EXTREMELY unlikely, however unsound otherwise.
let input_data_len = isize::try_from(data.len()).unwrap_or(isize::MAX);
let decode_buffer_avail = self.decode_buffer.len() - wpos;

OodleLZDecoder_DecodeSome(
self.decoder.as_ptr(),
&mut out as *mut _,
self.decode_buffer.as_mut_ptr().cast(),
wpos as isize,
self.uncompressed_size as isize,
decode_buffer_avail as isize,
data.as_ptr().cast(),
input_data_len,
OodleLZ_FuzzSafe_OodleLZ_FuzzSafe_No,
OodleLZ_CheckCRC_OodleLZ_CheckCRC_Yes,
OodleLZ_Verbosity_OodleLZ_Verbosity_None,
OodleLZ_Decode_ThreadPhase_OodleLZ_Decode_ThreadPhaseAll,
)
};

if result == 0 {
return Err(Error::other("Oodle decoder failed"));
}

let decoded_bytes = out.decodedCount as usize;
let consumed_bytes = out.compBufUsed as usize;
Expand Down Expand Up @@ -195,10 +151,3 @@ impl<R: Read> Read for OodleDecoder<R> {
Ok(total_written)
}
}

impl<R: Read> Drop for OodleDecoder<R> {
fn drop(&mut self) {
// SAFETY: Guaranteed to be a valid pointer to a Decoder by [OodleDecoder::new].
unsafe { OodleLZDecoder_Destroy(self.decoder.as_ptr()) }
}
}
17 changes: 17 additions & 0 deletions crates/oodle-rt/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "fstools_oodle_rt"
version.workspace = true
license.workspace = true
edition.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libloading = "0.8.3"

[build-dependencies.bindgen]
version = "0.69"
features = ["runtime", "which-rustfmt"]

[lints]
workspace = true
25 changes: 25 additions & 0 deletions crates/oodle-rt/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::{error::Error, path::PathBuf};

fn main() -> Result<(), Box<dyn Error>> {
println!("cargo:rerun-if-changed=oodle_rt.hpp");
println!("cargo:rerun-if-changed=oodle2.h");
println!("cargo:rerun-if-changed=oodle2base.h");

let bindings = bindgen::Builder::default()
.header("oodle_rt.hpp")
.rustified_enum("OodleLZ_FuzzSafe")
.rustified_enum("OodleLZ_Verbosity")
.rustified_enum("OodleLZ_Decode_ThreadPhase")
.rustified_enum("OodleLZ_Compressor")
.rustified_enum("OodleLZ_CheckCRC")
.ignore_functions()
.ignore_methods()
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()?;

let out_path = PathBuf::from(std::env::var("OUT_DIR")?);

bindings.write_to_file(out_path.join("bindings.rs"))?;

Ok(())
}
Loading
Loading