use std::io::Write;
use std::panic::Location;
use crate::helper::{ParseProblem, Parser, ProblemLocation, Seeker};
use crate::Result;
static MAGIC: u32 = 0x4F534943;
#[derive(Debug)]
struct Header {
block_size: usize,
blocks: Vec<(u64, bool)>,
}
impl Header {
pub fn from_binary<D: Parser + Seeker>(input: &mut D) -> Result<Self> {
let magic = input.bu32()?;
let block_size = input.bu32()? as usize;
if magic != MAGIC {
return Err(
ParseProblem::InvalidMagic("expected: 0x4F534943", Location::current()).into(),
);
} else if block_size == 0 || block_size > 0x8000000 {
return Err(ParseProblem::InvalidRange(
"0 < block size <= 0x8000000",
Location::current(),
)
.into());
}
let block_map = input.u8_array::<{ 0x8000 - 8 }>()?;
let block_total = block_map
.iter()
.enumerate()
.filter_map(|(i, &x)| if x != 0 { Some(i + 1) } else { None })
.max();
if let Some(block_total) = block_total {
let mut blocks = block_map
.iter()
.take(block_total)
.enumerate()
.map(|x| (0_u64, *x.1 == 1))
.collect::<Vec<_>>();
let mut offset = 0_u64;
for block in blocks.iter_mut() {
if block.1 {
block.0 = offset;
offset += block_size as u64;
}
}
Ok(Header { block_size, blocks })
} else {
Err(ParseProblem::InvalidHeader("invalid block map", Location::current()).into())
}
}
}
pub struct CisoReader<'reader, D: Parser + Seeker> {
header: Header,
reader: &'reader mut D,
data_offset: u64,
}
impl<'reader, D: Parser + Seeker> CisoReader<'reader, D> {
pub fn new(reader: &'reader mut D) -> Result<Self> {
let header = Header::from_binary(reader)?;
let data_offset = reader.position()?;
Ok(Self {
header,
reader,
data_offset,
})
}
pub fn block_size(&self) -> usize { self.header.block_size }
pub fn total_size(&self) -> usize { self.header.blocks.len() * self.header.block_size }
pub fn read_block(&mut self, index: usize) -> Result<Vec<u8>> {
let (offset, has_data) = self.header.blocks[index];
let mut buffer = vec![0; self.header.block_size];
if has_data {
self.reader.goto(self.data_offset + offset)?;
self.reader.read_into(&mut buffer)?;
}
Ok(buffer)
}
pub fn blocks<'this>(&'this mut self) -> BlockIterator<'this, 'reader, D> {
BlockIterator {
reader: self,
index: 0,
}
}
pub fn decompress<Writer: Write>(&'reader mut self, writer: &mut Writer) -> Result<()> {
self.blocks().try_for_each(|x| match x {
Ok(x) => Ok(writer.write_all(&x)?),
Err(e) => Err(e),
})
}
}
pub struct BlockIterator<'reader, 'x, D: Parser + Seeker> {
reader: &'reader mut CisoReader<'x, D>,
index: usize,
}
impl<'reader, 'x, D: Parser + Seeker> Iterator for BlockIterator<'reader, 'x, D> {
type Item = Result<Vec<u8>>;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.reader.header.blocks.len() {
None
} else {
let data = self.reader.read_block(self.index);
self.index += 1;
Some(data)
}
}
}