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