use crate::driver::FileDriver; use crate::utils::{IoCursor, ReadUtils}; use crate::zip::{CompressionMethod, ZipError, ZipFileInfo, ZipResult}; use bzip2::read::BzDecoder; use flate2::read::DeflateDecoder; use liblzma::read::XzDecoder; use liblzma::stream::{Filters, LzmaOptions, Stream}; use std::io::{ BufReader, Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom, }; use zstd::stream::Decoder as ZstdDecoder; enum Compression { Store(Io), Deflate(DeflateDecoder), BZip2(BzDecoder), Zstd(ZstdDecoder<'static, BufReader>), Xz(XzDecoder), } pub struct ZipFileReader<'d, Io: Read> { io: Compression>, info: &'d ZipFileInfo, } impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> { type Io = Io; type FileInfo = ZipFileInfo; fn info(&self) -> &Self::FileInfo { self.info } } impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { pub fn new(io: &'d mut Io, info: &'d ZipFileInfo) -> ZipResult { io.seek(SeekFrom::Start(info.header_pointer))?; let buf = io.read_arr::<30>()?; if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x04034b50 { return Err(ZipError::InvalidFileHeaderSignature.into()); } let data_pointer = info.header_pointer + 30 + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; Ok(Self { io: match info.compression_method { CompressionMethod::Store => Compression::Store(IoCursor::new( io, data_pointer, data_pointer + info.compressed_size, )?), CompressionMethod::Deflate => Compression::Deflate(DeflateDecoder::new( IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?, )), CompressionMethod::BZip2 => Compression::BZip2(BzDecoder::new(IoCursor::new( io, data_pointer, data_pointer + info.compressed_size, )?)), CompressionMethod::Lzma => { io.seek(SeekFrom::Start(data_pointer))?; let buf = io.read_arr::<9>()?; Compression::Xz(XzDecoder::new_stream( IoCursor::new(io, data_pointer + 9, data_pointer + info.compressed_size)?, Stream::new_raw_decoder( Filters::new().lzma1( LzmaOptions::new() .literal_context_bits((buf[4] % 9) as u32) .literal_position_bits((buf[4] / 9 % 5) as u32) .position_bits((buf[4] / 45) as u32) .dict_size( u32::from_le_bytes(buf[5..9].try_into().unwrap()).max(4096), ), ), ) .unwrap(), )) } CompressionMethod::Zstd => Compression::Zstd( ZstdDecoder::new(IoCursor::new( io, data_pointer, data_pointer + info.compressed_size, )?) .unwrap(), ), CompressionMethod::Xz => Compression::Xz(XzDecoder::new(IoCursor::new( io, data_pointer, data_pointer + info.compressed_size, )?)), CompressionMethod::Unsupported => { return Err(ZipError::UnsupportedCompressionMethod.into()) } }, info, }) } pub fn seekable(&self) -> bool { match self.io { Compression::Store(..) => true, _ => false, } } } impl<'d, Io: Read> Read for ZipFileReader<'d, Io> { fn read(&mut self, buf: &mut [u8]) -> IoResult { match &mut self.io { Compression::Store(io) => io.read(buf), Compression::Deflate(io) => io.read(buf), Compression::BZip2(io) => io.read(buf), Compression::Zstd(io) => io.read(buf), Compression::Xz(io) => io.read(buf), } } } impl<'d, Io: Read + Seek> Seek for ZipFileReader<'d, Io> { fn seek(&mut self, pos: SeekFrom) -> IoResult { match &mut self.io { Compression::Store(io) => io.seek(pos), _ => Err(IoError::new( IoErrorKind::Unsupported, ZipError::CompressedDataIsUnseekable, )), } } }