bpx/header.rs
1// Copyright (c) 2021, BlockProject 3D
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification,
6// are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of BlockProject 3D nor the names of its contributors
14// may be used to endorse or promote products derived from this software
15// without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29//! Declarations for basic constants and low-level file headers.
30
31use std::io;
32
33use byteorder::{ByteOrder, LittleEndian};
34
35use super::garraylen::*;
36use crate::{error::Error, Result};
37
38/// The size in bytes of the BPX Main Header.
39pub const SIZE_MAIN_HEADER: usize = 40;
40
41/// The size in bytes of a BPX Section Header.
42pub const SIZE_SECTION_HEADER: usize = 24;
43
44/// XZ section compression enable flag.
45pub const FLAG_COMPRESS_XZ: u8 = 0x2;
46
47/// Section weak checksum enable flag.
48pub const FLAG_CHECK_WEAK: u8 = 0x8;
49
50/// ZLIB section compression enable flag.
51pub const FLAG_COMPRESS_ZLIB: u8 = 0x1;
52
53/// Section CRC32 checksum enable flag.
54pub const FLAG_CHECK_CRC32: u8 = 0x4;
55
56/// The standard variant for a BPX Strings section.
57pub const SECTION_TYPE_STRING: u8 = 0xFF;
58
59/// The standard variant for a BPX Structured Data section.
60pub const SECTION_TYPE_SD: u8 = 0xFE;
61
62/// The BPX version this crate supports.
63pub const BPX_CURRENT_VERSION: u32 = 0x2;
64
65/// The values allowed for the version field in BPX main header.
66pub const KNOWN_VERSIONS: &[u32] = &[0x1, 0x2];
67
68/// The BPX Main Header.
69#[derive(Copy, Clone)]
70pub struct MainHeader
71{
72 /// BPX signature.
73 ///
74 /// Offset: +0
75 pub signature: [u8; 3],
76
77 /// Type byte.
78 ///
79 /// Offset: +3
80 pub btype: u8,
81
82 /// Weak checksum of all headers.
83 ///
84 /// Offset: +4
85 pub chksum: u32,
86
87 /// Total size of BPX in bytes.
88 ///
89 /// Offset: +8
90 pub file_size: u64,
91
92 /// Number of sections.
93 ///
94 /// Offset: +16
95 pub section_num: u32,
96
97 /// Version of BPX.
98 ///
99 /// Offset: +20
100 pub version: u32,
101
102 /// Extended Type Information.
103 ///
104 /// Offset: +24
105 pub type_ext: [u8; 16]
106}
107
108impl MainHeader
109{
110 /// Attempts to read a BPX Main Header from an IO backend.
111 ///
112 /// # Arguments
113 ///
114 /// * `reader`: the IO backend to read from.
115 ///
116 /// returns: Result<(u32, MainHeader), Error>
117 ///
118 /// # Errors
119 ///
120 /// Returns an [Error](crate::error::Error) if the data could not be
121 /// read from the IO backend or if the header is corrupted.
122 ///
123 /// # Examples
124 ///
125 /// ```should_panic
126 /// use bpx::header::{MainHeader, SIZE_MAIN_HEADER};
127 ///
128 /// let mut corrupted: [u8; SIZE_MAIN_HEADER] = [0; SIZE_MAIN_HEADER];
129 /// MainHeader::read(&mut corrupted.as_ref()).unwrap();
130 /// ```
131 pub fn read<TReader: io::Read>(reader: &mut TReader) -> Result<(u32, MainHeader)>
132 {
133 let mut buf: [u8; SIZE_MAIN_HEADER] = [0; SIZE_MAIN_HEADER];
134 let mut checksum: u32 = 0;
135
136 reader.read(&mut buf)?;
137 for i in 0..SIZE_MAIN_HEADER {
138 if i < 4 || i > 7 {
139 checksum += buf[i] as u32;
140 }
141 }
142 let head = MainHeader {
143 signature: extract_slice::<T3>(&buf, 0),
144 btype: buf[3],
145 chksum: LittleEndian::read_u32(&buf[4..8]),
146 file_size: LittleEndian::read_u64(&buf[8..16]),
147 section_num: LittleEndian::read_u32(&buf[16..20]),
148 version: LittleEndian::read_u32(&buf[20..24]),
149 type_ext: extract_slice::<T16>(&buf, 24)
150 };
151 if head.signature[0] != 'B' as u8 || head.signature[1] != 'P' as u8 || head.signature[2] != 'X' as u8 {
152 return Err(Error::Corruption(format!(
153 "incorrect signature, expected {}{}{}, got {}{}{}",
154 'B' as u8, 'P' as u8, 'X' as u8, head.signature[0], head.signature[1], head.signature[2]
155 )));
156 }
157 if !KNOWN_VERSIONS.contains(&head.version) {
158 return Err(Error::Unsupported(format!("unsupported version {}", head.version)));
159 }
160 return Ok((checksum, head));
161 }
162
163 /// Creates a new empty BPX Main Header.
164 pub fn new() -> MainHeader
165 {
166 return MainHeader {
167 signature: ['B' as u8, 'P' as u8, 'X' as u8], //+0
168 btype: 'P' as u8, //+3
169 chksum: 0, //+4
170 file_size: SIZE_MAIN_HEADER as u64, //+8
171 section_num: 0, //+16
172 version: BPX_CURRENT_VERSION, //+20
173 type_ext: [0; 16]
174 };
175 }
176
177 fn to_bytes(&self) -> [u8; SIZE_MAIN_HEADER]
178 {
179 let mut block: [u8; SIZE_MAIN_HEADER] = [0; SIZE_MAIN_HEADER];
180 block[0] = self.signature[0];
181 block[1] = self.signature[1];
182 block[2] = self.signature[2];
183 block[3] = self.btype;
184 LittleEndian::write_u32(&mut block[4..8], self.chksum);
185 LittleEndian::write_u64(&mut block[8..16], self.file_size);
186 LittleEndian::write_u32(&mut block[16..20], self.section_num);
187 LittleEndian::write_u32(&mut block[20..24], self.version);
188 for i in 24..40 {
189 block[i] = self.type_ext[i - 24];
190 }
191 return block;
192 }
193
194 /// Computes the checksum for this header.
195 pub fn get_checksum(&self) -> u32
196 {
197 let mut checksum: u32 = 0;
198 let buf = self.to_bytes();
199 for i in 0..SIZE_MAIN_HEADER {
200 checksum += buf[i] as u32;
201 }
202 return checksum;
203 }
204
205 /// Attempts to write this header to an IO backend.
206 ///
207 /// # Arguments
208 ///
209 /// * `writer`: the IO backend to write to.
210 ///
211 /// returns: Result<(), Error>
212 ///
213 /// # Errors
214 ///
215 /// Returns an [Error](crate::error::Error) if the data could not be
216 /// written to the IO backend.
217 pub fn write<TWriter: io::Write>(&self, writer: &mut TWriter) -> io::Result<()>
218 {
219 let buf = self.to_bytes();
220 writer.write(&buf)?;
221 writer.flush()?;
222 return Ok(());
223 }
224}
225
226/// The BPX Section Header.
227#[derive(Copy, Clone)]
228pub struct SectionHeader
229{
230 /// Data pointer.
231 ///
232 /// Offset: +0
233 pub pointer: u64,
234
235 /// Size in bytes after compression.
236 ///
237 /// Offset: +8
238 pub csize: u32,
239
240 /// Size in bytes before compression.
241 ///
242 /// Offset: +12
243 pub size: u32,
244
245 /// Data checksum.
246 ///
247 /// Offset: +16
248 pub chksum: u32,
249
250 /// Type byte.
251 ///
252 /// Offset: +20
253 pub btype: u8,
254
255 /// Flags (see FLAG_* constants).
256 ///
257 /// Offset: +21
258 pub flags: u8
259}
260
261impl SectionHeader
262{
263 /// Attempts to read a BPX Section Header from an IO backend.
264 ///
265 /// # Arguments
266 ///
267 /// * `reader`: the IO backend to read from.
268 ///
269 /// returns: Result<(u32, SectionHeader), Error>
270 ///
271 /// # Errors
272 ///
273 /// Returns an [Error](crate::error::Error) if the data could not be
274 /// read from the IO backend.
275 pub fn read<TReader: io::Read>(reader: &mut TReader) -> io::Result<(u32, SectionHeader)>
276 {
277 let mut buf: [u8; SIZE_SECTION_HEADER] = [0; SIZE_SECTION_HEADER];
278 let mut checksum: u32 = 0;
279
280 reader.read(&mut buf)?;
281 for i in 0..SIZE_SECTION_HEADER {
282 checksum += buf[i] as u32;
283 }
284 return Ok((
285 checksum,
286 SectionHeader {
287 pointer: LittleEndian::read_u64(&buf[0..8]),
288 csize: LittleEndian::read_u32(&buf[8..12]),
289 size: LittleEndian::read_u32(&buf[12..16]),
290 chksum: LittleEndian::read_u32(&buf[16..20]),
291 btype: buf[20],
292 flags: buf[21]
293 }
294 ));
295 }
296
297 /// Creates a new empty BPX Section Header.
298 pub fn new() -> SectionHeader
299 {
300 return SectionHeader {
301 pointer: 0, //+0
302 csize: 0, //+8
303 size: 0, //+12
304 chksum: 0, //+16
305 btype: 0, //+20
306 flags: 0 //+21
307 };
308 }
309
310 /// Checks if this section is huge (greater than 100Mb).
311 pub fn is_huge_section(&self) -> bool
312 {
313 return self.size > 100000000;
314 }
315
316 fn to_bytes(&self) -> [u8; SIZE_SECTION_HEADER]
317 {
318 let mut block: [u8; SIZE_SECTION_HEADER] = [0; SIZE_SECTION_HEADER];
319 LittleEndian::write_u64(&mut block[0..8], self.pointer);
320 LittleEndian::write_u32(&mut block[8..12], self.csize);
321 LittleEndian::write_u32(&mut block[12..16], self.size);
322 LittleEndian::write_u32(&mut block[16..20], self.chksum);
323 block[20] = self.btype;
324 block[21] = self.flags;
325 return block;
326 }
327
328 /// Computes the checksum for this header.
329 pub fn get_checksum(&self) -> u32
330 {
331 let mut checksum: u32 = 0;
332 let buf = self.to_bytes();
333 for i in 0..SIZE_SECTION_HEADER {
334 checksum += buf[i] as u32;
335 }
336 return checksum;
337 }
338
339 /// Attempts to write this header to an IO backend.
340 ///
341 /// # Arguments
342 ///
343 /// * `writer`: the IO backend to write to.
344 ///
345 /// returns: Result<(), Error>
346 ///
347 /// # Errors
348 ///
349 /// Returns an [Error](crate::error::Error) if the data could not be
350 /// written to the IO backend.
351 pub fn write<TWriter: io::Write>(&self, writer: &mut TWriter) -> io::Result<()>
352 {
353 let buf = self.to_bytes();
354 writer.write(&buf)?;
355 writer.flush()?;
356 return Ok(());
357 }
358}