ignis 0.1.0

A library for editing various 3DSFE formats.
Documentation
use crate::formats::archive;
use crate::formats::archive::BinArchive;
use crate::compression::{CompressionProvider, LZ13CompressionProvider};
use crate::filesystem::FilesystemLayer;
use crate::filesystem::{FE14PathLocalizer, PathLocalizer};
use crate::model::Language;
use anyhow::{anyhow, Result};

pub struct LayeredFilesystem {
    compression_provider: Box<dyn CompressionProvider>,
    path_localizer: Box<dyn PathLocalizer>,
    layers: Vec<Box<dyn FilesystemLayer>>,
    language: Language,
}

impl LayeredFilesystem {
    pub fn create_fe14_filesystem(
        layers: Vec<Box<dyn FilesystemLayer>>,
        language: Language,
    ) -> Self {
        LayeredFilesystem {
            compression_provider: Box::new(LZ13CompressionProvider {}),
            path_localizer: Box::new(FE14PathLocalizer {}),
            layers,
            language,
        }
    }

    pub fn localize_path(&self, filename: &str) -> Result<String> {
        self.path_localizer.localize_path(filename, &self.language)
    }

    pub fn is_compressed_file_path(&self, filename: &str) -> bool {
        self.compression_provider.is_compressed_filename(filename)
    }

    pub fn open(&self, filename: &str) -> Result<Vec<u8>> {
        for layer in self.layers.iter().rev() {
            if layer.exists(filename) {
                let contents = layer.open(filename)?;
                if self.is_compressed_file_path(filename) {
                    let decompressed_contents = self.compression_provider.decompress(&contents)?;
                    return Ok(decompressed_contents);
                } else {
                    return Ok(contents);
                }
            }
        }
        Err(anyhow!("File {} does not exist in any layer", filename))
    }

    pub fn save(&self, filename: &str, bytes: &[u8]) -> Result<()> {
        let layer = self
            .layers
            .first()
            .ok_or(anyhow!("LayeredFilesystem has no layers"))?;
        if filename.ends_with(".lz") {
            let compressed_contents = self.compression_provider.compress(bytes)?;
            layer.save(filename, &compressed_contents)
        } else {
            layer.save(filename, bytes)
        }
    }

    pub fn open_archive(&self, filename: &str) -> Result<BinArchive> {
        let bytes = self.open(&filename)?;
        archive::from_bytes(&bytes)
    }

    pub fn save_archive(&self, filename: &str, archive: &mut BinArchive) -> Result<()> {
        let raw_archive = archive.serialize()?;
        self.save(filename, &raw_archive)
    }

    pub fn open_archive_localized(&self, filename: &str) -> Result<BinArchive> {
        let localized_path = self.localize_path(filename)?;
        self.open_archive(&localized_path)
    }

    pub fn save_archive_localized(&self, filename: &str, archive: &mut BinArchive) -> Result<()> {
        let localized_path = self.localize_path(filename)?;
        self.save_archive(&localized_path, archive)
    }
}