twmap 0.10.0

Parse, edit and save Teeworlds and DDNet maps
Documentation
use crate::compression::decompress;
use crate::convert::{To, TryTo};
use crate::map::*;

use image::RgbaImage;
use ndarray::Array2;

use crate::map::checks::{
    CheckData, InternalMapChecking, LayerError, MapErr, MapErrorKind, MapItem,
};
use std::iter;

pub(crate) trait InternalLoad: CheckData {
    fn internal_load_unchecked(&mut self) -> Result<(), MapError>;

    fn internal_load(&mut self) -> Result<(), MapError> {
        self.internal_load_unchecked()?;
        self.check_data().map_err(MapErr::from)?;
        Ok(())
    }
}

impl InternalLoad for CompressedData<Vec<u8>, ()> {
    fn internal_load_unchecked(&mut self) -> Result<(), MapError> {
        self.check_data().map_err(MapErr::from)?;
        if let CompressedData::Compressed(compressed_data, size, _) = self {
            let decompressed_data = decompress(compressed_data, *size)
                .map_err(|err| MapErr::from(MapErrorKind::from(err)))?;
            *self = CompressedData::Loaded(decompressed_data);
        }
        Ok(())
    }
}

impl InternalLoad for CompressedData<RgbaImage, ImageLoadInfo> {
    fn internal_load_unchecked(&mut self) -> Result<(), MapError> {
        self.check_data().map_err(MapErr::from)?;
        if let CompressedData::Compressed(compressed_data, size, info) = self {
            let decompressed_data = decompress(compressed_data, *size)
                .map_err(|err| MapErr::from(MapErrorKind::from(err)))?;
            let loaded_data = <RgbaImage>::from_raw(
                info.size.w.try_to(),
                info.size.h.try_to(),
                decompressed_data,
            )
            .unwrap();
            *self = CompressedData::Loaded(loaded_data);
        }
        Ok(())
    }
}

impl<T: AnyTile> InternalLoad for CompressedData<Array2<T>, TilesLoadInfo>
where
    Self: CheckData,
{
    fn internal_load_unchecked(&mut self) -> Result<(), MapError> {
        if let CompressedData::Compressed(_, _, _) = self {
            self.check_data().map_err(MapErr::from)?;
        }
        if let CompressedData::Compressed(compressed_data, size, info) = self {
            let dims = info.size.unwrapped_as::<usize>();

            let mut decompressed_data = decompress(compressed_data, *size)
                .map_err(|err| MapErr::from(MapErrorKind::from(err)))?;
            if info.compression {
                assert_eq!(decompressed_data.len() % 4, 0);
                let data: Vec<u8> = decompressed_data
                    .chunks(4)
                    .flat_map(|tile| {
                        iter::repeat(tile)
                            .take(tile[2].to::<usize>() + 1)
                            .flat_map(|tile| [tile[0], tile[1], 0, tile[3]].to_vec())
                    })
                    .collect();
                if data.len() != dims.w * dims.h * 4 {
                    return Err(
                        MapErr::from(MapErrorKind::from(LayerError::TeeworldsCompression))
                            .with_type(Layer::TYPE)
                            .into(),
                    );
                }
                decompressed_data = data;
            }
            let tiles = T::view_boxed_slice(decompressed_data.into_boxed_slice())
                .unwrap()
                .to_vec();
            let tiles = Array2::from_shape_vec((dims.h, dims.w), tiles).unwrap();
            *self = CompressedData::Loaded(tiles);
        }
        Ok(())
    }
}

pub trait Load {
    fn load_unchecked(&mut self) -> Result<(), MapError>;

    fn load(&mut self) -> Result<(), MapError>;
}

impl<T: InternalLoad> Load for T {
    fn load_unchecked(&mut self) -> Result<(), MapError> {
        self.internal_load_unchecked()
    }

    fn load(&mut self) -> Result<(), MapError> {
        self.internal_load()
    }
}

impl Load for Image {
    fn load_unchecked(&mut self) -> Result<(), MapError> {
        if let Some(img) = self.image_mut() {
            img.load_unchecked()
                .map_err(|err| MapError(err.0.with_type(MapItem::Image)))
        } else {
            Ok(())
        }
    }

    fn load(&mut self) -> Result<(), MapError> {
        if let Some(img) = self.image_mut() {
            img.load()
                .map_err(|err| MapError(err.0.with_type(MapItem::Image)))
        } else {
            Ok(())
        }
    }
}

