psarc2 0.1.1

PlayStation archive reader
Documentation
//! Compression logic.

#[cfg(feature = "zlib")]
mod zlib;

use std::{
    fmt::Display,
    io::{Read, Seek, SeekFrom, Write},
};

use crate::{
    error::{Error, Result},
    file::BasicFileEntry,
    read::BlockSize,
};

/// How the archive is compressed.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CompressionType {
    /// No compression is applied.
    None,
    /// Zlib compression, requires the `zlib` feature flag to parse.
    Zlib,
    /// LZMA compression, requires the `lzma` feature flag to parse.
    Lzma,
}

impl CompressionType {
    /// Decompress a byte buffer based on the implementation.
    pub(crate) fn decompress<R, W, F>(
        self,
        reader: &mut R,
        writer: &mut W,
        entry: &F,
        block_size: BlockSize,
        block_sizes: &[u32],
    ) -> Result<()>
    where
        R: Read + Seek,
        W: Write,
        F: BasicFileEntry,
    {
        // Find the bytes
        reader.seek(SeekFrom::Start(
            u64::try_from(entry.offset()).map_err(|_| Error::AddressTooSmall)?,
        ))?;

        // Extract them, based on the compression
        match self {
            Self::None => {
                // Read exact amount of bytes
                let mut bytes = reader
                    .take(u64::try_from(entry.output_size()).map_err(|_| Error::AddressTooSmall)?);

                // Write to writer
                std::io::copy(&mut bytes, writer)?;

                Ok(())
            }
            // Only parse the compression if we support it directly
            #[cfg(feature = "zlib")]
            Self::Zlib => zlib::decompress(reader, writer, entry, block_size, block_sizes),
            // Only parse the compression if we support it directly
            #[cfg(feature = "lzma")]
            Self::Lzma => todo!(),
            #[cfg(not(all(feature = "lzma", feature = "zlib")))]
            unsupported => Err(Error::UnsupportedCompression(unsupported.to_string())),
        }
    }
}

impl Display for CompressionType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            Self::None => "none",
            Self::Zlib => "zlib",
            Self::Lzma => "lzma",
        })
    }
}

impl TryFrom<u32> for CompressionType {
    type Error = Error;

    fn try_from(value: u32) -> Result<Self> {
        match &value.to_be_bytes() {
            [0, 0, 0, 0] => Ok(Self::None),
            b"zlib" => Ok(Self::Zlib),
            b"lzma" => Ok(Self::Lzma),
            unsupported => Err(Error::UnsupportedCompression(
                String::from_utf8_lossy(unsupported).into_owned(),
            )),
        }
    }
}