ryg_rans_rs_cli/container/
header.rs1use crate::container::{HEADER_SIZE, MAGIC, MAJOR_VERSION, MINOR_VERSION};
4use crate::error::{AppError, FormatError};
5
6pub mod flags {
8 pub const GLOBAL_MODEL: u16 = 0x0001;
9 pub const ALWAYS_COMPRESS: u16 = 0x0002;
10 pub const HAS_EXTRA_HEADER: u16 = 0x0004;
11 pub const KNOWN_FLAGS: u16 = GLOBAL_MODEL | ALWAYS_COMPRESS | HAS_EXTRA_HEADER;
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct FileHeader {
18 pub major_version: u16,
19 pub minor_version: u16,
20 pub flags: u16,
21 pub default_codec_id: u16,
22 pub default_scale_bits: u8,
23 pub default_model_mode: u8,
24 pub declared_block_size: u32,
25}
26
27impl Default for FileHeader {
28 fn default() -> Self {
29 Self {
30 major_version: MAJOR_VERSION,
31 minor_version: MINOR_VERSION,
32 flags: 0,
33 default_codec_id: 2, default_scale_bits: 12,
35 default_model_mode: 0, declared_block_size: crate::limits::DEFAULT_BLOCK_SIZE,
37 }
38 }
39}
40
41impl FileHeader {
42 pub fn to_bytes(&self) -> [u8; HEADER_SIZE] {
44 let mut buf = [0u8; HEADER_SIZE];
45 buf[0..8].copy_from_slice(MAGIC);
46 buf[8..10].copy_from_slice(&self.major_version.to_le_bytes());
47 buf[10..12].copy_from_slice(&self.minor_version.to_le_bytes());
48 buf[12..14].copy_from_slice(&(HEADER_SIZE as u16).to_le_bytes());
49 buf[14..16].copy_from_slice(&self.flags.to_le_bytes());
50 buf[16..18].copy_from_slice(&self.default_codec_id.to_le_bytes());
51 buf[18] = self.default_scale_bits;
52 buf[19] = self.default_model_mode;
53 buf[20..24].copy_from_slice(&self.declared_block_size.to_le_bytes());
54 buf
56 }
57
58 pub fn from_bytes(bytes: &[u8]) -> Result<Self, AppError> {
60 if bytes.len() < HEADER_SIZE {
61 return Err(AppError::Format(FormatError {
62 detail: "header too short".into(),
63 block_index: None,
64 offset: Some(0),
65 }));
66 }
67
68 let magic = &bytes[0..8];
69 if magic != MAGIC {
70 return Err(AppError::Format(FormatError {
71 detail: format!("bad magic: expected RYGRANS\\0, got {:02x?}", magic),
72 block_index: None,
73 offset: Some(0),
74 }));
75 }
76
77 let major_version = u16::from_le_bytes([bytes[8], bytes[9]]);
78 if major_version != MAJOR_VERSION {
79 return Err(AppError::Format(FormatError {
80 detail: format!("unsupported major version: {}", major_version),
81 block_index: None,
82 offset: Some(8),
83 }));
84 }
85
86 let minor_version = u16::from_le_bytes([bytes[10], bytes[11]]);
87 let header_length = u16::from_le_bytes([bytes[12], bytes[13]]);
88 if header_length != HEADER_SIZE as u16 {
89 return Err(AppError::Format(FormatError {
90 detail: format!("unexpected header length: {}", header_length),
91 block_index: None,
92 offset: Some(12),
93 }));
94 }
95
96 let flags = u16::from_le_bytes([bytes[14], bytes[15]]);
97 let unknown_flags = flags & !flags::KNOWN_FLAGS;
99 if unknown_flags != 0 {
100 return Err(AppError::Format(FormatError {
101 detail: format!("unknown flags: {:04x}", unknown_flags),
102 block_index: None,
103 offset: Some(14),
104 }));
105 }
106
107 if bytes[24..32].iter().any(|&b| b != 0) {
109 return Err(AppError::Format(FormatError {
110 detail: "non-zero reserved bytes in header".into(),
111 block_index: None,
112 offset: Some(24),
113 }));
114 }
115
116 let default_codec_id = u16::from_le_bytes([bytes[16], bytes[17]]);
117 let default_scale_bits = bytes[18];
118 let default_model_mode = bytes[19];
119 let declared_block_size = u32::from_le_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]);
120
121 if declared_block_size == 0 {
122 return Err(AppError::Format(FormatError {
123 detail: "declared block size is zero".into(),
124 block_index: None,
125 offset: Some(20),
126 }));
127 }
128
129 Ok(Self {
130 major_version,
131 minor_version,
132 flags,
133 default_codec_id,
134 default_scale_bits,
135 default_model_mode,
136 declared_block_size,
137 })
138 }
139
140 pub fn has_global_model(&self) -> bool {
142 self.flags & flags::GLOBAL_MODEL != 0
143 }
144}