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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
//! `zip-core::raw` contains 1:1 memory format of zip headers for use in your
//! projects  e.g. if you want to build your own zip crate.
//!
//! Note: for concenience, all structs with variable size are split into a fixed
//! part and the variable part. The fixed part is prepended with `Fixed`. This
//! should make parsing easier
//!
//!
//! ### Rage List of things about <https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT>
//! - all fields unless noted are unsigned and little endian (there are no
//!   notions), however we should set one to -1 in 4.4.1.4, how?!?!
//! - optional signature that are not standart but are recomended.
//! - noone explains what zip64 does, what it tries to do, etc.
//! - measurements, sometimes its offset, sometimes total size, sometimes
//!   remaining size, and sometimes, size from this byte onwards
//!
//! #### Naming Rage
//! During reading the document I almost had a seizure regarding to naming.
//! Nevertheless I decided against renaming fields, as this is what the author
//! PKWARE decided on, and probably the whole industry has adopted to and i dont
//! want to create xkcd:927 However, if in future revisions some names are
//! adopted I would be more than happy. Here are some of the incompatibilities
//!
//! - of all fields and structures, sometimes sturct, sometimes record, often
//!   nothing
//! - sometimes central dir, sometimes central directory
//! - Zip64 end of central directory locator vs Zip64 end of central directory
//!   locator record
//! - the central directory is sometimes a single central directory header and
//!   sometimes a central directory structure
//! - sometimes its a length sometimes a size
extern crate alloc;
use alloc::vec::Vec;

#[cfg(feature = "parse")] pub mod parse;

/// part of [`LocalFileHeader`] which has a fixed size
///
/// [`LocalFileHeader`]: LocalFileHeader
#[derive(Debug, PartialEq)]
pub struct LocalFileHeaderFixed {
    pub local_file_header_signature: u32,
    pub version_needed_to_extract: u16,
    pub general_purpose_bit_flag: u16,
    pub compression_method: u16,
    pub last_mod_file_time: u16,
    pub last_mod_file_date: u16,
    pub crc_32: u32,
    pub compressed_size: u32,
    pub uncompressed_size: u32,
    pub file_name_length: u16,
    pub extra_field_length: u16,
}

/// Local File Header
///
/// The local file header is prepended before each file.
/// The bytes after it are usually the file or an Encryption Header.
/// is is linked by an [`CentralDirectoryHeader`]
///
/// see [4.3.7](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
///
/// [`CentralDirectoryHeader`]: CentralDirectoryHeader
#[derive(Debug, PartialEq)]
pub struct LocalFileHeader {
    pub fixed:       LocalFileHeaderFixed,
    pub file_name:   Vec<u8>,
    pub extra_field: Vec<u8>,
}

/// Data descriptor
///
/// the data descriptior is prepended to the file data.
/// It might be used sometimes, in this case the corresponding fields in
/// [`LocalFileHeader`] are zero.
///
/// Note: There exists a zip64 variant of this descriptor:
/// [`DataDescriptorZip64`] Note: the DataDescriptor should contain a signature,
/// see the struct [`DataDescriptorSignature`]. Its not standart but recomended.
/// When parsing you are on your own if the first 4 bytes a the signature or
/// just a random crc32 according to the documentation
/// see [`DataDescriptorZip64Signature`] for Zip64 with Signature
///
/// see [4.3.9](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
///
/// [`LocalFileHeader`]: LocalFileHeaderFixed
/// [`DataDescriptorZip64`]: DataDescriptorZip64
/// [`DataDescriptorSignature`]: DataDescriptorSignature
/// [`DataDescriptorZip64Signature`]: DataDescriptorZip64Signature
#[derive(Debug, PartialEq)]
pub struct DataDescriptor {
    pub crc_32: u32,
    pub compressed_size: u32,
    pub uncompressed_size: u32,
}

/// Data descriptor with a signature see [`DataDescriptor`]
///
/// [`DataDescriptor`]: DataDescriptor
#[derive(Debug, PartialEq)]
pub struct DataDescriptorSignature {
    pub signature: u32,
    pub crc_32: u32,
    pub compressed_size: u32,
    pub uncompressed_size: u32,
}

