1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::compression::compression_provider::CompressionProvider;
use anyhow::{anyhow, Result};
use nintendo_lz::decompress_arr;
use std::cmp::min;
fn get_occurrence_length(
bytes: &[u8],
new_ptr: usize,
new_length: usize,
old_ptr: usize,
old_length: usize,
) -> (i32, usize) {
if new_length == 0 || old_length == 0 {
return (0, 0);
}
let mut disp = 0;
let mut max_length = 0;
for i in 0..(old_length - 1) {
let current_old_start = old_ptr + i;
let mut current_length = 0;
for j in 0..new_length {
if bytes[current_old_start + j] != bytes[new_ptr + j] {
break;
}
current_length += 1;
}
if current_length > max_length {
max_length = current_length;
disp = old_length - i;
if max_length == new_length {
break;
}
}
}
(max_length as i32, disp)
}
pub struct LZ13CompressionProvider;
impl CompressionProvider for LZ13CompressionProvider {
fn is_compressed_filename(&self, filename: &str) -> bool {
filename.ends_with(".lz")
}
fn compress(&self, bytes: &[u8]) -> Result<Vec<u8>> {
let mut result: Vec<u8> = Vec::new();
let length = bytes.len();
result.reserve(9 + length + ((length - 1) >> 3));
result.push(0x13);
result.push(((length & 0xFF) + 1) as u8);
result.push(((length >> 8) & 0xFF) as u8);
result.push(((length >> 16) & 0xFF) as u8);
result.push(0x11);
result.push((length & 0xFF) as u8);
result.push(((length >> 8) & 0xFF) as u8);
result.push(((length >> 16) & 0xFF) as u8);
let mut out_buffer: Vec<u8> = Vec::new();
out_buffer.reserve_exact(8 * 4 + 1);
out_buffer.push(0);
let mut buffered_blocks = 0;
let mut read_bytes = 0;
while read_bytes < bytes.len() {
if buffered_blocks == 8 {
result.append(&mut out_buffer);
out_buffer.push(0);
buffered_blocks = 0;
}
let old_length = min(read_bytes, 0x1000);
let (length, disp) = get_occurrence_length(
bytes,
read_bytes,
min(bytes.len() - read_bytes, 0x1000),
read_bytes - old_length,
old_length,
);
if length < 3 {
out_buffer.push(bytes[read_bytes]);
read_bytes += 1;
} else {
read_bytes += length as usize;
out_buffer[0] |= (1 << (7 - buffered_blocks)) as u8;
if length > 0x110 {
out_buffer.push(0x10 | (((length - 0x111) >> 12) & 0x0F) as u8);
out_buffer.push((((length - 0x111) >> 4) & 0xFF) as u8);
out_buffer.push((((length - 0x111) << 4) & 0xF0) as u8);
} else if length > 0x10 {
out_buffer.push((((length - 0x111) >> 4) & 0x0F) as u8);
out_buffer.push((((length - 0x111) << 4) & 0xF0) as u8);
} else {
out_buffer.push((((length - 1) << 4) & 0xF0) as u8);
}
let last_index = out_buffer.len() - 1;
out_buffer[last_index] |= (((disp - 1) >> 8) & 0x0F) as u8;
out_buffer.push(((disp - 1) & 0xFF) as u8);
}
buffered_blocks += 1;
}
if buffered_blocks > 0 {
result.append(&mut out_buffer);
}
Ok(result)
}
fn decompress(&self, bytes: &[u8]) -> Result<Vec<u8>> {
if bytes[0] == 0 {
let mut result: Vec<u8> = Vec::new();
result.extend_from_slice(&bytes[4..]);
Ok(result)
} else {
let truncated_input = if bytes[0] == 0x13 { &bytes[4..] } else { bytes };
match decompress_arr(&truncated_input) {
Ok(decompressed_data) => Ok(decompressed_data),
Err(_) => Err(anyhow!("Invalid compressed file")),
}
}
}
}