stream_unpack/zip/structures/
cd_location.rs1use std::io::Cursor;
2
3use byteorder::{LittleEndian, ReadBytesExt};
4
5pub const CDLD_MAX_SIZE: usize = EOCD32_MAX_SIZE + 4 + EOCD64_LOCATOR_CONSTANT_SIZE;
6
7#[derive(Debug, Clone)]
8pub struct CentralDirectoryLocationData {
9 pub cd_disk_number: u32,
10 pub cd_size: u64,
11 pub cd_offset: u64,
12
13 #[cfg(feature = "zip-comments")]
14 pub comment: String
15}
16
17impl CentralDirectoryLocationData {
18 pub fn from_eocd32(eocd32: EndOfCentralDirectory32) -> Self {
19 Self {
20 cd_disk_number: eocd32.cd_disk_number as u32,
21 cd_size: eocd32.cd_size as u64,
22 cd_offset: eocd32.cd_offset as u64,
23
24 #[cfg(feature = "zip-comments")]
25 comment: eocd32.comment
26 }
27 }
28
29 pub fn from_eocd64(eocd32: EndOfCentralDirectory32, eocd64: EndOfCentralDirectory64) -> Self {
30 let mut data = Self::from_eocd32(eocd32);
31
32 if data.cd_disk_number == u16::MAX as u32 {
33 data.cd_disk_number = eocd64.cd_disk_number;
34 }
35
36 if data.cd_size == u32::MAX as u64 {
37 data.cd_size = eocd64.cd_size;
38 }
39
40 if data.cd_offset == u32::MAX as u64 {
41 data.cd_offset = eocd64.cd_offset;
42 }
43
44 data
45 }
46}
47
48pub const EOCD32_SIGNATURE: u32 = 0x06054b50;
49pub const EOCD32_CONSTANT_SIZE: usize = 18;
50pub const EOCD32_MAX_SIZE: usize = EOCD32_CONSTANT_SIZE + 4 + (u16::MAX as usize);
51
52#[derive(Debug, Clone)]
53pub struct EndOfCentralDirectory32 {
54 pub disk_number: u16,
55 pub cd_disk_number: u16,
56
57 pub cd_entry_count: u16,
58 pub cd_entry_count_total: u16,
59
60 pub cd_size: u32,
61 pub cd_offset: u32,
62
63 #[cfg(feature = "zip-comments")]
64 pub comment: String,
65
66 pub eocd32_size: usize
67}
68
69impl EndOfCentralDirectory32 {
70 pub fn from_bytes(data: impl AsRef<[u8]>) -> Option<Self> {
71 let data = data.as_ref();
72 if data.len() < EOCD32_CONSTANT_SIZE {
73 return None;
74 }
75
76 let mut cursor = Cursor::new(data);
77
78 let disk_number = cursor.read_u16::<LittleEndian>().unwrap();
79 let cd_disk_number = cursor.read_u16::<LittleEndian>().unwrap();
80 let cd_entry_count = cursor.read_u16::<LittleEndian>().unwrap();
81 let cd_entry_count_total = cursor.read_u16::<LittleEndian>().unwrap();
82 let cd_size = cursor.read_u32::<LittleEndian>().unwrap();
83 let cd_offset = cursor.read_u32::<LittleEndian>().unwrap();
84 let comment_length = cursor.read_u16::<LittleEndian>().unwrap();
85
86 if data.len() < EOCD32_CONSTANT_SIZE + comment_length as usize {
87 return None;
88 }
89
90 let comment_start = EOCD32_CONSTANT_SIZE;
91 let comment_end = comment_start + comment_length as usize;
92
93 Some(Self {
94 disk_number,
95 cd_disk_number,
96 cd_entry_count,
97 cd_entry_count_total,
98 cd_size,
99 cd_offset,
100
101 #[cfg(feature = "zip-comments")]
102 comment: String::from_utf8_lossy(&data[comment_start..comment_end]).to_string(),
103
104 eocd32_size: comment_end
105 })
106 }
107
108 pub fn find_offset(data: impl AsRef<[u8]>) -> Option<usize> {
113 let data = data.as_ref();
114 if data.len() < EOCD32_CONSTANT_SIZE {
115 return None;
116 }
117
118 let first_offset = data.len() - std::cmp::min(data.len(), EOCD32_MAX_SIZE);
119
120 for offset in (first_offset..(data.len() - EOCD32_CONSTANT_SIZE)).rev() {
121 let signature = u32::from_le_bytes(data[offset..(offset + 4)].try_into().unwrap());
122 if signature == EOCD32_SIGNATURE {
123 return Some(offset);
124 }
125 }
126
127 None
128 }
129
130 pub fn requires_zip64(&self) -> bool {
131 self.disk_number == u16::MAX ||
132 self.cd_disk_number == u16::MAX ||
133 self.cd_entry_count == u16::MAX ||
134 self.cd_entry_count_total == u16::MAX ||
135 self.cd_size == u32::MAX ||
136 self.cd_offset == u32::MAX
137 }
138}
139
140pub const EOCD64_LOCATOR_SIGNATURE: u32 = 0x07064b50;
141pub const EOCD64_LOCATOR_CONSTANT_SIZE: usize = 16;
142
143#[derive(Debug, Clone)]
144pub struct EndOfCentralDirectory64Locator {
145 pub eocd64_disk_number: u32,
146 pub eocd64_offset: u64,
147
148 pub disk_count: u32
149}
150
151impl EndOfCentralDirectory64Locator {
152 pub fn from_bytes(data: impl AsRef<[u8]>) -> Option<Self> {
153 let data = data.as_ref();
154 if data.len() < EOCD64_LOCATOR_CONSTANT_SIZE {
155 return None;
156 }
157
158 let mut cursor = Cursor::new(data);
159
160 let eocd64_disk_number = cursor.read_u32::<LittleEndian>().unwrap();
161 let eocd64_offset = cursor.read_u64::<LittleEndian>().unwrap();
162 let disk_count = cursor.read_u32::<LittleEndian>().unwrap();
163
164 Some(Self {
165 eocd64_disk_number,
166 eocd64_offset,
167 disk_count
168 })
169 }
170}
171
172pub const EOCD64_SIGNATURE: u32 = 0x06064b50;
173pub const EOCD64_CONSTANT_SIZE: usize = 52;
174
175#[derive(Debug, Clone)]
176pub struct EndOfCentralDirectory64 {
177 pub eocd64_size: u64,
178
179 pub version_made_by: u16,
180 pub version_needed: u16,
181
182 pub disk_number: u32,
183 pub cd_disk_number: u32,
184
185 pub cd_entry_count: u64,
186 pub cd_entry_count_total: u64,
187
188 pub cd_size: u64,
189 pub cd_offset: u64
190}
191
192impl EndOfCentralDirectory64 {
193 pub fn from_bytes(data: impl AsRef<[u8]>) -> Option<Self> {
194 let data = data.as_ref();
195 if data.len() < EOCD64_CONSTANT_SIZE {
196 return None;
197 }
198
199 let mut cursor = Cursor::new(data);
200
201 let eocd64_size = cursor.read_u64::<LittleEndian>().unwrap();
202 let version_made_by = cursor.read_u16::<LittleEndian>().unwrap();
203 let version_needed = cursor.read_u16::<LittleEndian>().unwrap();
204 let disk_number = cursor.read_u32::<LittleEndian>().unwrap();
205 let cd_disk_number = cursor.read_u32::<LittleEndian>().unwrap();
206 let cd_entry_count = cursor.read_u64::<LittleEndian>().unwrap();
207 let cd_entry_count_total = cursor.read_u64::<LittleEndian>().unwrap();
208 let cd_size = cursor.read_u64::<LittleEndian>().unwrap();
209 let cd_offset = cursor.read_u64::<LittleEndian>().unwrap();
210
211 Some(Self {
214 eocd64_size,
215 version_made_by,
216 version_needed,
217 disk_number,
218 cd_disk_number,
219 cd_entry_count,
220 cd_entry_count_total,
221 cd_size,
222 cd_offset
223 })
224 }
225}