summaryrefslogtreecommitdiff
path: root/src/sprite_loader.rs
blob: 01d13aa398a1b1d70672385ed8ff3db34209fd42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use serde::Deserialize;
use std::{collections::HashMap, error::Error, fs::File, io::BufReader, path::Path};

use crate::{content::materials::MaterialId, sim::cell::Cell};

fn load_img(path: &Path) -> Result<(Vec<u8>, u32, u32), Box<dyn Error>> {
    let mut decoder = png::Decoder::new(BufReader::new(File::open(path)?));
    decoder.set_transformations(png::Transformations::IDENTITY);

    let mut reader = decoder.read_info()?;
    // only None if it's too big for memory
    let mut buf = vec![0; reader.output_buffer_size().unwrap()];
    let info = reader.next_frame(&mut buf)?;

    if info.color_type != png::ColorType::Indexed {
        return Err(format!("{:#?}: not indexed", path.to_str()).into());
    }
    if info.bit_depth != png::BitDepth::Eight {
        return Err(format!(
            "{:#?}: expected 8-bit, got {:?}",
            path.to_str(),
            info.bit_depth
        )
        .into());
    }

    buf.truncate(info.buffer_size());
    Ok((buf, info.width, info.height))
}

#[derive(Deserialize)]
struct PaletteEntry {
    material: MaterialId,
}

#[derive(Deserialize)]
struct SpriteManifest {
    palette: HashMap<u8, PaletteEntry>,
}

fn load_manifest(path: &Path) -> Result<SpriteManifest, Box<dyn Error>> {
    Ok(toml::from_str(&std::fs::read_to_string(path)?)?)
}

pub struct SpriteCells {
    pub cells: Vec<Cell>,
    pub width: u32,
    pub height: u32,
}

pub fn load_sprite_to_cells(path: &str) -> SpriteCells {
    let img_result = load_img(&Path::join(Path::new(path), Path::new("img.png")));
    match img_result {
        Ok((indices, width, height)) => {
            let manifest_result =
                load_manifest(&Path::join(Path::new(path), Path::new("manifest.toml")));
            match manifest_result {
                Ok(SpriteManifest { palette }) => {
                    let cells: Vec<Cell> = indices
                        .iter()
                        .map(|i| {
                            Cell::from_material(
                                palette.get(i).map_or(MaterialId::Void, |v| v.material),
                            )
                        })
                        .collect();

                    SpriteCells {
                        cells,
                        width,
                        height,
                    }
                }
                Err(e) => {
                    panic!("Couldn't load sprite: {}", e)
                }
            }
        }
        Err(e) => {
            panic!("Couldn't load sprite: {}", e)
        }
    }
}