aboutsummaryrefslogtreecommitdiff
path: root/crates/rsli/src/compress/lzss.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-02-10 11:38:58 +0300
committerValentin Popov <valentin@popov.link>2026-02-10 11:38:58 +0300
commit842f4a85693b418af81560738aa3136ac500d9b1 (patch)
treed18cf54120294a312bf90d2a5282e3d640c43c57 /crates/rsli/src/compress/lzss.rs
parentce6e30f7272fd0c064ef52ac85cad1c0f05fd323 (diff)
downloadfparkan-842f4a85693b418af81560738aa3136ac500d9b1.tar.xz
fparkan-842f4a85693b418af81560738aa3136ac500d9b1.zip
Implement LZSS decompression with optional XOR decryption
- Added `lzss_decompress_simple` function for LZSS decompression in `lzss.rs`. - Introduced `XorState` struct and `xor_stream` function for XOR decryption in `xor.rs`. - Updated `mod.rs` to include new LZSS and XOR modules. - Refactored `parse_library` function in `parse.rs` to utilize the new XOR decryption functionality. - Cleaned up and organized code in `lib.rs` by removing redundant functions and structures. - Added tests for new functionality in `tests.rs`.
Diffstat (limited to 'crates/rsli/src/compress/lzss.rs')
-rw-r--r--crates/rsli/src/compress/lzss.rs79
1 files changed, 79 insertions, 0 deletions
diff --git a/crates/rsli/src/compress/lzss.rs b/crates/rsli/src/compress/lzss.rs
new file mode 100644
index 0000000..d30345c
--- /dev/null
+++ b/crates/rsli/src/compress/lzss.rs
@@ -0,0 +1,79 @@
+use super::xor::XorState;
+use crate::error::Error;
+use crate::Result;
+
+/// Simple LZSS decompression with optional on-the-fly XOR decryption
+pub fn lzss_decompress_simple(
+ data: &[u8],
+ expected_size: usize,
+ xor_key: Option<u16>,
+) -> Result<Vec<u8>> {
+ let mut ring = [0x20u8; 0x1000];
+ let mut ring_pos = 0xFEEusize;
+ let mut out = Vec::with_capacity(expected_size);
+ let mut in_pos = 0usize;
+
+ let mut control = 0u8;
+ let mut bits_left = 0u8;
+
+ // XOR state for on-the-fly decryption
+ let mut xor_state = xor_key.map(XorState::new);
+
+ // Helper to read byte with optional XOR decryption
+ let read_byte = |pos: usize, state: &mut Option<XorState>| -> Option<u8> {
+ let encrypted = data.get(pos).copied()?;
+ Some(if let Some(ref mut s) = state {
+ s.decrypt_byte(encrypted)
+ } else {
+ encrypted
+ })
+ };
+
+ while out.len() < expected_size {
+ if bits_left == 0 {
+ let byte = read_byte(in_pos, &mut xor_state)
+ .ok_or(Error::DecompressionFailed("lzss-simple: unexpected EOF"))?;
+ control = byte;
+ in_pos += 1;
+ bits_left = 8;
+ }
+
+ if (control & 1) != 0 {
+ let byte = read_byte(in_pos, &mut xor_state)
+ .ok_or(Error::DecompressionFailed("lzss-simple: unexpected EOF"))?;
+ in_pos += 1;
+
+ out.push(byte);
+ ring[ring_pos] = byte;
+ ring_pos = (ring_pos + 1) & 0x0FFF;
+ } else {
+ let low = read_byte(in_pos, &mut xor_state)
+ .ok_or(Error::DecompressionFailed("lzss-simple: unexpected EOF"))?;
+ let high = read_byte(in_pos + 1, &mut xor_state)
+ .ok_or(Error::DecompressionFailed("lzss-simple: unexpected EOF"))?;
+ in_pos += 2;
+
+ let offset = usize::from(low) | (usize::from(high & 0xF0) << 4);
+ let length = usize::from((high & 0x0F) + 3);
+
+ for step in 0..length {
+ let byte = ring[(offset + step) & 0x0FFF];
+ out.push(byte);
+ ring[ring_pos] = byte;
+ ring_pos = (ring_pos + 1) & 0x0FFF;
+ if out.len() >= expected_size {
+ break;
+ }
+ }
+ }
+
+ control >>= 1;
+ bits_left -= 1;
+ }
+
+ if out.len() != expected_size {
+ return Err(Error::DecompressionFailed("lzss-simple"));
+ }
+
+ Ok(out)
+}