psarc2 0.1.0

PlayStation archive reader
Documentation
//! Z-lib decompression.

use std::io::{Read, Seek, Write};

use flate2::read::ZlibDecoder;

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

/// Decompress a zlib-encoded file.
pub(crate) fn decompress<R, W, F>(
    reader: &mut R,
    writer: &mut W,
    entry: &F,
    block_size: BlockSize,
    block_sizes: &[u32],
) -> Result<()>
where
    R: Read + Seek,
    W: Write,
    F: BasicFileEntry,
{
    // Get the block size number
    let block_size = usize::try_from(block_size.as_u32()).map_err(|_| Error::AddressTooSmall)?;

    // Calculate how much blocks must be parsed
    let total_blocks = entry.output_size().div_ceil(block_size);

    // Extract all blocks
    let block_start = entry.index_list_size() as usize;
    for block_length in &block_sizes[block_start..block_start + total_blocks] {
        // Decrypt the blocks
        // Try to find the magic bytes denoting the block as zlib compressed
        let zlib_magic = read_u16(reader)
            // This value (0) will always trigger the rest of the bytes to be copied
            .unwrap_or_default();
        // Un-read bytes
        reader.seek_relative(-2)?;

        // Take the slice for this block
        let mut slice = reader.by_ref().take(u64::from(*block_length));

        // Decode if compressed
        if zlib_magic == 0x78DA || zlib_magic == 0x7801 {
            // Zlib detected, decode
            let mut decoder = ZlibDecoder::new(slice);
            std::io::copy(&mut decoder, writer)
                .map_err(|_| Error::Corrupt("could not copy decoded bytes to output"))?;
        } else {
            // No zlib, just copy the bytes
            std::io::copy(&mut slice, writer)
                .map_err(|_| Error::Corrupt("could not copy bytes to output"))?;
        }
    }

    Ok(())
}