Skip to main content

rar_stream/parsing/
marker_header.rs

1//! Marker header parser - RAR signature.
2//!
3//! The marker header is the first 7 bytes of a RAR file.
4//! RAR4: 0x52 0x61 0x72 0x21 0x1A 0x07 0x00
5//! RAR5: 0x52 0x61 0x72 0x21 0x1A 0x07 0x01 0x00
6
7use crate::error::{RarError, Result};
8
9/// RAR4 magic signature.
10pub const RAR4_SIGNATURE: [u8; 7] = [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00];
11
12/// RAR5 magic signature.
13pub const RAR5_SIGNATURE: [u8; 8] = [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00];
14
15#[derive(Debug, Clone)]
16pub struct MarkerHeader {
17    pub crc: u16,
18    pub header_type: u8,
19    pub flags: u16,
20    pub size: u32,
21}
22
23pub struct MarkerHeaderParser;
24
25impl MarkerHeaderParser {
26    pub const HEADER_SIZE: usize = 7;
27
28    /// Parse marker header from buffer.
29    /// The marker header is actually parsed as a generic RAR header.
30    /// The "size" field tells us how many bytes this header consumes.
31    pub fn parse(buffer: &[u8]) -> Result<MarkerHeader> {
32        if buffer.len() < Self::HEADER_SIZE {
33            return Err(RarError::BufferTooSmall {
34                needed: Self::HEADER_SIZE,
35                have: buffer.len(),
36            });
37        }
38
39        // Verify RAR4 signature (first 7 bytes)
40        if buffer[..7] != RAR4_SIGNATURE {
41            // Check for RAR5
42            if buffer.len() >= 8 && buffer[..8] == RAR5_SIGNATURE {
43                return Err(RarError::InvalidSignature); // RAR5 not supported yet
44            }
45            return Err(RarError::InvalidSignature);
46        }
47
48        // Parse as generic header structure
49        let crc = u16::from_le_bytes([buffer[0], buffer[1]]);
50        let header_type = buffer[2];
51        let flags = u16::from_le_bytes([buffer[3], buffer[4]]);
52        let size = u16::from_le_bytes([buffer[5], buffer[6]]) as u32;
53
54        Ok(MarkerHeader {
55            crc,
56            header_type,
57            flags,
58            size,
59        })
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn test_parse_rar4_marker() {
69        // RAR4 signature + header
70        let buffer = [
71            0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00, // RAR4 signature
72            0x00, 0x00, 0x00, 0x00, // padding for HEADER_SIZE
73        ];
74        let header = MarkerHeaderParser::parse(&buffer).unwrap();
75        assert_eq!(header.header_type, b'r'); // 0x72
76    }
77
78    #[test]
79    fn test_invalid_signature() {
80        let buffer = [0x00; 11];
81        assert!(matches!(
82            MarkerHeaderParser::parse(&buffer),
83            Err(RarError::InvalidSignature)
84        ));
85    }
86
87    #[test]
88    fn test_buffer_too_small() {
89        let buffer = [0x52, 0x61, 0x72];
90        assert!(matches!(
91            MarkerHeaderParser::parse(&buffer),
92            Err(RarError::BufferTooSmall { .. })
93        ));
94    }
95}