mpq 0.8.1

A library for reading MPQ archives
Documentation
use bzip2_rs as bzip2;
use implode::exploder::Exploder;
use implode::symbol::DEFAULT_CODE_TABLE;
use std::io::{self, Error, ErrorKind};

const COMPRESSION_HUFFMAN: u8 = 0x01;
const COMPRESSION_ZLIB: u8 = 0x02;
const COMPRESSION_PKWARE: u8 = 0x08;
const COMPRESSION_BZIP2: u8 = 0x10;
const COMPRESSION_SPARSE: u8 = 0x20;
const COMPRESSION_ADPCM_MONO: u8 = 0x40;
const COMPRESSION_ADPCM_STEREO: u8 = 0x80;
const COMPRESSION_LZMA: u8 = 0x12;

pub fn decompress(data: &mut [u8], out: &mut [u8]) -> Result<usize, Error> {
    let compression_type = data[0];

    if compression_type & COMPRESSION_BZIP2 != 0 {
        let mut ouput = io::Cursor::new(out);
        let mut reader = bzip2::DecoderReader::new(&data[1..]);
        io::copy(&mut reader, &mut ouput)?;
        return Ok(ouput.position() as usize);
    }

    if compression_type & COMPRESSION_ZLIB != 0 {
        let mut zlib = flate2::Decompress::new(true);

        match zlib.decompress(&data[1..], out, flate2::FlushDecompress::None) {
            Ok(_) => {}
            Err(e) => return Err(Error::new(ErrorKind::Other, e)),
        }

        return Ok(zlib.total_out() as usize);
    }

    if compression_type & COMPRESSION_PKWARE != 0 {
        let mut exploder = Exploder::new(&DEFAULT_CODE_TABLE);

        let mut cpos: u32 = 1;
        let len = data.len();
        let mut c = 0;

        while !exploder.ended {
            let abuf = &mut data[cpos as usize..len];

            let x = exploder.explode_block(abuf).unwrap();

            cpos += x.0 as u32;

            let bf = x.1;

            for (d, s) in out.iter_mut().zip(bf.iter()) {
                *d = *s;
                c += 1;
            }
        }

        return Ok(c);
    }

    if compression_type & COMPRESSION_HUFFMAN != 0 {
        return Err(Error::new(
            ErrorKind::Other,
            "Compression algorithm Huffman not supported",
        ));
    }

    if compression_type & COMPRESSION_SPARSE != 0 {
        return Err(Error::new(
            ErrorKind::Other,
            "Compression algorithm Sparse not supported",
        ));
    }

    if compression_type & COMPRESSION_ADPCM_STEREO != 0 {
        return Err(Error::new(
            ErrorKind::Other,
            "Compression algorithm ADPCM Stereo not supported",
        ));
    }

    if compression_type & COMPRESSION_ADPCM_MONO != 0 {
        return Err(Error::new(
            ErrorKind::Other,
            "Compression algorithm ADPCM Stereo not supported",
        ));
    }

    if compression_type & COMPRESSION_LZMA != 0 {
        return Err(Error::new(
            ErrorKind::Other,
            "Compression algorithm LZMA not supported",
        ));
    }

    Err(Error::new(ErrorKind::Other, "No compression type found"))
}

pub fn explode(data: &mut [u8], out: &mut [u8]) -> Result<usize, Error> {
    let mut exploder = Exploder::new(&DEFAULT_CODE_TABLE);

    let mut cpos: u32 = 0;
    let len = data.len();
    let mut c = 0;

    while !exploder.ended {
        let abuf = &mut data[cpos as usize..len];

        let x = exploder.explode_block(abuf).unwrap();

        cpos += x.0 as u32;

        let bf = x.1;

        for (d, s) in out.iter_mut().zip(bf.iter()) {
            *d = *s;
            c += 1;
        }
    }

    Ok(c)
}