use std::io;
use byteorder::{ByteOrder, LittleEndian};
use super::garraylen::*;
use crate::{error::Error, Result};
pub const SIZE_MAIN_HEADER: usize = 40;
pub const SIZE_SECTION_HEADER: usize = 24;
pub const FLAG_COMPRESS_XZ: u8 = 0x2;
pub const FLAG_CHECK_WEAK: u8 = 0x8;
pub const FLAG_COMPRESS_ZLIB: u8 = 0x1;
pub const FLAG_CHECK_CRC32: u8 = 0x4;
pub const SECTION_TYPE_STRING: u8 = 0xFF;
pub const SECTION_TYPE_SD: u8 = 0xFE;
pub const BPX_CURRENT_VERSION: u32 = 0x2;
pub const KNOWN_VERSIONS: &[u32] = &[0x1, 0x2];
#[derive(Copy, Clone)]
pub struct MainHeader
{
pub signature: [u8; 3],
pub btype: u8,
pub chksum: u32,
pub file_size: u64,
pub section_num: u32,
pub version: u32,
pub type_ext: [u8; 16]
}
impl MainHeader
{
pub fn read<TReader: io::Read>(reader: &mut TReader) -> Result<(u32, MainHeader)>
{
let mut buf: [u8; SIZE_MAIN_HEADER] = [0; SIZE_MAIN_HEADER];
let mut checksum: u32 = 0;
reader.read(&mut buf)?;
for i in 0..SIZE_MAIN_HEADER {
if i < 4 || i > 7 {
checksum += buf[i] as u32;
}
}
let head = MainHeader {
signature: extract_slice::<T3>(&buf, 0),
btype: buf[3],
chksum: LittleEndian::read_u32(&buf[4..8]),
file_size: LittleEndian::read_u64(&buf[8..16]),
section_num: LittleEndian::read_u32(&buf[16..20]),
version: LittleEndian::read_u32(&buf[20..24]),
type_ext: extract_slice::<T16>(&buf, 24)
};
if head.signature[0] != 'B' as u8 || head.signature[1] != 'P' as u8 || head.signature[2] != 'X' as u8 {
return Err(Error::Corruption(format!(
"incorrect signature, expected {}{}{}, got {}{}{}",
'B' as u8, 'P' as u8, 'X' as u8, head.signature[0], head.signature[1], head.signature[2]
)));
}
if !KNOWN_VERSIONS.contains(&head.version) {
return Err(Error::Unsupported(format!("unsupported version {}", head.version)));
}
return Ok((checksum, head));
}
pub fn new() -> MainHeader
{
return MainHeader {
signature: ['B' as u8, 'P' as u8, 'X' as u8], btype: 'P' as u8, chksum: 0, file_size: SIZE_MAIN_HEADER as u64, section_num: 0, version: BPX_CURRENT_VERSION, type_ext: [0; 16]
};
}
fn to_bytes(&self) -> [u8; SIZE_MAIN_HEADER]
{
let mut block: [u8; SIZE_MAIN_HEADER] = [0; SIZE_MAIN_HEADER];
block[0] = self.signature[0];
block[1] = self.signature[1];
block[2] = self.signature[2];
block[3] = self.btype;
LittleEndian::write_u32(&mut block[4..8], self.chksum);
LittleEndian::write_u64(&mut block[8..16], self.file_size);
LittleEndian::write_u32(&mut block[16..20], self.section_num);
LittleEndian::write_u32(&mut block[20..24], self.version);
for i in 24..40 {
block[i] = self.type_ext[i - 24];
}
return block;
}
pub fn get_checksum(&self) -> u32
{
let mut checksum: u32 = 0;
let buf = self.to_bytes();
for i in 0..SIZE_MAIN_HEADER {
checksum += buf[i] as u32;
}
return checksum;
}
pub fn write<TWriter: io::Write>(&self, writer: &mut TWriter) -> io::Result<()>
{
let buf = self.to_bytes();
writer.write(&buf)?;
writer.flush()?;
return Ok(());
}
}
#[derive(Copy, Clone)]
pub struct SectionHeader
{
pub pointer: u64,
pub csize: u32,
pub size: u32,
pub chksum: u32,
pub btype: u8,
pub flags: u8
}
impl SectionHeader
{
pub fn read<TReader: io::Read>(reader: &mut TReader) -> io::Result<(u32, SectionHeader)>
{
let mut buf: [u8; SIZE_SECTION_HEADER] = [0; SIZE_SECTION_HEADER];
let mut checksum: u32 = 0;
reader.read(&mut buf)?;
for i in 0..SIZE_SECTION_HEADER {
checksum += buf[i] as u32;
}
return Ok((
checksum,
SectionHeader {
pointer: LittleEndian::read_u64(&buf[0..8]),
csize: LittleEndian::read_u32(&buf[8..12]),
size: LittleEndian::read_u32(&buf[12..16]),
chksum: LittleEndian::read_u32(&buf[16..20]),
btype: buf[20],
flags: buf[21]
}
));
}
pub fn new() -> SectionHeader
{
return SectionHeader {
pointer: 0, csize: 0, size: 0, chksum: 0, btype: 0, flags: 0 };
}
pub fn is_huge_section(&self) -> bool
{
return self.size > 100000000;
}
fn to_bytes(&self) -> [u8; SIZE_SECTION_HEADER]
{
let mut block: [u8; SIZE_SECTION_HEADER] = [0; SIZE_SECTION_HEADER];
LittleEndian::write_u64(&mut block[0..8], self.pointer);
LittleEndian::write_u32(&mut block[8..12], self.csize);
LittleEndian::write_u32(&mut block[12..16], self.size);
LittleEndian::write_u32(&mut block[16..20], self.chksum);
block[20] = self.btype;
block[21] = self.flags;
return block;
}
pub fn get_checksum(&self) -> u32
{
let mut checksum: u32 = 0;
let buf = self.to_bytes();
for i in 0..SIZE_SECTION_HEADER {
checksum += buf[i] as u32;
}
return checksum;
}
pub fn write<TWriter: io::Write>(&self, writer: &mut TWriter) -> io::Result<()>
{
let buf = self.to_bytes();
writer.write(&buf)?;
writer.flush()?;
return Ok(());
}
}