/// Data descriptor for Zip64, sizes are 8 bytes instead of 4, see
/// [`DataDescriptor`]
///
/// [`DataDescriptor`]: DataDescriptor
#[derive(Debug, PartialEq)]
pub struct DataDescriptorZip64 {
    pub crc_32: u32,
    pub compressed_size: u64,
    pub uncompressed_size: u64,
}

/// Data descriptor for Zip64, sizes are 8 bytes instead of 4 and with a
/// signature, see [`DataDescriptor`]
///
/// [`DataDescriptor`]: DataDescriptor
#[derive(Debug, PartialEq)]
pub struct DataDescriptorZip64Signature {
    pub signature: u32,
    pub crc_32: u32,
    pub compressed_size: u64,
    pub uncompressed_size: u64,
}

/// part of [`ArchiveExtraDataRecord`] which has a fixed size
///
/// [`ArchiveExtraDataRecord`]: ArchiveExtraDataRecord
#[derive(Debug, PartialEq)]
pub struct ArchiveExtraDataRecordFixed {
    pub archive_extra_data_signature: u32,
    pub extra_field_length: u64,
}

/// Archive Extra Data Record
///
/// May be used to support the Central Directory Encryption Feature
///
/// see [4.3.11](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
#[derive(Debug, PartialEq)]
pub struct ArchiveExtraDataRecord {
    pub fixed: ArchiveExtraDataRecordFixed,
    pub extra_field_data: Vec<u8>,
}

/// part of [`CentralDirectoryHeader`] which has a fixed size
///
/// [`CentralDirectoryHeader`]: CentralDirectoryHeader
#[derive(Debug, PartialEq)]
pub struct CentralDirectoryHeaderFixed {
    pub central_file_header_signature: u32,
    pub version_made_by: u16,
    pub version_needed_to_extract: u16,
    pub general_purpose_bit_flag: u16,
    pub compression_method: u16,
    pub last_mod_file_time: u16,
    pub last_mod_file_date: u16,
    pub crc_32: u32,
    pub compressed_size: u32,
    pub uncompressed_size: u32,
    pub file_name_length: u16,
    pub extra_field_length: u16,
    pub file_comment_length: u16,
    pub disk_number_start: u16,
    pub internal_file_attributes: u16,
    pub external_file_attributes: u32,
    pub relative_offset_of_local_header: u32,
}

/// Central Directory Header
///
/// The Central Directory Structure contains of multiple Central Directory
/// Headers and a Digital Signature. Note: the documentation is confusing about
/// this, the Digital Signature is left out in `4.3.6` and Archive Decryption
/// Header and [`ArchiveExtraDataRecord`] seem to be not part of it, though they
/// have a strong dependency.
///
/// Each Central Directory Header links to exactly one [`LocalFileHeader`]
///
/// see [4.3.12](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
///
/// [`ArchiveExtraDataRecord`]: ArchiveExtraDataRecord
/// [`LocalFileHeader`]: LocalFileHeader
#[derive(Debug, PartialEq)]
pub struct CentralDirectoryHeader {
    pub fixed:        CentralDirectoryHeaderFixed,
    pub file_name:    Vec<u8>,
    pub extra_field:  Vec<u8>,
    pub file_comment: Vec<u8>,
}

/// part of [`DigitalSignature`] which has a fixed size
///
/// [`DigitalSignature`]: DigitalSignature
#[derive(Debug, PartialEq)]
pub struct DigitalSignatureFixed {
    pub header_signature: u32,
    pub size_of_data:     u16,
}

/// part of [`LocalFileHeader`] which has a fixed size
///
/// [`LocalFileHeader`]: LocalFileHeader
#[derive(Debug, PartialEq)]
pub struct DigitalSignature {
    pub fixed: DigitalSignatureFixed,
    pub signature_data: Vec<u8>,
}

/// Zip64 end of central directory record
///
/// see [4.3.14](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
#[derive(Debug, PartialEq)]
pub struct Zip64EndOfCentralDirectoryFixed {
    pub zip64_end_of_central_dir_signature: u32,
    pub size_of_zip64_end_of_central_directory_record: u64,
    pub version_made_by: u16,
    pub version_needed_to_extract: u16,
    pub number_of_this_disk: u32,
    pub number_of_the_disk_with_the_start_of_the_central_directory: u32,
    pub total_number_of_entries_in_the_central_directory_on_this_disk: u64,
    pub total_number_of_entries_in_the_central_directory: u64,
    pub size_of_the_central_directory: u64,
    pub offset_of_start_of_central_directory_with_respect_to_the_starting_disk_number: u64,
}

