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