stream_unpack/zip/structures/
file_header.rs1use std::io::Cursor;
2
3use byteorder::{ReadBytesExt, LittleEndian};
4
5pub const ENCRYPTED_FLAG: u16 = 0x0001;
6pub const STRONG_ENCRYPTION_FLAG: u16 = 0x0040;
7
8#[derive(Debug, Clone)]
10pub struct FileHeaderExtraField {
11 pub id: u16,
12 pub data: Vec<u8>
13}
14
15impl FileHeaderExtraField {
16 pub fn from_bytes(data: impl AsRef<[u8]>) -> Option<Self> {
19 let data = data.as_ref();
20 if data.len() < 4 {
21 return None;
22 }
23
24 let mut cursor = Cursor::new(data);
25
26 let id = cursor.read_u16::<LittleEndian>().unwrap();
27 let size = cursor.read_u16::<LittleEndian>().unwrap();
28
29 if data.len() < 4 + (size as usize) {
30 return None;
31 }
32
33 Some(Self {
34 id,
35 data: data[4..(4 + size as usize)].to_owned()
36 })
37 }
38
39 pub fn read_extra_fields(data: impl AsRef<[u8]>) -> Option<Vec<FileHeaderExtraField>> {
42 let data = data.as_ref();
43
44 let mut extra_fileds = Vec::new();
45 let mut offset = 0;
46 while offset < data.len() {
47 let Some(field) = FileHeaderExtraField::from_bytes(&data[offset..]) else {
48 return None;
49 };
50
51 offset += field.size();
52 extra_fileds.push(field);
53 }
54
55 Some(extra_fileds)
56 }
57
58 pub fn size(&self) -> usize {
60 4 + self.data.len()
61 }
62}
63
64pub const ZIP64_EXTRA_FIELD_ID: u16 = 0x0001;
65
66#[derive(Debug, Default)]
67pub struct Zip64OriginalData {
68 pub uncompressed_size: u32,
69 pub compressed_size: u32,
70 pub local_header_offset: u32,
71 pub disk_number: u16
72}
73
74#[derive(Debug)]
75pub struct Zip64ProcessedData {
76 pub uncompressed_size: u64,
77 pub compressed_size: u64,
78 pub local_header_offset: u64,
79 pub disk_number: u32
80}
81
82impl Zip64OriginalData {
83 pub fn process(&self, fields: impl AsRef<[FileHeaderExtraField]>) -> Option<Zip64ProcessedData> {
86 let required_size = self.required_zip64_size();
87
88 if required_size == 0 {
89 return Some(self.as_processed());
90 }
91
92 let fields = fields.as_ref();
93 let Some(zip64) = fields.iter().find(|f| f.id == ZIP64_EXTRA_FIELD_ID) else {
94 return Some(self.as_processed());
95 };
96
97 if zip64.data.len() < required_size {
98 return None;
99 }
100
101 Some(self.read_all_if_needed(&zip64.data))
102 }
103
104 fn required_zip64_size(&self) -> usize {
105 (
106 if self.uncompressed_size == u32::MAX { 8 } else { 0 } +
107 if self.compressed_size == u32::MAX { 8 } else { 0 } +
108 if self.local_header_offset == u32::MAX { 8 } else { 0 } +
109 if self.disk_number == u16::MAX { 4 } else { 0 }
110 )
111 }
112
113 fn as_processed(&self) -> Zip64ProcessedData {
114 Zip64ProcessedData {
115 compressed_size: self.compressed_size as u64,
116 uncompressed_size: self.uncompressed_size as u64,
117 local_header_offset: self.local_header_offset as u64,
118 disk_number: self.disk_number as u32
119 }
120 }
121
122 fn read_all_if_needed(&self, data: impl AsRef<[u8]>) -> Zip64ProcessedData {
123 let data = data.as_ref();
124 let mut cursor = Cursor::new(data);
125
126 let uncompressed_size = Self::read_u64_if_needed(&mut cursor, self.uncompressed_size);
127 let compressed_size = Self::read_u64_if_needed(&mut cursor, self.compressed_size);
128 let file_header_offset = Self::read_u64_if_needed(&mut cursor, self.local_header_offset);
129 let disk_number = Self::read_u32_if_needed(&mut cursor, self.disk_number);
130
131 Zip64ProcessedData {
132 uncompressed_size,
133 compressed_size,
134 local_header_offset: file_header_offset,
135 disk_number
136 }
137 }
138
139 fn read_u64_if_needed(cursor: &mut Cursor<&[u8]>, value: u32) -> u64 {
140 if value != u32::MAX {
141 value as u64
142 } else {
143 cursor.read_u64::<LittleEndian>().unwrap()
144 }
145 }
146
147 fn read_u32_if_needed(cursor: &mut Cursor<&[u8]>, value: u16) -> u32 {
148 if value != u16::MAX {
149 value as u32
150 } else {
151 cursor.read_u32::<LittleEndian>().unwrap()
152 }
153 }
154}