use super::*;
#[macro_export]
macro_rules! static_bundle {
($vis:vis $name:ident = $path:expr) => {
$vis static $name: [$crate::Block; include_bytes!($path).len() / ::core::mem::size_of::<$crate::Block>()] = unsafe {
::core::mem::transmute(*include_bytes!($path))
};
}
}
pub struct BundleReader<'a> {
blocks: &'a [Block],
directory: Section,
key: Key,
}
impl<'a> BundleReader<'a> {
#[inline]
pub fn open(blocks: &'a [Block], key: Key) -> Result<Self, ErrorKind> {
open(blocks, key)
}
#[inline]
pub fn key(&self) -> &Key {
&self.key
}
pub fn find_desc(&self, path: &[u8]) -> Option<Descriptor> {
let encrypted_dir = get_directory(self.blocks, &self.directory)?;
dir::find_encrypted(encrypted_dir, path, &self.directory, &self.key)
}
#[inline]
pub fn find_file(&self, path: &[u8]) -> Option<Descriptor> {
match self.find_desc(path) {
Some(desc) if desc.is_file() => Some(desc),
_ => None
}
}
}
impl<'a> BundleReader<'a> {
pub fn read(&self, path: &[u8], key: &Key) -> Result<Vec<u8>, ErrorKind> {
let desc = match self.find_file(path) {
Some(desc) => desc,
None => return Err(ErrorKind::NotFound),
};
self.read_data(&desc, key)
}
pub fn read_to_string(&self, path: &[u8], key: &Key) -> Result<String, ErrorKind> {
let desc = match self.find_file(path) {
Some(desc) => desc,
None => return Err(ErrorKind::NotFound),
};
let data = self.read_data(&desc, key)?;
String::from_utf8(data).map_err(|_| ErrorKind::InvalidData)
}
#[inline]
pub fn read_section(&self, section: &Section, key: &Key) -> Result<Vec<Block>, ErrorKind> {
read_section(&self.blocks, section, key)
}
#[inline]
pub fn read_data(&self, desc: &Descriptor, key: &Key) -> Result<Vec<u8>, ErrorKind> {
read_data(&self.blocks, desc, key)
}
#[inline]
pub fn read_data_into(&self, desc: &Descriptor, key: &Key, byte_offset: usize, dest: &mut [u8]) -> Result<(), ErrorKind> {
read_data_into(&self.blocks, desc, key, byte_offset, dest)
}
}
#[inline]
fn get_directory<'a>(blocks: &'a [Block], directory: &Section) -> Option<&'a [Descriptor]> {
let start = directory.offset as usize;
let len = directory.size as usize;
let size = len.checked_mul(Descriptor::BLOCKS_LEN)?;
let end = start.checked_add(size)?;
let blocks = blocks.get(start..end)?;
let descriptors = unsafe {
slice::from_raw_parts(blocks.as_ptr() as *const Descriptor, len)
};
Some(descriptors)
}
fn open<'a>(blocks: &'a [Block], key: Key) -> Result<BundleReader<'a>, ErrorKind> {
if blocks.len() < Header::BLOCKS_LEN {
return Err(ErrorKind::InvalidInput);
}
let header_blocks: [Block; Header::BLOCKS_LEN] = blocks[..Header::BLOCKS_LEN].try_into().unwrap();
let mut header = Header::from(header_blocks);
if !crypt::decrypt_header(&mut header, &key) {
return Err(ErrorKind::InvalidData);
}
let encrypted_dir = match get_directory(blocks, &header.info.directory) {
Some(encrypted_dir) => encrypted_dir,
None => return Err(ErrorKind::InvalidInput),
};
let encrypted_dir_blocks = unsafe {
slice::from_raw_parts(encrypted_dir.as_ptr() as *const Block, encrypted_dir.len() * Descriptor::BLOCKS_LEN)
};
if !crypt::verify_section(encrypted_dir_blocks, &header.info.directory, &key) {
return Err(ErrorKind::InvalidData);
}
Ok(BundleReader {
blocks,
directory: header.info.directory,
key,
})
}