use super::{MarFileInfo, MarItem};
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{self, BufRead, ErrorKind, Read, Seek, SeekFrom};
use std::u32;
const MAR_ID: &[u8; MAR_ID_SIZE] = b"MAR1";
const MAR_ID_SIZE: usize = 4;
const SIGNATURE_BLOCK_OFFSET: u64 = 16;
pub fn get_info<R>(mut archive: R) -> io::Result<MarFileInfo>
where
R: Read + Seek,
{
archive.rewind()?;
let mut id = [0; MAR_ID_SIZE];
archive.read_exact(&mut id)?;
if id != *MAR_ID {
return Err(io::Error::new(
ErrorKind::InvalidData,
"Not a MAR file (invalid bytes at start of file).",
));
}
let offset_to_index = archive.read_u32::<BigEndian>()?;
let num_signatures = archive.read_u32::<BigEndian>()?;
archive.seek(SeekFrom::Start(offset_to_index as u64))?;
let offset_to_content = archive.read_u32::<BigEndian>()?;
let has_signature_block = offset_to_content as usize != MAR_ID_SIZE + 4;
archive.seek(SeekFrom::Start(SIGNATURE_BLOCK_OFFSET))?;
for _ in 0..num_signatures {
archive.seek(SeekFrom::Current(4))?;
let signature_len = archive.read_u32::<BigEndian>()?;
archive.seek(SeekFrom::Current(signature_len as i64))?;
}
let pos = archive.seek(SeekFrom::Current(0))?;
if pos > u32::MAX as u64 {
return Err(io::Error::new(
ErrorKind::InvalidData,
"Signature block size overflow",
));
}
let offset_additional_blocks = pos as u32;
let has_additional_blocks = offset_additional_blocks == offset_to_content;
let num_additional_blocks = if has_additional_blocks {
archive.read_u32::<BigEndian>()?
} else {
0
};
Ok(MarFileInfo {
offset_to_index,
has_signature_block,
num_signatures,
offset_additional_blocks,
has_additional_blocks,
num_additional_blocks,
})
}
pub fn read_index<R>(mut archive: R) -> io::Result<Vec<MarItem>>
where
R: Read + Seek,
{
archive.rewind()?;
let mut id = [0; MAR_ID_SIZE];
archive.read_exact(&mut id)?;
if id != *MAR_ID {
return Err(io::Error::new(
ErrorKind::InvalidData,
"Not a MAR file (invalid bytes at start of file).",
));
}
let offset_to_index = archive.read_u32::<BigEndian>()?;
archive.seek(SeekFrom::Start(offset_to_index as u64))?;
let size_of_index = archive.read_u32::<BigEndian>()?;
let mut buf = vec![0; size_of_index as usize];
archive.read_exact(&mut buf)?;
let mut items = vec![];
let mut buf = &buf[..];
while !buf.is_empty() {
items.push(read_next_item(&mut buf)?);
}
Ok(items)
}
pub(crate) fn read_next_item<R: BufRead>(mut index: R) -> io::Result<MarItem> {
let offset = index.read_u32::<BigEndian>()?;
let length = index.read_u32::<BigEndian>()?;
let flags = index.read_u32::<BigEndian>()?;
let mut name = Vec::new();
index.read_until(0, &mut name)?;
name.pop();
let name = String::from_utf8(name)
.map_err(|_| io::Error::new(ErrorKind::InvalidData, "Filename is not UTF-8"))?;
Ok(MarItem {
offset,
length,
flags,
name,
})
}