sevenz_rust2/
archive.rs

1#[cfg(feature = "compress")]
2use crate::encoder_options::EncoderOptions;
3use crate::{NtTime, bitset::BitSet, block::*};
4
5pub(crate) const SIGNATURE_HEADER_SIZE: u64 = 32;
6pub(crate) const SEVEN_Z_SIGNATURE: &[u8] = &[b'7', b'z', 0xBC, 0xAF, 0x27, 0x1C];
7
8pub(crate) const K_END: u8 = 0x00;
9pub(crate) const K_HEADER: u8 = 0x01;
10pub(crate) const K_ARCHIVE_PROPERTIES: u8 = 0x02;
11pub(crate) const K_ADDITIONAL_STREAMS_INFO: u8 = 0x03;
12pub(crate) const K_MAIN_STREAMS_INFO: u8 = 0x04;
13pub(crate) const K_FILES_INFO: u8 = 0x05;
14pub(crate) const K_PACK_INFO: u8 = 0x06;
15pub(crate) const K_UNPACK_INFO: u8 = 0x07;
16pub(crate) const K_SUB_STREAMS_INFO: u8 = 0x08;
17pub(crate) const K_SIZE: u8 = 0x09;
18pub(crate) const K_CRC: u8 = 0x0A;
19pub(crate) const K_FOLDER: u8 = 0x0B;
20pub(crate) const K_CODERS_UNPACK_SIZE: u8 = 0x0C;
21pub(crate) const K_NUM_UNPACK_STREAM: u8 = 0x0D;
22pub(crate) const K_EMPTY_STREAM: u8 = 0x0E;
23pub(crate) const K_EMPTY_FILE: u8 = 0x0F;
24pub(crate) const K_ANTI: u8 = 0x10;
25pub(crate) const K_NAME: u8 = 0x11;
26pub(crate) const K_C_TIME: u8 = 0x12;
27pub(crate) const K_A_TIME: u8 = 0x13;
28pub(crate) const K_M_TIME: u8 = 0x14;
29pub(crate) const K_WIN_ATTRIBUTES: u8 = 0x15;
30
31/// TODO: Implement reading & writing comments
32#[allow(unused)]
33pub(crate) const K_COMMENT: u8 = 0x16;
34pub(crate) const K_ENCODED_HEADER: u8 = 0x17;
35pub(crate) const K_START_POS: u8 = 0x18;
36pub(crate) const K_DUMMY: u8 = 0x19;
37
38#[derive(Debug, Default, Clone)]
39pub struct Archive {
40    /// Offset from beginning of file + SIGNATURE_HEADER_SIZE to packed streams.
41    pub(crate) pack_pos: u64,
42    pub(crate) pack_sizes: Vec<u64>,
43    pub(crate) pack_crcs_defined: BitSet,
44    pub(crate) pack_crcs: Vec<u64>,
45    pub(crate) sub_streams_info: Option<SubStreamsInfo>,
46    pub blocks: Vec<Block>,
47    pub files: Vec<ArchiveEntry>,
48    pub stream_map: StreamMap,
49    pub is_solid: bool,
50}
51
52#[derive(Debug, Default, Clone)]
53pub(crate) struct SubStreamsInfo {
54    pub(crate) unpack_sizes: Vec<u64>,
55    pub(crate) has_crc: BitSet,
56    pub(crate) crcs: Vec<u64>,
57}
58
59#[derive(Debug, Default, Clone)]
60pub struct ArchiveEntry {
61    pub name: String,
62    pub has_stream: bool,
63    pub is_directory: bool,
64    pub is_anti_item: bool,
65    pub has_creation_date: bool,
66    pub has_last_modified_date: bool,
67    pub has_access_date: bool,
68    pub creation_date: NtTime,
69    pub last_modified_date: NtTime,
70    pub access_date: NtTime,
71    pub has_windows_attributes: bool,
72    pub windows_attributes: u32,
73    pub has_crc: bool,
74    pub crc: u64,
75    pub compressed_crc: u64,
76    pub size: u64,
77    pub compressed_size: u64,
78}
79
80impl ArchiveEntry {
81    pub fn new() -> Self {
82        Self::default()
83    }
84
85    pub fn new_file(entry_name: &str) -> Self {
86        Self {
87            name: entry_name.to_string(),
88            has_stream: true,
89            is_directory: false,
90            ..Default::default()
91        }
92    }
93
94    pub fn new_directory(entry_name: &str) -> Self {
95        Self {
96            name: entry_name.to_string(),
97            has_stream: false,
98            is_directory: true,
99            ..Default::default()
100        }
101    }
102
103    pub fn from_path(path: impl AsRef<std::path::Path>, entry_name: String) -> Self {
104        let path = path.as_ref();
105        #[cfg(target_os = "windows")]
106        let entry_name = {
107            let mut name_bytes = entry_name.into_bytes();
108            for b in &mut name_bytes {
109                if *b == b'\\' {
110                    *b = b'/';
111                }
112            }
113            String::from_utf8(name_bytes).unwrap()
114        };
115        let mut entry = ArchiveEntry {
116            name: entry_name,
117            has_stream: path.is_file(),
118            is_directory: path.is_dir(),
119            ..Default::default()
120        };
121
122        if let Ok(meta) = path.metadata() {
123            if let Ok(modified) = meta.modified() {
124                if let Ok(date) = NtTime::try_from(modified) {
125                    entry.last_modified_date = date;
126                    entry.has_last_modified_date = entry.last_modified_date.0 > 0;
127                }
128            }
129            if let Ok(date) = meta.created() {
130                if let Ok(date) = NtTime::try_from(date) {
131                    entry.creation_date = date;
132                    entry.has_creation_date = entry.creation_date.0 > 0;
133                }
134            }
135            if let Ok(date) = meta.accessed() {
136                if let Ok(date) = NtTime::try_from(date) {
137                    entry.access_date = date;
138                    entry.has_access_date = entry.access_date.0 > 0;
139                }
140            }
141        }
142        entry
143    }
144
145    pub fn name(&self) -> &str {
146        self.name.as_ref()
147    }
148
149    pub fn is_directory(&self) -> bool {
150        self.is_directory
151    }
152
153    pub fn has_stream(&self) -> bool {
154        self.has_stream
155    }
156
157    pub fn creation_date(&self) -> NtTime {
158        self.creation_date
159    }
160
161    pub fn last_modified_date(&self) -> NtTime {
162        self.last_modified_date
163    }
164
165    pub fn size(&self) -> u64 {
166        self.size
167    }
168
169    pub fn windows_attributes(&self) -> u32 {
170        self.windows_attributes
171    }
172
173    pub fn access_date(&self) -> NtTime {
174        self.access_date
175    }
176
177    pub fn is_anti_item(&self) -> bool {
178        self.is_anti_item
179    }
180}
181
182#[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
183#[cfg(feature = "compress")]
184#[derive(Debug, Default)]
185pub struct EncoderConfiguration {
186    pub method: EncoderMethod,
187    pub options: Option<EncoderOptions>,
188}
189
190#[cfg(feature = "compress")]
191impl From<EncoderMethod> for EncoderConfiguration {
192    fn from(value: EncoderMethod) -> Self {
193        Self::new(value)
194    }
195}
196
197#[cfg(feature = "compress")]
198impl Clone for EncoderConfiguration {
199    fn clone(&self) -> Self {
200        Self {
201            method: self.method,
202            options: self.options.clone(),
203        }
204    }
205}
206
207#[cfg(feature = "compress")]
208impl EncoderConfiguration {
209    pub fn new(method: EncoderMethod) -> Self {
210        Self {
211            method,
212            options: None,
213        }
214    }
215
216    pub fn with_options(mut self, options: EncoderOptions) -> Self {
217        self.options = Some(options);
218        self
219    }
220}
221
222/// Encoder method that can be chained (filter, compression and encryption).
223#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Hash)]
224pub struct EncoderMethod(&'static str, &'static [u8]);
225
226impl EncoderMethod {
227    pub const ID_COPY: &'static [u8] = &[0x00];
228    pub const ID_DELTA: &'static [u8] = &[0x03];
229
230    pub const ID_LZMA: &'static [u8] = &[0x03, 0x01, 0x01];
231    pub const ID_BCJ_X86: &'static [u8] = &[0x03, 0x03, 0x01, 0x03];
232    pub const ID_BCJ2: &'static [u8] = &[0x03, 0x03, 0x01, 0x1B];
233    pub const ID_BCJ_PPC: &'static [u8] = &[0x03, 0x03, 0x02, 0x05];
234    pub const ID_BCJ_IA64: &'static [u8] = &[0x03, 0x03, 0x04, 0x01];
235    pub const ID_BCJ_ARM: &'static [u8] = &[0x03, 0x03, 0x05, 0x01];
236    pub const ID_BCJ_ARM64: &'static [u8] = &[0xA];
237    pub const ID_BCJ_ARM_THUMB: &'static [u8] = &[0x03, 0x03, 0x07, 0x01];
238    pub const ID_BCJ_SPARC: &'static [u8] = &[0x03, 0x03, 0x08, 0x05];
239    pub const ID_PPMD: &'static [u8] = &[0x03, 0x04, 0x01];
240
241    pub const ID_LZMA2: &'static [u8] = &[0x21];
242    pub const ID_BZIP2: &'static [u8] = &[0x04, 0x02, 0x02];
243    pub const ID_ZSTD: &'static [u8] = &[0x04, 0xF7, 0x11, 0x01];
244    pub const ID_BROTLI: &'static [u8] = &[0x04, 0xF7, 0x11, 0x02];
245    pub const ID_LZ4: &'static [u8] = &[0x04, 0xF7, 0x11, 0x04];
246    pub const ID_LZS: &'static [u8] = &[0x04, 0xF7, 0x11, 0x05];
247    pub const ID_LIZARD: &'static [u8] = &[0x04, 0xF7, 0x11, 0x06];
248    pub const ID_DEFLATE: &'static [u8] = &[0x04, 0x01, 0x08];
249    pub const ID_DEFLATE64: &'static [u8] = &[0x04, 0x01, 0x09];
250    pub const ID_AES256SHA256: &'static [u8] = &[0x06, 0xF1, 0x07, 0x01];
251
252    pub const COPY: Self = Self("COPY", Self::ID_COPY);
253    pub const LZMA: Self = Self("LZMA", Self::ID_LZMA);
254    pub const LZMA2: Self = Self("LZMA2", Self::ID_LZMA2);
255    pub const PPMD: Self = Self("PPMD", Self::ID_PPMD);
256    pub const BZIP2: Self = Self("BZIP2", Self::ID_BZIP2);
257    pub const ZSTD: Self = Self("ZSTD", Self::ID_ZSTD);
258    pub const BROTLI: Self = Self("BROTLI", Self::ID_BROTLI);
259    pub const LZ4: Self = Self("LZ4", Self::ID_LZ4);
260    pub const LZS: Self = Self("LZS", Self::ID_LZS);
261    pub const LIZARD: Self = Self("LIZARD", Self::ID_LIZARD);
262    pub const DEFLATE: Self = Self("DEFLATE", Self::ID_DEFLATE);
263    pub const DEFLATE64: Self = Self("DEFLATE64", Self::ID_DEFLATE64);
264    pub const AES256SHA256: Self = Self("AES256SHA256", Self::ID_AES256SHA256);
265
266    pub const BCJ_X86_FILTER: Self = Self("BCJ_X86", Self::ID_BCJ_X86);
267    pub const BCJ_PPC_FILTER: Self = Self("BCJ_PPC", Self::ID_BCJ_PPC);
268    pub const BCJ_IA64_FILTER: Self = Self("BCJ_IA64", Self::ID_BCJ_IA64);
269    pub const BCJ_ARM_FILTER: Self = Self("BCJ_ARM", Self::ID_BCJ_ARM);
270    pub const BCJ_ARM64_FILTER: Self = Self("BCJ_ARM64", Self::ID_BCJ_ARM64);
271    pub const BCJ_ARM_THUMB_FILTER: Self = Self("BCJ_ARM_THUMB", Self::ID_BCJ_ARM_THUMB);
272    pub const BCJ_SPARC_FILTER: Self = Self("BCJ_SPARC", Self::ID_BCJ_SPARC);
273    pub const DELTA_FILTER: Self = Self("DELTA", Self::ID_DELTA);
274    pub const BCJ2_FILTER: Self = Self("BCJ2", Self::ID_BCJ2);
275
276    const ENCODING_METHODS: &'static [&'static EncoderMethod] = &[
277        &Self::COPY,
278        &Self::LZMA,
279        &Self::LZMA2,
280        &Self::PPMD,
281        &Self::BZIP2,
282        &Self::ZSTD,
283        &Self::BROTLI,
284        &Self::LZ4,
285        &Self::LZS,
286        &Self::LIZARD,
287        &Self::DEFLATE,
288        &Self::DEFLATE64,
289        &Self::AES256SHA256,
290        &Self::BCJ_X86_FILTER,
291        &Self::BCJ_PPC_FILTER,
292        &Self::BCJ_IA64_FILTER,
293        &Self::BCJ_ARM_FILTER,
294        &Self::BCJ_ARM64_FILTER,
295        &Self::BCJ_ARM_THUMB_FILTER,
296        &Self::BCJ_SPARC_FILTER,
297        &Self::DELTA_FILTER,
298        &Self::BCJ2_FILTER,
299    ];
300
301    #[inline]
302    pub const fn name(&self) -> &'static str {
303        self.0
304    }
305
306    #[inline]
307    pub const fn id(&self) -> &'static [u8] {
308        self.1
309    }
310
311    #[inline]
312    pub fn by_id(id: &[u8]) -> Option<Self> {
313        Self::ENCODING_METHODS
314            .iter()
315            .find(|item| item.id() == id)
316            .cloned()
317            .cloned()
318    }
319}
320
321#[derive(Debug, Default, Clone)]
322pub struct StreamMap {
323    pub(crate) block_first_pack_stream_index: Vec<usize>,
324    pub(crate) pack_stream_offsets: Vec<u64>,
325    pub block_first_file_index: Vec<usize>,
326    pub file_block_index: Vec<Option<usize>>,
327}
328
329#[derive(Debug, Clone, Copy)]
330pub(crate) struct StartHeader {
331    pub(crate) next_header_offset: u64,
332    pub(crate) next_header_size: u64,
333    pub(crate) next_header_crc: u64,
334}