impl Load for Sound {
    fn load_unchecked(&mut self) -> Result<(), MapError> {
        self.data
            .load_unchecked()
            .map_err(|err| MapError(err.0.with_type(MapItem::Sound)))
    }

    fn load(&mut self) -> Result<(), MapError> {
        self.data
            .load()
            .map_err(|err| MapError(err.0.with_type(MapItem::Sound)))
    }
}

impl Load for Layer {
    fn load_unchecked(&mut self) -> Result<(), MapError> {
        match self {
            Layer::Game(l) => l.tiles_mut().load_unchecked(),
            Layer::Tiles(l) => l.tiles_mut().load_unchecked(),
            Layer::Front(l) => l.tiles_mut().load_unchecked(),
            Layer::Tele(l) => l.tiles_mut().load_unchecked(),
            Layer::Speedup(l) => l.tiles_mut().load_unchecked(),
            Layer::Switch(l) => l.tiles_mut().load_unchecked(),
            Layer::Tune(l) => l.tiles_mut().load_unchecked(),
            Layer::Sounds(_) | Layer::Quads(_) | Layer::Invalid(_) => Ok(()),
        }
    }

    fn load(&mut self) -> Result<(), MapError> {
        match self {
            Layer::Game(l) => l.tiles_mut().load(),
            Layer::Tiles(l) => l.tiles_mut().load(),
            Layer::Front(l) => l.tiles_mut().load(),
            Layer::Tele(l) => l.tiles_mut().load(),
            Layer::Speedup(l) => l.tiles_mut().load(),
            Layer::Switch(l) => l.tiles_mut().load(),
            Layer::Tune(l) => l.tiles_mut().load(),
            Layer::Sounds(_) | Layer::Quads(_) | Layer::Invalid(_) => Ok(()),
        }
        .map_err(|err| MapError(err.0.with_type(MapItem::Layer)))
    }
}

pub trait LoadMultiple {
    type Item;

    fn load_unchecked(&mut self) -> Result<(), MapError>;

    fn load_conditionally(
        &mut self,
        condition: impl Fn(&Self::Item) -> bool + Copy,
    ) -> Result<(), MapError>;

    fn load(&mut self) -> Result<(), MapError> {
        self.load_conditionally(|_| true)
    }
}

impl<T: Load> LoadMultiple for [T] {
    type Item = T;

    fn load_unchecked(&mut self) -> Result<(), MapError> {
        for (i, item) in self.iter_mut().enumerate() {
            item.load_unchecked()
                .map_err(|err| MapError(err.0.with_index(i)))?;
        }
        Ok(())
    }

    fn load_conditionally(
        &mut self,
        condition: impl Fn(&Self::Item) -> bool + Copy,
    ) -> Result<(), MapError> {
        for (i, item) in self.iter_mut().enumerate() {
            if !condition(item) {
                continue;
            }
            item.load().map_err(|err| MapError(err.0.with_index(i)))?;
        }
        Ok(())
    }
}

impl LoadMultiple for Group {
    type Item = Layer;

    fn load_unchecked(&mut self) -> Result<(), MapError> {
        self.layers
            .load_unchecked()
            .map_err(|err| MapError(err.0.with_type(MapItem::Group)))
    }

    fn load_conditionally(
        &mut self,
        condition: impl Fn(&Self::Item) -> bool + Copy,
    ) -> Result<(), MapError> {
        self.layers
            .load_conditionally(condition)
            .map_err(|err| MapError(err.0.with_type(MapItem::Group)))
    }
}

impl LoadMultiple for [Group] {
    type Item = Layer;

    fn load_unchecked(&mut self) -> Result<(), MapError> {
        for (i, item) in self.iter_mut().enumerate() {
            item.load_unchecked()
                .map_err(|err| MapError(err.0.with_index(i)))?;
        }
        Ok(())
    }

    fn load_conditionally(
        &mut self,
        condition: impl Fn(&Self::Item) -> bool + Copy,
    ) -> Result<(), MapError> {
        for (i, item) in self.iter_mut().enumerate() {
            item.load_conditionally(condition)
                .map_err(|err| MapError(err.0.with_index(i)))?;
        }
        Ok(())
    }
}

impl TwMap {
    pub fn load(&mut self) -> Result<(), MapError> {
        self.images.load()?;
        self.groups.load()?;
        self.sounds.load()?;
        Ok(())
    }

    pub fn load_unchecked(&mut self) -> Result<(), MapError> {
        self.images.load_unchecked()?;
        self.groups.load_unchecked()?;
        self.sounds.load_unchecked()?;
        Ok(())
    }
}