diff options
| author | Valentin Popov <valentin@popov.link> | 2026-02-10 11:38:58 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-02-10 11:38:58 +0300 |
| commit | 842f4a85693b418af81560738aa3136ac500d9b1 (patch) | |
| tree | d18cf54120294a312bf90d2a5282e3d640c43c57 /crates/rsli/src/compress/lzss.rs | |
| parent | ce6e30f7272fd0c064ef52ac85cad1c0f05fd323 (diff) | |
| download | fparkan-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.rs | 79 |
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) +} |
