1use super::*;
4
5#[derive(
7 Copy,
8 Clone,
9 Eq,
10 PartialEq,
11 Ord,
12 PartialOrd,
13 IntoBytes,
14 FromBytes,
15 Unaligned,
16 KnownLayout,
17 Immutable,
18)]
19#[repr(transparent)]
20pub struct ChecksumKind(pub u8);
21
22impl ChecksumKind {
23 pub const NONE: ChecksumKind = ChecksumKind(0);
25 pub const MD5: ChecksumKind = ChecksumKind(1);
27 pub const SHA_1: ChecksumKind = ChecksumKind(2);
29 pub const SHA_256: ChecksumKind = ChecksumKind(3);
31}
32
33impl std::fmt::Debug for ChecksumKind {
34 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
35 static NAMES: [&str; 4] = ["NONE", "MD5", "SHA_1", "SHA_256"];
36
37 if let Some(name) = NAMES.get(self.0 as usize) {
38 f.write_str(name)
39 } else {
40 write!(f, "??({})", self.0)
41 }
42 }
43}
44
45#[test]
46fn checksum_kind_debug() {
47 assert_eq!(format!("{:?}", ChecksumKind::SHA_256), "SHA_256");
48 assert_eq!(format!("{:?}", ChecksumKind(42)), "??(42)");
49}
50
51pub struct FileChecksumsSubsection<'a> {
55 #[allow(missing_docs)]
56 pub bytes: &'a [u8],
57}
58
59impl<'a> FileChecksumsSubsection<'a> {
60 #[allow(missing_docs)]
61 pub fn new(bytes: &'a [u8]) -> Self {
62 Self { bytes }
63 }
64
65 pub fn iter(&self) -> FileChecksumIter<'a> {
67 FileChecksumIter { bytes: self.bytes }
68 }
69
70 pub fn get_file(&self, file_index: u32) -> anyhow::Result<FileChecksum<'a>> {
73 if let Some(b) = self.bytes.get(file_index as usize..) {
74 if let Some(c) = FileChecksumIter::new(b).next() {
75 Ok(c)
76 } else {
77 bail!("failed to decode FileChecksum record");
78 }
79 } else {
80 bail!("file index is out of range of file checksums subsection");
81 }
82 }
83}
84
85pub struct FileChecksumsSubsectionMut<'a> {
87 #[allow(missing_docs)]
88 pub bytes: &'a mut [u8],
89}
90
91impl<'a> HasRestLen for FileChecksumsSubsectionMut<'a> {
92 fn rest_len(&self) -> usize {
93 self.bytes.len()
94 }
95}
96
97impl<'a> FileChecksumsSubsectionMut<'a> {
98 #[allow(missing_docs)]
99 pub fn new(bytes: &'a mut [u8]) -> Self {
100 Self { bytes }
101 }
102
103 pub fn iter_mut(&mut self) -> FileChecksumMutIter<'_> {
105 FileChecksumMutIter { bytes: self.bytes }
106 }
107
108 pub fn get_file_mut(&mut self, file_index: u32) -> anyhow::Result<FileChecksumMut<'_>> {
111 if let Some(b) = self.bytes.get_mut(file_index as usize..) {
112 if let Some(c) = FileChecksumMutIter::new(b).next() {
113 Ok(c)
114 } else {
115 bail!("failed to decode FileChecksum record");
116 }
117 } else {
118 bail!("file index is out of range of file checksums subsection");
119 }
120 }
121}
122
123pub struct FileChecksum<'a> {
125 pub header: &'a FileChecksumHeader,
127 pub checksum_data: &'a [u8],
129}
130
131pub struct FileChecksumMut<'a> {
133 pub header: &'a mut FileChecksumHeader,
135 pub checksum_data: &'a mut [u8],
137}
138
139impl<'a> FileChecksum<'a> {
140 pub fn name(&self) -> NameIndex {
143 NameIndex(self.header.name.get())
144 }
145}
146
147#[derive(IntoBytes, FromBytes, KnownLayout, Immutable, Unaligned, Clone, Debug)]
155#[repr(C)]
156pub struct FileChecksumHeader {
157 pub name: U32<LE>,
159
160 pub checksum_size: u8,
162
163 pub checksum_kind: ChecksumKind,
165}
166
167pub struct FileChecksumIter<'a> {
169 pub bytes: &'a [u8],
171}
172
173impl<'a> HasRestLen for FileChecksumIter<'a> {
174 fn rest_len(&self) -> usize {
175 self.bytes.len()
176 }
177}
178
179pub struct FileChecksumMutIter<'a> {
181 pub bytes: &'a mut [u8],
183}
184
185impl<'a> HasRestLen for FileChecksumMutIter<'a> {
186 fn rest_len(&self) -> usize {
187 self.bytes.len()
188 }
189}
190
191impl<'a> FileChecksumIter<'a> {
192 pub fn new(bytes: &'a [u8]) -> Self {
194 Self { bytes }
195 }
196}
197
198impl<'a> Iterator for FileChecksumIter<'a> {
199 type Item = FileChecksum<'a>;
200
201 fn next(&mut self) -> Option<Self::Item> {
202 if self.bytes.is_empty() {
203 return None;
204 }
205
206 let mut p = Parser::new(self.bytes);
207 let len_before = p.len();
208 let header: &FileChecksumHeader = p.get().ok()?;
209 let checksum_data = p.bytes(header.checksum_size as usize).ok()?;
210
211 let record_len = len_before - p.len();
213 let _ = p.skip((4 - (record_len & 3)) & 3);
214
215 self.bytes = p.into_rest();
216 Some(FileChecksum {
217 header,
218 checksum_data,
219 })
220 }
221}
222
223impl<'a> FileChecksumMutIter<'a> {
224 pub fn new(bytes: &'a mut [u8]) -> Self {
226 Self { bytes }
227 }
228}
229
230impl<'a> Iterator for FileChecksumMutIter<'a> {
231 type Item = FileChecksumMut<'a>;
232
233 fn next(&mut self) -> Option<Self::Item> {
234 if self.bytes.is_empty() {
235 return None;
236 }
237
238 let mut p = ParserMut::new(take(&mut self.bytes));
239 let len_before = p.len();
240 let header: &mut FileChecksumHeader = p.get_mut().ok()?;
241 let checksum_data = p.bytes_mut(header.checksum_size as usize).ok()?;
242
243 let record_len = len_before - p.len();
245 let _ = p.skip((4 - (record_len & 3)) & 3);
246
247 self.bytes = p.into_rest();
248 Some(FileChecksumMut {
249 header,
250 checksum_data,
251 })
252 }
253}
254
255#[test]
257fn iter_ranges() {
258 const PAD: u8 = 0xaa;
259
260 #[rustfmt::skip]
261 let data = &[
262 200, 0, 0, 0, 0, 0, PAD, PAD,
267 42, 0, 0, 0, 16, 1, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
274 0xc8, 0xc9, 0xca, 0xcb,
275 0xcc, 0xcd, 0xce, 0xcf,
276 PAD, PAD,
278
279 ];
281
282 let sums = FileChecksumsSubsection::new(data);
283 let mut iter = sums.iter().with_ranges();
284
285 let (sub0_range, _) = iter.next().unwrap();
286 assert_eq!(sub0_range, 0..8);
287
288 let (sub1_range, _) = iter.next().unwrap();
289 assert_eq!(sub1_range, 8..32);
290
291 assert!(iter.next().is_none());
292}
293
294#[test]
296fn iter_mut() {
297 const PAD: u8 = 0xaa;
298
299 #[rustfmt::skip]
300 let data = &[
301 42, 0, 0, 0, 16, 1, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
306 0xc8, 0xc9, 0xca, 0xcb,
307 0xcc, 0xcd, 0xce, 0xcf,
308 PAD, PAD,
310
311 ];
313
314 let mut data_mut = data.to_vec();
315 let mut sums = FileChecksumsSubsectionMut::new(&mut data_mut);
316 let mut iter = sums.iter_mut();
317 assert_eq!(iter.rest_len(), 24); let sum0 = iter.next().unwrap();
320 assert_eq!(iter.rest_len(), 0); assert_eq!(sum0.header.name.get(), 42);
322 assert_eq!(
323 sum0.checksum_data,
324 &[
325 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
326 0xce, 0xcf
327 ]
328 );
329
330 sum0.header.name = U32::new(0xcafef00d);
331 sum0.checksum_data[4] = 0xff;
332
333 #[rustfmt::skip]
334 let expected_new_data = &[
335 0x0d, 0xf0, 0xfe, 0xca, 16, 1, 0xc0, 0xc1, 0xc2, 0xc3, 0xff, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
341 0xcc, 0xcd, 0xce, 0xcf,
342 PAD, PAD,
344
345 ];
347
348 assert_eq!(data_mut.as_slice(), expected_new_data);
349}
350
351#[test]
353fn basic_iter() {
354 const PAD: u8 = 0xaa;
355
356 #[rustfmt::skip]
357 let data = &[
358 42, 0, 0, 0, 16, 1, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
363 0xc8, 0xc9, 0xca, 0xcb,
364 0xcc, 0xcd, 0xce, 0xcf,
365 PAD, PAD,
367
368 0, 1, 0, 0, 16, 1, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
374 0xd8, 0xd9, 0xda, 0xdb,
375 0xdc, 0xdd, 0xde, 0xdf,
376 PAD, PAD,
378 ];
380
381 {
383 let mut iter = FileChecksumIter::new(data);
384 assert_eq!(iter.rest_len(), 48); let sum0 = iter.next().unwrap();
386 assert_eq!(sum0.name(), NameIndex(42));
387 assert_eq!(
388 sum0.checksum_data,
389 &[
390 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
391 0xce, 0xcf
392 ]
393 );
394
395 assert_eq!(iter.rest_len(), 24); let sum1 = iter.next().unwrap();
398 assert_eq!(sum1.name(), NameIndex(0x100));
399 assert_eq!(
400 sum1.checksum_data,
401 &[
402 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd,
403 0xde, 0xdf,
404 ]
405 );
406
407 assert_eq!(iter.rest_len(), 0); assert!(iter.next().is_none());
409 }
410
411 {
414 let mut data_mut = data.to_vec();
415 let mut iter = FileChecksumMutIter::new(&mut data_mut);
416 assert_eq!(iter.rest_len(), 48); let sum0 = iter.next().unwrap();
418 assert_eq!(sum0.header.name.get(), 42);
419 assert_eq!(
420 sum0.checksum_data,
421 &[
422 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
423 0xce, 0xcf
424 ]
425 );
426
427 assert_eq!(iter.rest_len(), 24); let sum1 = iter.next().unwrap();
430 assert_eq!(sum1.header.name.get(), 0x100);
431 assert_eq!(
432 sum1.checksum_data,
433 &[
434 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd,
435 0xde, 0xdf,
436 ]
437 );
438
439 assert_eq!(iter.rest_len(), 0); assert!(iter.next().is_none());
441 }
442}
443
444#[test]
445fn test_get_file() {
446 const PAD: u8 = 0xaa;
447
448 #[rustfmt::skip]
449 let data = &[
450 42, 0, 0, 0, 0, 0, PAD, PAD, 0xee, 0, 0, 0, 0, 0, PAD, PAD, 0, 0xcc, 0, 0, 0, 0, PAD, PAD, ];
455
456 {
458 let sums = FileChecksumsSubsection::new(data);
459
460 let sum0 = sums.get_file(0).unwrap();
461 assert_eq!(sum0.name(), NameIndex(42));
462
463 let sum1 = sums.get_file(8).unwrap();
464 assert_eq!(sum1.name(), NameIndex(0xee));
465
466 let sum2 = sums.get_file(0x10).unwrap();
467 assert_eq!(sum2.name(), NameIndex(0xcc00));
468
469 assert!(sums.get_file(0x1000).is_err());
471
472 assert!(sums.get_file(0x16).is_err());
474 }
475
476 {
478 let mut data_mut = data.to_vec();
479 let mut sums = FileChecksumsSubsectionMut::new(&mut data_mut);
480
481 let sum0 = sums.get_file_mut(0).unwrap();
482 assert_eq!(sum0.header.name.get(), 42);
483
484 let sum1 = sums.get_file_mut(8).unwrap();
485 assert_eq!(sum1.header.name.get(), 0xee);
486
487 let sum2 = sums.get_file_mut(0x10).unwrap();
488 assert_eq!(sum2.header.name.get(), 0xcc00);
489
490 sum2.header.name = U32::new(0xcafe);
492
493 assert!(sums.get_file_mut(0x1000).is_err());
495
496 assert!(sums.get_file_mut(0x16).is_err());
498
499 #[rustfmt::skip]
500 let expected_data = &[
501 42, 0, 0, 0, 0, 0, PAD, PAD, 0xee, 0, 0, 0, 0, 0, PAD, PAD, 0xfe, 0xca, 0, 0, 0, 0, PAD, PAD, ];
505
506 assert_eq!(data_mut.as_slice(), expected_data);
507 }
508}