iroga 0.4.0

Command line app to pack a single directory into an IRO archive (FF7 mod manager format)
Documentation
use std::{
    io::{BufReader, Read, Seek, Write},
    result::Result,
};

use crate::Error;
use crate::compression;
use crate::iro_entry::FileFlags;
pub use crate::iro_entry::IroEntry;
pub use crate::iro_header::IroHeader;
use crate::iro_parser::{parse_iro_entry_v2, parse_iro_header_v2};

pub struct IroArchive<RW> {
    stream: RW,
}

impl<R: Read + Seek> IroArchive<R> {
    pub fn open(stream: R) -> Self {
        IroArchive { stream }
    }

    pub fn read_header(&mut self) -> Result<IroHeader, Error> {
        let mut iro_header_bytes = [0u8; 20];
        self.stream.read_exact(&mut iro_header_bytes)?;
        let (_, iro_header) = parse_iro_header_v2(&iro_header_bytes)?;
        Ok(iro_header)
    }

    pub fn read_iro_entries(&mut self, iro_header: &IroHeader) -> Result<Vec<IroEntry>, Error> {
        let mut iro_entries: Vec<IroEntry> = Vec::new();
        for _ in 0..iro_header.num_files {
            let mut entry_len_bytes = [0u8; 2];
            self.stream.read_exact(&mut entry_len_bytes)?;
            let entry_len = u16::from_le_bytes(entry_len_bytes);

            let mut entry_bytes = vec![0u8; entry_len as usize - 2];
            self.stream.read_exact(entry_bytes.as_mut())?;

            let (_, iro_entry) = parse_iro_entry_v2(iro_header, &entry_bytes)?;
            iro_entries.push(iro_entry);
        }
        Ok(iro_entries)
    }

    pub fn seek_and_read_file_entry<W: Write>(
        &mut self,
        iro_entry: &IroEntry,
        writer: &mut W
    ) -> Result<(), Error> {
        let mut buf_reader = BufReader::new(&mut self.stream);
        buf_reader.seek(std::io::SeekFrom::Start(iro_entry.offset))?;
        let mut entry_buffer = buf_reader.take(iro_entry.data_len as u64);
        match iro_entry.flags {
            FileFlags::LzssCompressed => compression::lzss_decompress(&mut entry_buffer, writer)?,
            FileFlags::LzmaCompressed => compression::lzma_decompress(&mut entry_buffer, writer)?,
            _ => {
                std::io::copy(&mut entry_buffer, writer)?;
            }
        };
        Ok(())
    }
}