1use crate::error::{RarError, Result};
7
8pub const FILE_HEADER_TYPE: u8 = 0x74; #[derive(Debug, Clone, PartialEq, Eq)]
12pub struct FileHeader {
13 pub crc: u16,
14 pub header_type: u8,
15 pub flags: u16,
16 pub head_size: u16,
17 pub packed_size: u64,
18 pub unpacked_size: u64,
19 pub host_os: u8,
20 pub file_crc: u32,
21 pub timestamp: u32,
22 pub version: u8,
23 pub method: u8,
24 pub name_size: u16,
25 pub attributes: u32,
26 pub name: String,
27 pub continues_from_previous: bool,
29 pub continues_in_next: bool,
30 pub is_encrypted: bool,
31 pub has_comment: bool,
32 pub has_info_from_previous: bool,
33 pub has_high_size: bool,
34 pub has_special_name: bool,
35 pub has_salt: bool,
36 pub has_extended_time: bool,
37 pub salt: Option<[u8; 8]>,
39}
40
41pub struct FileHeaderParser;
42
43impl FileHeaderParser {
44 pub const HEADER_SIZE: usize = 280;
46 const MIN_HEADER_SIZE: usize = 32;
48
49 pub fn parse(buffer: &[u8]) -> Result<FileHeader> {
50 if buffer.len() < Self::MIN_HEADER_SIZE {
51 return Err(RarError::BufferTooSmall {
52 needed: Self::MIN_HEADER_SIZE,
53 have: buffer.len(),
54 });
55 }
56
57 let mut offset = 0;
58
59 let crc = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
60 offset += 2;
61
62 let header_type = buffer[offset];
63 offset += 1;
64
65 let flags = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
66 offset += 2;
67
68 let head_size = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
69 offset += 2;
70
71 let mut packed_size = u32::from_le_bytes([
72 buffer[offset],
73 buffer[offset + 1],
74 buffer[offset + 2],
75 buffer[offset + 3],
76 ]) as u64;
77 offset += 4;
78
79 let mut unpacked_size = u32::from_le_bytes([
80 buffer[offset],
81 buffer[offset + 1],
82 buffer[offset + 2],
83 buffer[offset + 3],
84 ]) as u64;
85 offset += 4;
86
87 let host_os = buffer[offset];
88 offset += 1;
89
90 let file_crc = u32::from_le_bytes([
91 buffer[offset],
92 buffer[offset + 1],
93 buffer[offset + 2],
94 buffer[offset + 3],
95 ]);
96 offset += 4;
97
98 let timestamp = u32::from_le_bytes([
99 buffer[offset],
100 buffer[offset + 1],
101 buffer[offset + 2],
102 buffer[offset + 3],
103 ]);
104 offset += 4;
105
106 let version = buffer[offset];
107 offset += 1;
108
109 let method = buffer[offset];
110 offset += 1;
111
112 let name_size = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
113 offset += 2;
114
115 let attributes = u32::from_le_bytes([
116 buffer[offset],
117 buffer[offset + 1],
118 buffer[offset + 2],
119 buffer[offset + 3],
120 ]);
121 offset += 4;
122
123 let continues_from_previous = (flags & 0x0001) != 0;
125 let continues_in_next = (flags & 0x0002) != 0;
126 let is_encrypted = (flags & 0x0004) != 0;
127 let has_comment = (flags & 0x0008) != 0;
128 let has_info_from_previous = (flags & 0x0010) != 0;
129 let has_high_size = (flags & 0x0100) != 0; let has_special_name = (flags & 0x0040) != 0; let has_salt = (flags & 0x0080) != 0;
132 let has_extended_time = (flags & 0x0200) != 0;
133
134 if has_high_size && buffer.len() >= offset + 8 {
136 let high_packed = u32::from_le_bytes([
137 buffer[offset],
138 buffer[offset + 1],
139 buffer[offset + 2],
140 buffer[offset + 3],
141 ]) as u64;
142 offset += 4;
143 let high_unpacked = u32::from_le_bytes([
144 buffer[offset],
145 buffer[offset + 1],
146 buffer[offset + 2],
147 buffer[offset + 3],
148 ]) as u64;
149 offset += 4;
150
151 packed_size |= high_packed << 32;
152 unpacked_size |= high_unpacked << 32;
153 }
154
155 let name_end = offset
157 .checked_add(name_size as usize)
158 .ok_or(RarError::InvalidHeader)?;
159 if buffer.len() < name_end {
160 return Err(RarError::BufferTooSmall {
161 needed: name_end,
162 have: buffer.len(),
163 });
164 }
165 let name = String::from_utf8_lossy(&buffer[offset..name_end]).to_string();
166 offset = name_end;
167
168 let salt = if (has_salt || is_encrypted)
173 && buffer.len() >= offset + 8
174 && head_size as usize > offset
175 {
176 let mut s = [0u8; 8];
177 s.copy_from_slice(&buffer[offset..offset + 8]);
178 Some(s)
179 } else {
180 None
181 };
182
183 Ok(FileHeader {
184 crc,
185 header_type,
186 flags,
187 head_size,
188 packed_size,
189 unpacked_size,
190 host_os,
191 file_crc,
192 timestamp,
193 version,
194 method,
195 name_size,
196 attributes,
197 name,
198 continues_from_previous,
199 continues_in_next,
200 is_encrypted,
201 has_comment,
202 has_info_from_previous,
203 has_high_size,
204 has_special_name,
205 has_salt,
206 has_extended_time,
207 salt,
208 })
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_parse_file_header() {
218 let mut buffer = vec![0u8; 36];
220 buffer[2] = FILE_HEADER_TYPE; buffer[5] = 36; buffer[26] = 4; buffer[32] = b't';
224 buffer[33] = b'e';
225 buffer[34] = b's';
226 buffer[35] = b't';
227
228 let header = FileHeaderParser::parse(&buffer).unwrap();
229 assert_eq!(header.header_type, FILE_HEADER_TYPE);
230 assert_eq!(header.name, "test");
231 }
232
233 #[test]
234 fn test_compression_method() {
235 let mut buffer = vec![0u8; 36];
236 buffer[2] = FILE_HEADER_TYPE;
237 buffer[5] = 36;
238 buffer[25] = 0x30; buffer[26] = 4; buffer[32..36].copy_from_slice(b"test");
241
242 let header = FileHeaderParser::parse(&buffer).unwrap();
243 assert_eq!(header.method, 0x30); }
245}