/// part of [`Zip64EndOfCentralDirectory`] which has a fixed size
///
/// [`Zip64EndOfCentralDirectory`]: Zip64EndOfCentralDirectory
#[derive(Debug, PartialEq)]
pub struct Zip64EndOfCentralDirectory {
    pub fixed: Zip64EndOfCentralDirectoryFixed,
    pub zip64_extensible_data_sector: Vec<u8>,
}

///  Zip64 end of central directory locator
///
/// see [4.3.14](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
#[derive(Debug, PartialEq)]
pub struct Zip64EndOfCentralDirectoryLocator {
    pub zip64_end_of_central_dir_locator_signature: u32,
    pub number_of_the_disk_with_the_start_of_the_central_directory: u32,
    pub relative_offset_of_the_zip64_end_of_central_directory_record: u64,
    pub number_of_this_disk: u32,
}

/// part of [`EndOfCentralDirectory`] which has a fixed size
///
/// [`EndOfCentralDirectory`]: EndOfCentralDirectory
#[derive(Debug, PartialEq)]
pub struct EndOfCentralDirectoryFixed {
    pub end_of_central_dir_signature: u32,
    pub number_of_this_disk: u16,
    pub number_of_the_disk_with_the_start_of_the_central_directory: u16,
    pub total_number_of_entries_in_the_central_directory_on_this_disk: u16,
    pub total_number_of_entries_in_the_central_directory: u16,
    pub size_of_the_central_directory: u32,
    pub offset_of_start_of_central_directory_with_respect_to_the_starting_disk_number: u32,
    pub zip_file_comment_length: u16,
}

/// End of central directory record
///
/// see [4.3.16](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
#[derive(Debug, PartialEq)]
pub struct EndOfCentralDirectory {
    pub fixed: EndOfCentralDirectoryFixed,
    pub zip_file_comment: Vec<u8>,
}

impl LocalFileHeaderFixed {
    pub const LOCAL_FILE_HEADER_SIGNATURE: u32 = 0x04034b50;
    pub const SIZE_IN_BYTES: usize = 30;
}

impl DataDescriptor {
    /// Note: At time of writing this is an optional, de-facto-standart
    /// signature
    pub const SIGNATURE: u32 = 0x08074b50;
    pub const SIZE_IN_BYTES: usize = 12;
}

impl DataDescriptorSignature {
    pub const SIZE_IN_BYTES: usize = 16;
}

impl DataDescriptorZip64 {
    pub const SIZE_IN_BYTES: usize = 20;
}

impl DataDescriptorZip64Signature {
    pub const SIZE_IN_BYTES: usize = 24;
}

impl ArchiveExtraDataRecordFixed {
    pub const SIZE_IN_BYTES: usize = 12;
}

impl ArchiveExtraDataRecordFixed {
    pub const ARCHIVE_EXTRA_DATE_SIGNATURE: u32 = 0x08064b50;
}

impl CentralDirectoryHeaderFixed {
    pub const CENTRAL_FILE_HEADER_SIGNATURE: u32 = 0x02014b50;
    pub const SIZE_IN_BYTES: usize = 46;
}

impl DigitalSignatureFixed {
    pub const HEADER_SIGNATURE: u32 = 0x05054b50;
    pub const SIZE_IN_BYTES: usize = 6;
}

impl Zip64EndOfCentralDirectoryFixed {
    pub const SIZE_IN_BYTES: usize = 56;
    pub const ZIP64_END_OF_CENTRAL_DIR_SIGNATURE: u32 = 0x06064b50;
}

impl Zip64EndOfCentralDirectoryLocator {
    pub const SIZE_IN_BYTES: usize = 20;
    pub const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE: u32 = 0x07064b50;
}

impl EndOfCentralDirectoryFixed {
    pub const END_OF_CENTRAL_DIR_SIGNATURE: u32 = 0x06054b50;
    pub const SIZE_IN_BYTES: usize = 22;
}