use std::io::{Cursor, Read, Seek};
use crate::{
error::{Error, Result},
file::BasicFileEntry,
path::ArchiveFlags,
read::{read_u32, read_unsigned},
};
#[derive(Default)]
pub struct DecryptionKey {
pub key: [u8; 32],
pub iv: [u8; 16],
}
pub(crate) struct TableOfContent {
length: u32,
entry_count: u32,
}
impl TableOfContent {
const ENTRY_SIZE: usize = 30;
pub(crate) fn read<R>(reader: &mut R) -> Result<Self>
where
R: Read,
{
let length = read_u32(reader)?;
let entry_size = read_u32(reader)?;
if entry_size as usize != Self::ENTRY_SIZE {
return Err(Error::Corrupt("table of content entry size mismatch"));
}
let entry_count = read_u32(reader)?;
Ok(Self {
length,
entry_count,
})
}
pub(crate) fn read_entries<R>(
&self,
reader: &mut R,
archive_flags: ArchiveFlags,
decryption_key: Option<DecryptionKey>,
) -> Result<Vec<TableOfContentEntry>>
where
R: Read + Seek,
{
let entry_bytes = self.entry_count as usize * Self::ENTRY_SIZE;
let mut entries_buffer = vec![0; entry_bytes];
reader.read_exact(&mut entries_buffer)?;
archive_flags.decrypt(&mut entries_buffer, decryption_key)?;
let mut cursor = Cursor::new(entries_buffer);
let mut entries = (0..(self.entry_count as usize))
.map(|_| TableOfContentEntry::read(&mut cursor))
.collect::<Result<Vec<_>>>()?;
for index in 0..entries.len() - 1 {
entries[index].input_size = entries[index + 1].offset - entries[index].offset;
}
Ok(entries)
}
pub(crate) const fn blocks_amount(&self) -> usize {
let blocks_offset = Self::ENTRY_SIZE * self.entry_count as usize + 32;
(self.length as usize - blocks_offset) / 2
}
}
#[derive(Debug)]
pub(crate) struct TableOfContentEntry {
pub(crate) index_list_size: u32,
pub(crate) size: usize,
pub(crate) offset: usize,
pub(crate) input_size: usize,
}
impl TableOfContentEntry {
pub(crate) fn read<R>(reader: &mut R) -> Result<Self>
where
R: Read + Seek,
{
reader
.seek_relative(i64::try_from(size_of::<u128>()).map_err(|_| Error::AddressTooSmall)?)?;
let index_list_size = read_u32(reader)?;
let size = read_unsigned::<5>(reader)?;
let offset = read_unsigned::<5>(reader)?;
Ok(Self {
index_list_size,
size,
offset,
input_size: 0,
})
}
}
impl BasicFileEntry for TableOfContentEntry {
fn index_list_size(&self) -> u32 {
self.index_list_size
}
fn input_size(&self) -> usize {
self.input_size
}
fn output_size(&self) -> usize {
self.size
}
fn offset(&self) -> usize {
self.offset
}
}