use std::io::{Error, ErrorKind, Read, Result, Take};
use flate2::read::ZlibDecoder;
use liblzma::read::XzDecoder;
use zerocopy::LE;
use crate::{
ReadBytesExt,
compression::Compression,
error::InnoError,
lzma_stream_header::LzmaStreamHeader,
read::{
block::{INNO_BLOCK_SIZE, InnoBlockReader},
crc32::Crc32Reader,
decoder::Decoder,
},
version::InnoVersion,
};
pub struct InnoStreamReader<R: Read> {
inner: Decoder<InnoBlockReader<Take<R>>>,
compression: Compression,
inno_version: InnoVersion,
}
impl<R: Read> InnoStreamReader<R> {
pub fn new(mut inner: R, version: InnoVersion) -> Result<Self> {
let compression = Self::read_header(&mut inner, version)?;
let mut chunk_reader = InnoBlockReader::new(inner.take(compression.size()));
Ok(Self {
inner: match compression {
Compression::LZMA1(_) => {
let stream = LzmaStreamHeader::read(&mut chunk_reader)?;
Decoder::LZMA1(XzDecoder::new_stream(chunk_reader, stream))
}
Compression::Zlib(_) => Decoder::Zlib(ZlibDecoder::new(chunk_reader)),
Compression::Stored(_) => Decoder::Stored(chunk_reader),
},
compression,
inno_version: version,
})
}
fn read_header(reader: &mut R, version: InnoVersion) -> Result<Compression> {
let expected_crc32 = reader.read_u32::<LE>()?;
let mut crc32_reader = Crc32Reader::new(reader);
let compression = if version >= (4, 0, 9) {
let size = if version >= 6.7 {
crc32_reader.read_u64::<LE>()?
} else {
crc32_reader.read_u32::<LE>()?.into()
};
let compressed = crc32_reader.read_u8()? != 0;
if compressed {
if version >= (4, 1, 6) {
Compression::LZMA1(size)
} else {
Compression::Zlib(size)
}
} else {
Compression::Stored(size)
}
} else {
let compressed_size = crc32_reader.read_u32::<LE>()?;
let uncompressed_size = crc32_reader.read_u32::<LE>()?;
let mut compression = if compressed_size.cast_signed() == -1 {
Compression::Stored(uncompressed_size.into())
} else {
Compression::Zlib(compressed_size.into())
};
*compression.size_mut() += compression.size().div_ceil(INNO_BLOCK_SIZE.into()) * 4;
compression
};
let actual_crc32 = crc32_reader.finalize();
if actual_crc32 != expected_crc32 {
return Err(Error::new(
ErrorKind::InvalidData,
InnoError::CrcChecksumMismatch {
location: "Inno stream header",
actual: actual_crc32,
expected: expected_crc32,
},
));
}
Ok(compression)
}
pub fn reset(self) -> Result<Self> {
let version = self.inno_version;
let reader = self
.into_inner() .into_inner() .into_inner() .into_inner(); Self::new(reader, version)
}
#[must_use]
#[inline]
pub const fn get_ref(&self) -> &Decoder<InnoBlockReader<Take<R>>> {
&self.inner
}
#[must_use]
#[inline]
pub const fn get_mut(&mut self) -> &mut Decoder<InnoBlockReader<Take<R>>> {
&mut self.inner
}
#[must_use]
#[inline]
pub fn into_inner(self) -> Decoder<InnoBlockReader<Take<R>>> {
self.inner
}
#[must_use]
pub fn is_end_of_stream(&self) -> bool {
self.get_ref().get_ref().total_in() == self.compression.size() as usize
}
}
impl<R: Read> Read for InnoStreamReader<R> {
fn read(&mut self, dest: &mut [u8]) -> Result<usize> {
self.inner.read(dest)
}
}