imf 0.3.3

Utilities for processing .imf files
Documentation
use bytebuilder::{builder::ByteBuilder, reader::ByteReader, traits::Byteable};

pub type TileType = i16;
pub type DimensionType = u32;

#[derive(Clone, PartialEq, Eq)]
pub struct IMF {
    pub width: DimensionType,
    pub height: DimensionType,
    pub layers: Vec<Vec<Tile>>,
}
impl std::fmt::Debug for IMF {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "IMF {{")?;
        writeln!(f, "  width: {},", self.width)?;
        writeln!(f, "  height: {},", self.height)?;
        writeln!(f, "  map: [")?;
        for (i, map) in self.layers.iter().enumerate().rev() {
            writeln!(f, "    layer {i}")?;
            for chunk in map.chunks(self.width as usize) {
                writeln!(f, "      {:?},", chunk)?;
            }
        }
        writeln!(f, "  ]")?;
        writeln!(f, "}}")
    }
}
impl IMF {
    pub fn new(width: DimensionType, height: DimensionType, fill: Tile) -> IMF {
        IMF {
            width,
            height,
            layers: vec![vec![fill; (width * height) as usize]],
        }
    }
    pub fn new_with_layers(
        width: DimensionType,
        height: DimensionType,
        fill: Vec<Tile>,
    ) -> Result<IMF, ()> {
        Ok(IMF {
            width,
            height,
            layers: fill
                .iter()
                .map(|f| vec![f.clone(); (width * height) as usize])
                .collect(),
        })
    }

    pub(crate) fn ser_v3(&self) -> Vec<u8> {
        let mut bb = ByteBuilder::new();
        bb.push_u8(3);
        bb.push_u32(self.width);
        bb.push_u32(self.height);
        bb.push_u32(self.layers.len() as u32);
        for map in &self.layers {
            for tile in map {
                match tile {
                    Tile::Int(t) => {
                        bb.push_u8(0);
                        bb.push_i16(*t)
                    }
                    Tile::Sides(sides) => {
                        bb.push_u8(1);
                        bb.push_i16(sides.n);
                        bb.push_i16(sides.e);
                        bb.push_i16(sides.s);
                        bb.push_i16(sides.w);
                    }
                }
            }
        }
        bb.bytes
    }

    pub(crate) fn deser_v3(br: &mut ByteReader) -> Option<Self> {
        let width = br.read_u32()?;
        let height = br.read_u32()?;
        let layer_count = br.read_u32()?;
        let mut layers = Vec::new();
        for _ in 0..layer_count {
            let mut layer = Vec::new();
            for _ in 0..(width * height) {
                let tile_type = br.read_u8()?;
                match tile_type {
                    0 => {
                        let t = br.read_i16()?;
                        layer.push(Tile::Int(t));
                    }
                    1 => {
                        let n = br.read_i16()?;
                        let e = br.read_i16()?;
                        let s = br.read_i16()?;
                        let w = br.read_i16()?;
                        layer.push(Tile::Sides(Sides { n, e, s, w }));
                    }
                    _ => return None,
                }
            }
            layers.push(layer);
        }
        Some(IMF {
            width,
            height,
            layers,
        })
    }

    pub fn get(&self, x: DimensionType, y: DimensionType, layer: usize) -> Option<&Tile> {
        if x >= self.width || y >= self.height {
            return None;
        }
        let layer = self.layers.get(layer)?;
        let index = y as usize * self.width as usize + x as usize;
        layer.get(index)
    }
    pub fn set(
        &mut self,
        x: DimensionType,
        y: DimensionType,
        layer: usize,
        tile: Tile,
    ) -> Option<()> {
        if x >= self.width || y >= self.height {
            return None;
        }
        let layer = self.layers.get_mut(layer)?;
        let index = y as usize * self.width as usize + x as usize;
        if index < layer.len() {
            layer[index] = tile;
            Some(())
        } else {
            None
        }
    }
    pub fn get_layer(&self, layer: usize) -> Option<&[Tile]> {
        self.layers.get(layer).map(|l| l.as_slice())
    }
    pub fn get_layer_mut(&mut self, layer: usize) -> Option<&mut [Tile]> {
        self.layers.get_mut(layer).map(|l| l.as_mut_slice())
    }
}

impl Byteable for IMF {
    fn to_bytes(&self) -> Vec<u8> {
        self.ser_v3()
    }

    fn from_bytes(bytes: &[u8]) -> Option<Self> {
        let mut br = ByteReader::new(bytes);
        let version = br.read_u8()?;
        match version {
            3 => IMF::deser_v3(&mut br),
            _ => None,
        }
    }
}

impl Default for IMF {
    fn default() -> Self {
        IMF::new(8, 8, Tile::Int(0))
    }
}
#[derive(Clone, PartialEq, Eq)]
pub enum Tile {
    Int(TileType),
    Sides(Sides),
}
impl std::fmt::Debug for Tile {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Tile::Int(t) => write!(f, "i{}", t),
            Tile::Sides(sides) => write!(f, "s[{:?}]", sides),
        }
    }
}

impl Tile {
    pub fn is_int(&self) -> bool {
        matches!(self, Tile::Int(_))
    }
    pub fn is_sides(&self) -> bool {
        matches!(self, Tile::Sides(_))
    }
    pub fn force_int(&self) -> TileType {
        match self {
            Tile::Int(t) => *t,
            Tile::Sides(Sides {
                n,
                e: _,
                s: _,
                w: _,
            }) => *n,
        }
    }
    pub fn force_sides(&self) -> Sides {
        match self {
            Tile::Int(t) => Sides {
                n: *t,
                e: *t,
                s: *t,
                w: *t,
            },
            Tile::Sides(sides) => sides.clone(),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Sides {
    pub n: TileType,
    pub e: TileType,
    pub s: TileType,
    pub w: TileType,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_imf() {
        let mut imf =
            IMF::new_with_layers(3, 3, vec![Tile::Int(0), Tile::Int(1), Tile::Int(2)]).unwrap();
        imf.set(1, 0, 0, Tile::Int(1)).unwrap();
        println!("{:?}", imf);
        let bytes = imf.to_bytes();
        let imf2 = IMF::from_bytes(&bytes).unwrap();
        assert_eq!(imf.width, imf2.width);
        assert_eq!(imf.height, imf2.height);
        assert_eq!(imf.layers, imf2.layers);
    }
}