cab/
ctype.rs

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/// A scheme for compressing data within the cabinet.
18#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
19pub enum CompressionType {
20    /// No compression.
21    None,
22    /// MSZIP compression.  MSZIP is described further in
23    /// [MS-MCI](https://msdn.microsoft.com/en-us/library/cc483131.aspx).
24    MsZip,
25    /// Quantum compression with the given level and memory.
26    Quantum(u16, u16),
27    /// LZX compression with the given window size.  The LZX compression scheme
28    /// is described further in
29    /// [MS-PATCH](https://msdn.microsoft.com/en-us/library/cc483133.aspx).
30    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}