maple_render_core/
repository.rs1use std::{
2 collections::HashMap,
3 io::{Cursor, Read, Seek},
4};
5#[cfg(not(target_arch = "wasm32"))]
6use std::{fs::File, io::BufReader, path::Path};
7
8use image::RgbaImage;
9use zip::ZipArchive;
10
11use crate::{
12 error::{Error, Result},
13 mapping::Mapping,
14 template::Template,
15};
16
17trait ReadSeek: Read + Seek {}
18impl<T: Read + Seek> ReadSeek for T {}
19
20pub struct Repository {
21 zip: ZipArchive<Box<dyn ReadSeek>>,
22 pub template: Template,
23 mappings: HashMap<i32, Mapping>,
24 peak_cache_count: usize,
25}
26
27impl Repository {
28 #[cfg(not(target_arch = "wasm32"))]
29 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
30 let path = path.as_ref();
31
32 if !path.exists() {
33 return Err(Error::FileNotFound(path.to_path_buf()));
34 }
35
36 let file = File::open(path)?;
37 let reader: Box<dyn ReadSeek> = Box::new(BufReader::new(file));
38 Self::load_from_reader(reader)
39 }
40
41 pub fn load_from_bytes(bytes: Vec<u8>) -> Result<Self> {
42 let reader: Box<dyn ReadSeek> = Box::new(Cursor::new(bytes));
43 Self::load_from_reader(reader)
44 }
45
46 fn load_from_reader(reader: Box<dyn ReadSeek>) -> Result<Self> {
47 let mut zip = ZipArchive::new(reader)?;
48
49 let template_json = Self::load_text_from_zip(&mut zip, "template.json")?;
50 let template: Template = serde_json::from_str(&template_json)?;
51
52 Ok(Repository { zip, template, mappings: HashMap::new(), peak_cache_count: 0 })
53 }
54
55 fn load_text_from_zip<R: Read + Seek>(zip: &mut ZipArchive<R>, name: &str) -> Result<String> {
56 let mut file = zip.by_name(name).map_err(|_| Error::MissingFile(name.to_string()))?;
57 let mut contents = String::new();
58 file.read_to_string(&mut contents)?;
59 Ok(contents)
60 }
61
62 fn load_image(&mut self, name: &str) -> Result<RgbaImage> {
63 let mut file = self.zip.by_name(name).map_err(|_| Error::MissingFile(name.to_string()))?;
64 let mut data = Vec::new();
65 file.read_to_end(&mut data)?;
66 let img = image::load_from_memory(&data)?;
67 Ok(img.to_rgba8())
68 }
69
70 fn load_frame(&mut self, frame: i32) -> Result<Mapping> {
71 let light_name = format!("frame{}_light.png", frame);
72 let dark_name = format!("frame{}_dark.png", frame);
73 let map_name = format!("frame{}_map.png", frame);
74 let sel_name = format!("frame{}_sel.png", frame);
75 let transparent_name = format!("frame{}_transparent.png", frame);
76
77 let light = self.load_image(&light_name)?;
78 let dark = self.load_image(&dark_name)?;
79 let map1 = self.load_image(&map_name)?;
80 let map2 = self.load_image(&sel_name)?;
81
82 let neutral = match self.load_image(&transparent_name) {
83 Ok(img) => img,
84 Err(_) => light.clone(),
85 };
86
87 Ok(Mapping {
88 light,
89 dark,
90 map1,
91 map2,
92 neutral,
93 scale: 1,
94 light_name,
95 dark_name,
96 map1_name: map_name,
97 map2_name: sel_name,
98 neutral_name: transparent_name,
99 smooth_cache: std::sync::OnceLock::new(),
100 })
101 }
102
103 pub fn get_mapping(&mut self, index: i32) -> Result<&Mapping> {
104 let actual_index = if !self.template.is_animation() { 0 } else { index };
105
106 if !self.mappings.contains_key(&actual_index) {
107 let mapping = self.load_frame(actual_index)?;
108 self.mappings.insert(actual_index, mapping);
109
110 if self.mappings.len() > self.peak_cache_count {
111 self.peak_cache_count = self.mappings.len();
112 }
113 }
114
115 Ok(self.mappings.get(&actual_index).unwrap())
116 }
117
118 pub fn remove_mapping(&mut self, index: i32) {
119 self.mappings.remove(&index);
120 }
121
122 pub fn take_mapping(&mut self, index: i32) -> Result<Mapping> {
123 let actual_index = if !self.template.is_animation() { 0 } else { index };
124
125 if !self.mappings.contains_key(&actual_index) {
126 let mapping = self.load_frame(actual_index)?;
127 return Ok(mapping);
128 }
129
130 Ok(self.mappings.remove(&actual_index).unwrap())
131 }
132
133 pub fn is_animation(&self) -> bool {
134 self.template.is_animation()
135 }
136
137 pub fn length(&self) -> u32 {
138 self.template.frames
139 }
140
141 pub fn get_palette(&self) -> Vec<i32> {
142 self.template.palette.clone()
143 }
144
145 pub fn get_period(&self) -> f64 {
146 self.template.period()
147 }
148
149 pub fn get_hold(&self) -> f64 {
150 self.template.hold
151 }
152
153 pub fn peak(&self) -> usize {
154 self.peak_cache_count
155 }
156}