1use crate::error::{RarError, Result};
7
8pub const FILE_HEADER_TYPE: u8 = 0x74; #[derive(Debug, Clone)]
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 is_old_version: bool,
37 pub has_extended_time: bool,
38}
39
40pub struct FileHeaderParser;
41
42impl FileHeaderParser {
43 pub const HEADER_SIZE: usize = 280;
45 const MIN_HEADER_SIZE: usize = 32;
47
48 pub fn parse(buffer: &[u8]) -> Result<FileHeader> {
49 if buffer.len() < Self::MIN_HEADER_SIZE {
50 return Err(RarError::BufferTooSmall {
51 needed: Self::MIN_HEADER_SIZE,
52 have: buffer.len(),
53 });
54 }
55
56 let mut offset = 0;
57
58 let crc = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
59 offset += 2;
60
61 let header_type = buffer[offset];
62 offset += 1;
63
64 let flags = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
65 offset += 2;
66
67 let head_size = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
68 offset += 2;
69
70 let mut packed_size = u32::from_le_bytes([
71 buffer[offset],
72 buffer[offset + 1],
73 buffer[offset + 2],
74 buffer[offset + 3],
75 ]) as u64;
76 offset += 4;
77
78 let mut unpacked_size = u32::from_le_bytes([
79 buffer[offset],
80 buffer[offset + 1],
81 buffer[offset + 2],
82 buffer[offset + 3],
83 ]) as u64;
84 offset += 4;
85
86 let host_os = buffer[offset];
87 offset += 1;
88
89 let file_crc = u32::from_le_bytes([
90 buffer[offset],
91 buffer[offset + 1],
92 buffer[offset + 2],
93 buffer[offset + 3],
94 ]);
95 offset += 4;
96
97 let timestamp = u32::from_le_bytes([
98 buffer[offset],
99 buffer[offset + 1],
100 buffer[offset + 2],
101 buffer[offset + 3],
102 ]);
103 offset += 4;
104
105 let version = buffer[offset];
106 offset += 1;
107
108 let method = buffer[offset];
109 offset += 1;
110
111 let name_size = u16::from_le_bytes([buffer[offset], buffer[offset + 1]]);
112 offset += 2;
113
114 let attributes = u32::from_le_bytes([
115 buffer[offset],
116 buffer[offset + 1],
117 buffer[offset + 2],
118 buffer[offset + 3],
119 ]);
120 offset += 4;
121
122 let continues_from_previous = (flags & 0x01) != 0;
124 let continues_in_next = (flags & 0x02) != 0;
125 let is_encrypted = (flags & 0x04) != 0;
126 let has_comment = (flags & 0x08) != 0;
127 let has_info_from_previous = (flags & 0x10) != 0;
128 let has_high_size = (flags & 0x100) != 0;
129 let has_special_name = (flags & 0x200) != 0;
130 let has_salt = (flags & 0x400) != 0;
131 let is_old_version = (flags & 0x800) != 0;
132 let has_extended_time = (flags & 0x1000) != 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
165 Ok(FileHeader {
166 crc,
167 header_type,
168 flags,
169 head_size,
170 packed_size,
171 unpacked_size,
172 host_os,
173 file_crc,
174 timestamp,
175 version,
176 method,
177 name_size,
178 attributes,
179 name,
180 continues_from_previous,
181 continues_in_next,
182 is_encrypted,
183 has_comment,
184 has_info_from_previous,
185 has_high_size,
186 has_special_name,
187 has_salt,
188 is_old_version,
189 has_extended_time,
190 })
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn test_parse_file_header() {
200 let mut buffer = vec![0u8; 36];
202 buffer[2] = FILE_HEADER_TYPE; buffer[5] = 36; buffer[26] = 4; buffer[32] = b't';
206 buffer[33] = b'e';
207 buffer[34] = b's';
208 buffer[35] = b't';
209
210 let header = FileHeaderParser::parse(&buffer).unwrap();
211 assert_eq!(header.header_type, FILE_HEADER_TYPE);
212 assert_eq!(header.name, "test");
213 }
214
215 #[test]
216 fn test_compression_method() {
217 let mut buffer = vec![0u8; 36];
218 buffer[2] = FILE_HEADER_TYPE;
219 buffer[5] = 36;
220 buffer[25] = 0x30; buffer[26] = 4; buffer[32..36].copy_from_slice(b"test");
223
224 let header = FileHeaderParser::parse(&buffer).unwrap();
225 assert_eq!(header.method, 0x30); }
227}