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 + name_size as usize;
157 if buffer.len() < name_end {
158 return Err(RarError::BufferTooSmall {
159 needed: name_end,
160 have: buffer.len(),
161 });
162 }
163 let name = String::from_utf8_lossy(&buffer[offset..name_end]).to_string();
164 offset = name_end;
165
166 let salt = if has_salt && buffer.len() >= offset + 8 {
168 let mut s = [0u8; 8];
169 s.copy_from_slice(&buffer[offset..offset + 8]);
170 Some(s)
171 } else {
172 None
173 };
174
175 Ok(FileHeader {
176 crc,
177 header_type,
178 flags,
179 head_size,
180 packed_size,
181 unpacked_size,
182 host_os,
183 file_crc,
184 timestamp,
185 version,
186 method,
187 name_size,
188 attributes,
189 name,
190 continues_from_previous,
191 continues_in_next,
192 is_encrypted,
193 has_comment,
194 has_info_from_previous,
195 has_high_size,
196 has_special_name,
197 has_salt,
198 has_extended_time,
199 salt,
200 })
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn test_parse_file_header() {
210 let mut buffer = vec![0u8; 36];
212 buffer[2] = FILE_HEADER_TYPE; buffer[5] = 36; buffer[26] = 4; buffer[32] = b't';
216 buffer[33] = b'e';
217 buffer[34] = b's';
218 buffer[35] = b't';
219
220 let header = FileHeaderParser::parse(&buffer).unwrap();
221 assert_eq!(header.header_type, FILE_HEADER_TYPE);
222 assert_eq!(header.name, "test");
223 }
224
225 #[test]
226 fn test_compression_method() {
227 let mut buffer = vec![0u8; 36];
228 buffer[2] = FILE_HEADER_TYPE;
229 buffer[5] = 36;
230 buffer[25] = 0x30; buffer[26] = 4; buffer[32..36].copy_from_slice(b"test");
233
234 let header = FileHeaderParser::parse(&buffer).unwrap();
235 assert_eq!(header.method, 0x30); }
237}