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#[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 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#[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}