mpq-rs 0.1.0

A pure-rust implementation of a MoPaQ archive reader and writer
Documentation
use std::io::Error as IoError;
use std::io::{Read, Write};

use byteorder::{ReadBytesExt, WriteBytesExt, LE};

use crate::core::consts::*;
use crate::core::error::MpqError;

#[derive(Debug)]
pub(crate) struct FileHeader {
    pub header_size: u32,
    pub archive_size: u32,
    pub format_version: u16,
    pub block_size: u16,
    pub hash_table_offset: u32,
    pub block_table_offset: u32,
    pub hash_table_entries: u32,
    pub block_table_entries: u32,
}

impl FileHeader {
    pub fn new_v1(
        archive_size: u32,
        block_size: u32,
        hash_table_offset: u32,
        block_table_offset: u32,
        hash_table_entries: u32,
        block_table_entries: u32,
    ) -> FileHeader {
        let mut block_size = block_size / 512;
        let mut pow = 1;
        while block_size > 2 {
            block_size /= 2;
            pow += 1;
        }

        FileHeader {
            format_version: 0,
            header_size: HEADER_MPQ_SIZE as u32,
            archive_size,
            block_size: pow,
            hash_table_offset,
            hash_table_entries,
            block_table_offset,
            block_table_entries,
        }
    }

    pub fn from_reader<R: Read>(mut reader: R) -> Result<FileHeader, MpqError> {
        let header_size = reader.read_u32::<LE>()?;
        let archive_size = reader.read_u32::<LE>()?;
        let format_version = reader.read_u16::<LE>()?;
        let block_size = reader.read_u16::<LE>()?;
        let hash_table_offset = reader.read_u32::<LE>()?;
        let block_table_offset = reader.read_u32::<LE>()?;
        let hash_table_entries = reader.read_u32::<LE>()?;
        let block_table_entries = reader.read_u32::<LE>()?;

        if format_version != 0 {
            return Err(MpqError::UnsupportedVersion);
        }

        Ok(FileHeader {
            header_size,
            archive_size,
            format_version,
            block_size,
            hash_table_offset,
            block_table_offset,
            hash_table_entries,
            block_table_entries,
        })
    }

    pub fn write<W: Write>(&self, mut writer: W) -> Result<(), IoError> {
        writer.write_u32::<LE>(HEADER_MPQ_MAGIC)?;
        writer.write_u32::<LE>(self.header_size)?;
        writer.write_u32::<LE>(self.archive_size)?;
        writer.write_u16::<LE>(self.format_version)?;
        writer.write_u16::<LE>(self.block_size)?;
        writer.write_u32::<LE>(self.hash_table_offset)?;
        writer.write_u32::<LE>(self.block_table_offset)?;
        writer.write_u32::<LE>(self.hash_table_entries)?;
        writer.write_u32::<LE>(self.block_table_entries)?;

        Ok(())
    }
}

#[derive(Debug)]
pub struct UserHeader {
    pub(crate) user_data_size: u32,
    pub(crate) file_header_offset: u32,
}

impl UserHeader {
    pub fn new<R: Read>(mut reader: R) -> Result<UserHeader, MpqError> {
        let user_data_size = reader.read_u32::<LE>()?;
        let file_header_offset = reader.read_u32::<LE>()?;

        Ok(UserHeader {
            user_data_size,
            file_header_offset,
        })
    }
}