1use std::io;
2
3use lzxd::Lzxd;
4
5use crate::mszip::MsZipDecompressor;
6
7const CTYPE_NONE: u16 = 0;
8const CTYPE_MSZIP: u16 = 1;
9const CTYPE_QUANTUM: u16 = 2;
10const CTYPE_LZX: u16 = 3;
11
12const QUANTUM_LEVEL_MIN: u16 = 1;
13const QUANTUM_LEVEL_MAX: u16 = 7;
14const QUANTUM_MEMORY_MIN: u16 = 10;
15const QUANTUM_MEMORY_MAX: u16 = 21;
16
17#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
19pub enum CompressionType {
20 None,
22 MsZip,
25 Quantum(u16, u16),
27 Lzx(lzxd::WindowSize),
31}
32
33impl CompressionType {
34 pub(crate) fn from_bitfield(bits: u16) -> io::Result<CompressionType> {
35 let ctype = bits & 0x000f;
36 if ctype == CTYPE_NONE {
37 Ok(CompressionType::None)
38 } else if ctype == CTYPE_MSZIP {
39 Ok(CompressionType::MsZip)
40 } else if ctype == CTYPE_QUANTUM {
41 let level = (bits & 0x00f0) >> 4;
42 if !(QUANTUM_LEVEL_MIN..=QUANTUM_LEVEL_MAX).contains(&level) {
43 invalid_data!("Invalid Quantum level: 0x{:02x}", level);
44 }
45 let memory = (bits & 0x1f00) >> 8;
46 if !(QUANTUM_MEMORY_MIN..=QUANTUM_MEMORY_MAX).contains(&memory) {
47 invalid_data!("Invalid Quantum memory: 0x{:02x}", memory);
48 }
49 Ok(CompressionType::Quantum(level, memory))
50 } else if ctype == CTYPE_LZX {
51 let window = (bits & 0x1f00) >> 8;
52 let window = match window {
53 15 => lzxd::WindowSize::KB32,
54 16 => lzxd::WindowSize::KB64,
55 17 => lzxd::WindowSize::KB128,
56 18 => lzxd::WindowSize::KB256,
57 19 => lzxd::WindowSize::KB512,
58 20 => lzxd::WindowSize::MB1,
59 21 => lzxd::WindowSize::MB2,
60 22 => lzxd::WindowSize::MB4,
61 23 => lzxd::WindowSize::MB8,
62 24 => lzxd::WindowSize::MB16,
63 25 => lzxd::WindowSize::MB32,
64 _ => invalid_data!("Invalid LZX window: 0x{:02x}", window),
65 };
66 Ok(CompressionType::Lzx(window))
67 } else {
68 invalid_data!("Invalid compression type: 0x{:04x}", bits);
69 }
70 }
71
72 pub(crate) fn to_bitfield(self) -> u16 {
73 match self {
74 CompressionType::None => CTYPE_NONE,
75 CompressionType::MsZip => CTYPE_MSZIP,
76 CompressionType::Quantum(level, memory) => {
77 CTYPE_QUANTUM
78 | (level.max(QUANTUM_LEVEL_MIN).min(QUANTUM_LEVEL_MAX)
79 << 4)
80 | (memory.max(QUANTUM_MEMORY_MIN).min(QUANTUM_MEMORY_MAX)
81 << 8)
82 }
83 CompressionType::Lzx(window_size) => {
84 let window = match window_size {
85 lzxd::WindowSize::KB32 => 15,
86 lzxd::WindowSize::KB64 => 16,
87 lzxd::WindowSize::KB128 => 17,
88 lzxd::WindowSize::KB256 => 18,
89 lzxd::WindowSize::KB512 => 19,
90 lzxd::WindowSize::MB1 => 20,
91 lzxd::WindowSize::MB2 => 21,
92 lzxd::WindowSize::MB4 => 22,
93 lzxd::WindowSize::MB8 => 23,
94 lzxd::WindowSize::MB16 => 24,
95 lzxd::WindowSize::MB32 => 25,
96 };
97 CTYPE_LZX | (window << 8)
98 }
99 }
100 }
101
102 pub(crate) fn into_decompressor(self) -> io::Result<Decompressor> {
103 match self {
104 CompressionType::None => Ok(Decompressor::Uncompressed),
105 CompressionType::MsZip => {
106 Ok(Decompressor::MsZip(Box::new(MsZipDecompressor::new())))
107 }
108 CompressionType::Quantum(_, _) => {
109 invalid_data!("Quantum decompression is not yet supported.")
110 }
111 CompressionType::Lzx(window_size) => {
112 Ok(Decompressor::Lzx(Box::new(Lzxd::new(window_size))))
113 }
114 }
115 }
116}
117
118pub enum Decompressor {
119 Uncompressed,
120 MsZip(Box<MsZipDecompressor>),
121 Lzx(Box<Lzxd>),
122}
123
124impl Decompressor {
125 pub(crate) fn reset(&mut self) {
126 match self {
127 Self::Uncompressed => {}
128 Self::MsZip(d) => d.reset(),
129 Self::Lzx(d) => d.reset(),
130 }
131 }
132
133 pub(crate) fn decompress(
134 &mut self,
135 data: Vec<u8>,
136 uncompressed_size: usize,
137 ) -> io::Result<Vec<u8>> {
138 let data = match self {
139 Decompressor::Uncompressed => data,
140 Decompressor::MsZip(decompressor) => decompressor
141 .decompress_block(&data, uncompressed_size)
142 .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?
143 .to_vec(),
144 Decompressor::Lzx(decompressor) => decompressor
145 .decompress_next(&data, uncompressed_size)
146 .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?
147 .to_vec(),
148 };
149 Ok(data)
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::CompressionType;
156
157 #[test]
158 fn compression_type_to_bitfield() {
159 assert_eq!(CompressionType::None.to_bitfield(), 0x0);
160 assert_eq!(CompressionType::MsZip.to_bitfield(), 0x1);
161 assert_eq!(CompressionType::Quantum(7, 20).to_bitfield(), 0x1472);
162 assert_eq!(
163 CompressionType::Lzx(lzxd::WindowSize::MB2).to_bitfield(),
164 0x1503
165 );
166 }
167
168 #[test]
169 fn compression_type_from_bitfield() {
170 assert_eq!(
171 CompressionType::from_bitfield(0x0).unwrap(),
172 CompressionType::None
173 );
174 assert_eq!(
175 CompressionType::from_bitfield(0x1).unwrap(),
176 CompressionType::MsZip
177 );
178 assert_eq!(
179 CompressionType::from_bitfield(0x1472).unwrap(),
180 CompressionType::Quantum(7, 20)
181 );
182 assert_eq!(
183 CompressionType::from_bitfield(0x1503).unwrap(),
184 CompressionType::Lzx(lzxd::WindowSize::MB2)
185 );
186 }
187}