feat: add initial implementation of rsli crate
Some checks failed
Test / Lint (push) Failing after 1m30s
Test / Test (push) Has been skipped

- Created Cargo.toml for the rsli crate with flate2 dependency.
- Implemented ResourceData enum for handling borrowed and owned byte slices.
- Added OutputBuffer trait and its Vec<u8> implementation for writing data.
- Defined a comprehensive Error enum for error handling in the library.
- Developed the Library struct to manage resource entries and provide methods for loading and unpacking resources.
- Implemented various packing methods and decompression algorithms, including LZSS and Deflate.
- Added tests for validating the functionality of the rsli library against sample data.
This commit is contained in:
2026-02-09 22:58:16 +00:00
parent 5a97f2e429
commit e08b5f3853
9 changed files with 2354 additions and 1 deletions

7
crates/rsli/Cargo.toml Normal file
View File

@@ -0,0 +1,7 @@
[package]
name = "rsli"
version = "0.1.0"
edition = "2021"
[dependencies]
flate2 = { version = "1", default-features = false, features = ["rust_backend"] }

41
crates/rsli/src/data.rs Normal file
View File

@@ -0,0 +1,41 @@
use std::io;
#[derive(Clone, Debug)]
pub enum ResourceData<'a> {
Borrowed(&'a [u8]),
Owned(Vec<u8>),
}
impl<'a> ResourceData<'a> {
pub fn as_slice(&self) -> &[u8] {
match self {
Self::Borrowed(slice) => slice,
Self::Owned(buf) => buf.as_slice(),
}
}
pub fn into_owned(self) -> Vec<u8> {
match self {
Self::Borrowed(slice) => slice.to_vec(),
Self::Owned(buf) => buf,
}
}
}
impl AsRef<[u8]> for ResourceData<'_> {
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
pub trait OutputBuffer {
fn write_exact(&mut self, data: &[u8]) -> io::Result<()>;
}
impl OutputBuffer for Vec<u8> {
fn write_exact(&mut self, data: &[u8]) -> io::Result<()> {
self.clear();
self.extend_from_slice(data);
Ok(())
}
}

129
crates/rsli/src/error.rs Normal file
View File

@@ -0,0 +1,129 @@
use core::fmt;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Io(std::io::Error),
InvalidMagic {
got: [u8; 2],
},
UnsupportedVersion {
got: u8,
},
InvalidEntryCount {
got: i16,
},
EntryTableOutOfBounds {
table_offset: u64,
table_len: u64,
file_len: u64,
},
EntryTableDecryptFailed,
CorruptEntryTable(&'static str),
EntryIdOutOfRange {
id: u32,
entry_count: u32,
},
EntryDataOutOfBounds {
id: u32,
offset: u64,
size: u32,
file_len: u64,
},
AoTrailerInvalid,
MediaOverlayOutOfBounds {
overlay: u32,
file_len: u64,
},
UnsupportedMethod {
raw: u32,
},
PackedSizePastEof {
id: u32,
offset: u64,
packed_size: u32,
file_len: u64,
},
DeflateEofPlusOneQuirkRejected {
id: u32,
},
DecompressionFailed(&'static str),
OutputSizeMismatch {
expected: u32,
got: u32,
},
IntegerOverflow,
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::Io(value)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Io(e) => write!(f, "I/O error: {e}"),
Error::InvalidMagic { got } => write!(f, "invalid RsLi magic: {got:02X?}"),
Error::UnsupportedVersion { got } => write!(f, "unsupported RsLi version: {got:#x}"),
Error::InvalidEntryCount { got } => write!(f, "invalid entry_count: {got}"),
Error::EntryTableOutOfBounds {
table_offset,
table_len,
file_len,
} => write!(
f,
"entry table out of bounds: off={table_offset}, len={table_len}, file={file_len}"
),
Error::EntryTableDecryptFailed => write!(f, "failed to decrypt entry table"),
Error::CorruptEntryTable(s) => write!(f, "corrupt entry table: {s}"),
Error::EntryIdOutOfRange { id, entry_count } => {
write!(f, "entry id out of range: id={id}, count={entry_count}")
}
Error::EntryDataOutOfBounds {
id,
offset,
size,
file_len,
} => write!(
f,
"entry data out of bounds: id={id}, off={offset}, size={size}, file={file_len}"
),
Error::AoTrailerInvalid => write!(f, "invalid AO trailer"),
Error::MediaOverlayOutOfBounds { overlay, file_len } => {
write!(
f,
"media overlay out of bounds: overlay={overlay}, file={file_len}"
)
}
Error::UnsupportedMethod { raw } => write!(f, "unsupported packing method: {raw:#x}"),
Error::PackedSizePastEof {
id,
offset,
packed_size,
file_len,
} => write!(
f,
"packed range past EOF: id={id}, off={offset}, size={packed_size}, file={file_len}"
),
Error::DeflateEofPlusOneQuirkRejected { id } => {
write!(f, "deflate EOF+1 quirk rejected for entry {id}")
}
Error::DecompressionFailed(s) => write!(f, "decompression failed: {s}"),
Error::OutputSizeMismatch { expected, got } => {
write!(f, "output size mismatch: expected={expected}, got={got}")
}
Error::IntegerOverflow => write!(f, "integer overflow"),
}
}
}
impl std::error::Error for Error {}

1165
crates/rsli/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff