use core::fmt;
use crate::error::{LhaResult, LhaError};
use crate::stub_io::{Read, Take, discard_to_end};
use crate::crc::Crc16;
use crate::header::{CompressionMethod, LhaHeader};
#[cfg(feature = "lz")]
mod lzs;
#[cfg(feature = "lz")]
mod lz5;
#[cfg(feature = "lh1")]
mod lhv1;
mod lhv2;
#[cfg(feature = "lz")]
pub use lzs::*;
#[cfg(feature = "lz")]
pub use lz5::*;
#[cfg(feature = "lh1")]
pub use lhv1::*;
pub use lhv2::*;
pub trait Decoder<R> {
type Error: fmt::Debug;
fn into_inner(self) -> R;
fn fill_buffer(&mut self, buf: &mut[u8]) -> Result<(), LhaError<Self::Error>>;
}
#[derive(Debug)]
pub struct LhaDecodeReader<R> {
header: LhaHeader,
crc: Crc16,
output_length: u64,
decoder: Option<DecoderAny<Take<R>>>
}
#[derive(Debug)]
pub struct PassthroughDecoder<R> {
inner: R
}
#[derive(Debug)]
pub struct UnsupportedDecoder<R> {
inner: R
}
pub struct LhaDecodeError<R: Read> {
read: R,
source: LhaError<R::Error>
}
#[non_exhaustive]
#[derive(Debug)]
pub enum DecoderAny<R> {
PassthroughDecoder(PassthroughDecoder<R>),
UnsupportedDecoder(UnsupportedDecoder<R>),
#[cfg(feature = "lz")]
LzsDecoder(LzsDecoder<R>),
#[cfg(feature = "lz")]
Lz5Decoder(Lz5Decoder<R>),
#[cfg(feature = "lh1")]
Lh1Decoder(Lh1Decoder<R>),
Lh4Decoder(Lh5Decoder<R>),
Lh5Decoder(Lh5Decoder<R>),
Lh6Decoder(Lh7Decoder<R>),
Lh7Decoder(Lh7Decoder<R>),
#[cfg(feature = "lhx")]
LhxDecoder(LhxDecoder<R>),
}
macro_rules! decoder_any_dispatch {
(($model:expr)($($spec:tt)*) => $expr:expr) => {
match $model {
DecoderAny::PassthroughDecoder($($spec)*) => $expr,
DecoderAny::UnsupportedDecoder($($spec)*) => $expr,
#[cfg(feature = "lz")]
DecoderAny::LzsDecoder($($spec)*) => $expr,
#[cfg(feature = "lz")]
DecoderAny::Lz5Decoder($($spec)*) => $expr,
#[cfg(feature = "lh1")]
DecoderAny::Lh1Decoder($($spec)*) => $expr,
DecoderAny::Lh4Decoder($($spec)*)|
DecoderAny::Lh5Decoder($($spec)*) => $expr,
DecoderAny::Lh6Decoder($($spec)*)|
DecoderAny::Lh7Decoder($($spec)*) => $expr,
#[cfg(feature = "lhx")]
DecoderAny::LhxDecoder($($spec)*) => $expr,
}
};
}
impl<R: Read> Default for LhaDecodeReader<R> {
fn default() -> Self {
LhaDecodeReader {
header: Default::default(),
crc: Crc16::default(),
output_length: 0,
decoder: None
}
}
}
impl<R: Read> LhaDecodeReader<R> where R::Error: fmt::Debug {
pub fn new(mut rd: R) -> Result<LhaDecodeReader<R>, LhaDecodeError<R>> {
let header = match LhaHeader::read(rd.by_ref()).and_then(|h|
h.ok_or_else(|| LhaError::HeaderParse("a header is missing"))
)
{
Ok(h) => h,
Err(e) => return Err(wrap_err(rd, e))
};
let decoder = DecoderAny::new_from_header(&header, rd);
let crc = Crc16::default();
Ok(LhaDecodeReader {
header,
crc,
output_length: 0,
decoder: Some(decoder)
})
}
pub fn begin_new(&mut self, mut rd: R) -> Result<bool, LhaDecodeError<R>> {
let res = match LhaHeader::read(rd.by_ref()) {
Ok(Some(header)) => {
let decoder = DecoderAny::new_from_header(&header, rd);
self.decoder = Some(decoder);
self.header = header;
true
}
Ok(None) => {
let decoder = UnsupportedDecoder::new(rd.take(0));
self.decoder = Some(DecoderAny::UnsupportedDecoder(decoder));
false
}
Err(e) => return Err(wrap_err(rd, e))
};
self.crc.reset();
self.output_length = 0;
Ok(res)
}
pub fn begin_with_header_and_decoder(&mut self, header: LhaHeader, decoder: DecoderAny<Take<R>>) {
self.decoder = Some(decoder);
self.header = header;
self.crc.reset();
self.output_length = 0;
}
#[cfg(feature = "std")]
pub fn next_file(&mut self) -> Result<bool, LhaDecodeError<R>> {
self.next_file_with_sink::<{8*1024}>()
}
#[cfg(not(feature = "std"))]
pub fn next_file(&mut self) -> Result<bool, LhaDecodeError<R>> {
self.next_file_with_sink::<512>()
}
pub fn next_file_with_sink<const BUF: usize>(&mut self) -> Result<bool, LhaDecodeError<R>> {
let mut limited_rd = self.decoder.take().expect("decoder not empty").into_inner();
if limited_rd.limit() != 0 {
if let Err(e) = discard_to_end::<_, BUF>(&mut limited_rd).map_err(LhaError::Io) {
return Err(wrap_err(limited_rd.into_inner(), e))
}
}
self.begin_new(limited_rd.into_inner())
}
pub fn header(&self) -> &LhaHeader {
&self.header
}
pub fn into_inner(self) -> R {
self.decoder.expect("decoder not empty").into_inner().into_inner()
}
pub fn take_inner(&mut self) -> Option<R> {
self.header.original_size = 0;
self.output_length = 0;
self.crc.reset();
self.decoder.take().map(|decoder| decoder.into_inner().into_inner())
}
pub fn len(&self) -> u64 {
self.header.original_size - self.output_length
}
pub fn is_empty(&self) -> bool {
self.header.original_size == self.output_length
}
pub fn is_present(&self) -> bool {
self.decoder.is_some()
}
pub fn is_absent(&self) -> bool {
self.decoder.is_none()
}
pub fn crc_is_ok(&self) -> bool {
self.crc.sum16() == self.header.file_crc
}
pub fn crc_check(&self) -> LhaResult<u16, R> {
if self.crc_is_ok() {
Ok(self.header.file_crc)
}
else {
Err(LhaError::Checksum("crc16 mismatch"))
}
}
pub fn is_decoder_supported(&self) -> bool {
self.decoder.as_ref().map(|d| d.is_supported()).unwrap_or(false)
}
}
#[cfg(feature = "std")]
impl<R: Read<Error=std::io::Error>> std::io::Read for LhaDecodeReader<R> {
fn read(&mut self, buf: &mut[u8]) -> std::io::Result<usize> {
let len = buf.len().min((self.header.original_size - self.output_length) as usize);
let target = &mut buf[..len];
self.decoder.as_mut().unwrap().fill_buffer(target)?;
self.output_length += len as u64;
self.crc.digest(target);
Ok(len)
}
}
#[cfg(not(feature = "std"))]
impl<R: Read> Read for LhaDecodeReader<R> where R::Error: fmt::Debug {
type Error = LhaError<R::Error>;
fn unexpected_eof() -> Self::Error {
LhaError::Io(R::unexpected_eof())
}
fn read_all(&mut self, buf: &mut[u8]) -> Result<usize, Self::Error> {
let len = buf.len().min((self.header.original_size - self.output_length) as usize);
let target = &mut buf[..len];
self.decoder.as_mut().unwrap().fill_buffer(target)?;
self.output_length += len as u64;
self.crc.digest(target);
Ok(len)
}
}
impl<R: Read> DecoderAny<R> {
pub fn new_from_header(header: &LhaHeader, rd: R) -> DecoderAny<Take<R>> {
let limited_rd = rd.take(header.compressed_size);
match header.compression_method() {
Ok(compression) => DecoderAny::new_from_compression(compression, limited_rd),
Err(..) => DecoderAny::UnsupportedDecoder(UnsupportedDecoder::new(limited_rd))
}
}
pub fn new_from_compression(
compression: CompressionMethod,
rd: R
) -> Self
{
match compression {
CompressionMethod::Pm0|
CompressionMethod::Lz4|
CompressionMethod::Lh0 => DecoderAny::PassthroughDecoder(PassthroughDecoder::new(rd)),
#[cfg(feature = "lz")]
CompressionMethod::Lzs => DecoderAny::LzsDecoder(LzsDecoder::new(rd)),
#[cfg(feature = "lz")]
CompressionMethod::Lz5 => DecoderAny::Lz5Decoder(Lz5Decoder::new(rd)),
#[cfg(feature = "lh1")]
CompressionMethod::Lh1 => DecoderAny::Lh1Decoder(Lh1Decoder::new(rd)),
CompressionMethod::Lh4 => DecoderAny::Lh4Decoder(Lh5Decoder::new(rd)),
CompressionMethod::Lh5 => DecoderAny::Lh5Decoder(Lh5Decoder::new(rd)),
CompressionMethod::Lh6 => DecoderAny::Lh6Decoder(Lh7Decoder::new(rd)),
CompressionMethod::Lh7 => DecoderAny::Lh7Decoder(Lh7Decoder::new(rd)),
#[cfg(feature = "lhx")]
CompressionMethod::Lhx => DecoderAny::LhxDecoder(LhxDecoder::new(rd)),
_ => DecoderAny::UnsupportedDecoder(UnsupportedDecoder::new(rd))
}
}
pub fn is_supported(&self) -> bool {
!matches!(self, DecoderAny::UnsupportedDecoder(..))
}
}
impl<R: Read> Decoder<R> for DecoderAny<R> where R::Error: fmt::Debug {
type Error = R::Error;
fn into_inner(self) -> R {
decoder_any_dispatch!((self)(decoder) => decoder.into_inner())
}
#[inline]
fn fill_buffer(&mut self, buf: &mut[u8]) -> Result<(), LhaError<Self::Error>> {
decoder_any_dispatch!((self)(decoder) => decoder.fill_buffer(buf))
}
}
impl<R: Read> PassthroughDecoder<R> {
pub fn new(inner: R) -> Self {
PassthroughDecoder { inner }
}
}
impl<R: Read> Decoder<R> for PassthroughDecoder<R> where R::Error: fmt::Debug {
type Error = R::Error;
fn into_inner(self) -> R {
self.inner
}
#[inline]
fn fill_buffer(&mut self, buf: &mut[u8]) -> Result<(), LhaError<Self::Error>> {
self.inner.read_exact(buf).map_err(LhaError::Io)
}
}
impl<R: Read> UnsupportedDecoder<R> {
pub fn new(inner: R) -> Self {
UnsupportedDecoder { inner }
}
}
impl<R: Read> Decoder<R> for UnsupportedDecoder<R> where R::Error: fmt::Debug {
type Error = R::Error;
fn into_inner(self) -> R {
self.inner
}
#[inline]
fn fill_buffer(&mut self, _buf: &mut[u8]) -> Result<(), LhaError<Self::Error>> {
Err(LhaError::Decompress("unsupported compression method"))
}
}
impl<R: Read> LhaDecodeError<R> {
pub fn get_ref(&self) -> &R {
&self.read
}
pub fn get_mut(&mut self) -> &mut R {
&mut self.read
}
pub fn into_inner(self) -> R {
self.read
}
}
#[cfg(feature = "std")]
impl<R: Read> std::error::Error for LhaDecodeError<R>
where LhaError<R::Error>: std::error::Error + 'static
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
impl<R: Read> fmt::Debug for LhaDecodeError<R>
where LhaError<R::Error>: fmt::Debug
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LhaDecodeError")
.field("source", &self.source)
.finish()
}
}
impl<R: Read> fmt::Display for LhaDecodeError<R>
where LhaError<R::Error>: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "LHA decode error: {}", self.source)
}
}
impl<R: Read> From<LhaDecodeError<R>> for LhaError<R::Error> {
fn from(e: LhaDecodeError<R>) -> Self {
e.source
}
}
#[cfg(feature = "std")]
impl<R: Read> From<LhaDecodeError<R>> for std::io::Error
where std::io::Error: From<LhaError<R::Error>>
{
fn from(e: LhaDecodeError<R>) -> Self {
e.source.into()
}
}
fn wrap_err<R: Read>(read: R, source: LhaError<R::Error>) -> LhaDecodeError<R> {
LhaDecodeError { read, source }
}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use std::io;
use super::*;
#[test]
fn decode_error_works() {
let rd = io::Cursor::new(vec![0u8;3]);
let mut err = LhaDecodeReader::new(rd).unwrap_err();
assert_eq!(err.to_string(), "LHA decode error: while parsing LHA header: a header is missing");
assert_eq!(err.get_ref().get_ref(), &vec![0u8;3]);
assert_eq!(err.get_mut().get_mut(), &mut vec![0u8;3]);
let rd = err.into_inner();
assert_eq!(rd.position(), 1);
assert_eq!(rd.into_inner(), vec![0u8;3]);
}
}