1use std::collections::HashMap;
5use std::rc::Rc;
6
7use banished_formats::pkg::Archive;
8use banished_formats::types;
9use banished_formats::ui::{self, Element, Font, SpriteSheet};
10
11pub type SheetId = usize;
13pub type FontId = usize;
15
16#[derive(Debug, Clone, Copy, PartialEq)]
17pub struct SpriteRef {
19 pub sheet: SheetId,
21 pub width: i32,
23 pub height: i32,
25 pub uv: [f32; 4],
27}
28
29pub struct LoadedSheet {
31 pub reference: String,
33 pub sheet: SpriteSheet,
35 pub image: Option<String>,
37 pub shader: Option<String>,
40 pub pixels: Option<Rc<(u32, u32, Vec<u8>)>>,
43}
44
45pub struct LoadedFont {
47 pub font: Font,
49 pub sheet: Option<SheetId>,
51 pub ascent: i32,
53}
54
55pub struct Resources {
57 pub archive: Rc<Archive>,
59 elements: HashMap<String, Option<Rc<Element>>>,
60 sheet_ids: HashMap<String, Option<SheetId>>,
61 pub sheets: Vec<LoadedSheet>,
63 font_ids: HashMap<String, Option<FontId>>,
64 pub fonts: Vec<LoadedFont>,
66 tables: HashMap<String, Rc<Option<HashMap<String, String>>>>,
67 ordered: HashMap<String, Rc<Vec<(String, String)>>>,
69}
70
71impl Resources {
72 pub fn new(archive: Rc<Archive>) -> Self {
74 Resources {
75 archive,
76 elements: HashMap::new(),
77 sheet_ids: HashMap::new(),
78 sheets: Vec::new(),
79 font_ids: HashMap::new(),
80 fonts: Vec::new(),
81 tables: HashMap::new(),
82 ordered: HashMap::new(),
83 }
84 }
85
86 pub fn element(&mut self, reference: &str) -> Option<Rc<Element>> {
88 if let Some(e) = self.elements.get(reference) {
89 return e.clone();
90 }
91 let e = self
92 .archive
93 .read_ref(reference)
94 .and_then(|d| ui::element(&d))
95 .map_err(|e| log::warn!("{reference}: {e}"))
96 .ok()
97 .map(Rc::new);
98 self.elements.insert(reference.to_string(), e.clone());
99 e
100 }
101
102 pub fn sheet(&mut self, reference: &str) -> Option<SheetId> {
104 if let Some(id) = self.sheet_ids.get(reference) {
105 return *id;
106 }
107 let id =
108 self.archive
109 .read_ref(reference)
110 .and_then(|d| ui::sprite_sheet(&d))
111 .map_err(|e| log::warn!("{reference}: {e}"))
112 .ok()
113 .map(|sheet| {
114 let mi = sheet.material.as_deref().and_then(|m| {
115 types::material_instance(&self.archive.read_ref(m).ok()?).ok()
116 });
117 let image = mi
118 .as_ref()
119 .and_then(|mi| mi.texture("diffuse").map(str::to_owned));
120 let shader = mi.and_then(|mi| mi.material);
121 self.sheets.push(LoadedSheet {
122 reference: reference.to_string(),
123 sheet,
124 image,
125 shader,
126 pixels: None,
127 });
128 self.sheets.len() - 1
129 });
130 self.sheet_ids.insert(reference.to_string(), id);
131 id
132 }
133
134 pub fn image_sheet(&mut self, key: &str, width: u32, height: u32, rgba: Vec<u8>) -> SpriteRef {
140 let reference = format!("image:{key}");
141 let sheet = match self.sheet_ids.get(&reference) {
142 Some(Some(id)) => *id,
143 _ => {
144 self.sheets.push(LoadedSheet {
145 reference: reference.clone(),
146 sheet: SpriteSheet {
147 material_path: String::new(),
148 source_image: String::new(),
149 width: width as i32,
150 height: height as i32,
151 unknown_flag: false,
152 sprites: Vec::new(),
153 material: None,
154 },
155 image: None,
156 shader: None,
157 pixels: Some(Rc::new((width, height, rgba))),
158 });
159 let id = self.sheets.len() - 1;
160 self.sheet_ids.insert(reference, Some(id));
161 id
162 }
163 };
164 SpriteRef {
165 sheet,
166 width: width as i32,
167 height: height as i32,
168 uv: [0.0, 0.0, 1.0, 1.0],
169 }
170 }
171
172 pub fn sprite(&mut self, sheet_ref: Option<&str>, name: &str) -> Option<SpriteRef> {
174 if name.is_empty() {
175 return None;
176 }
177 let sheet = self.sheet(sheet_ref?)?;
178 let s = self.sheets[sheet].sheet.sprite(name)?;
179 Some(SpriteRef {
180 sheet,
181 width: s.width,
182 height: s.height,
183 uv: s.uv,
184 })
185 }
186
187 pub fn font(&mut self, reference: &str) -> Option<FontId> {
189 if let Some(id) = self.font_ids.get(reference) {
190 return *id;
191 }
192 let id = self
193 .archive
194 .read_ref(reference)
195 .and_then(|d| ui::font(&d))
196 .map_err(|e| log::warn!("{reference}: {e}"))
197 .ok()
198 .map(|font| {
199 let sheet = font.sprite_sheet.as_deref().and_then(|s| self.sheet(s));
200 let ascent = font.baseline_offset();
202 self.fonts.push(LoadedFont {
203 font,
204 sheet,
205 ascent,
206 });
207 self.fonts.len() - 1
208 });
209 self.font_ids.insert(reference.to_string(), id);
210 id
211 }
212
213 pub fn string_at(&mut self, table: &str, index: i32) -> String {
216 let list = match self.ordered.get(table) {
217 Some(l) => l.clone(),
218 None => {
219 let l = Rc::new(
220 self.archive
221 .read_ref(table)
222 .and_then(|d| ui::string_table(&d))
223 .unwrap_or_default(),
224 );
225 self.ordered.insert(table.to_string(), l.clone());
226 l
227 }
228 };
229 usize::try_from(index)
230 .ok()
231 .and_then(|i| list.get(i))
232 .map(|(_, v)| v.clone())
233 .unwrap_or_default()
234 }
235
236 pub fn string(&mut self, table: Option<&str>, key: &str) -> String {
238 let Some(table) = table else {
239 return key.to_string();
240 };
241 let map = match self.tables.get(table) {
242 Some(m) => m.clone(),
243 None => {
244 let m: Option<HashMap<String, String>> = self
245 .archive
246 .read_ref(table)
247 .and_then(|d| ui::string_table(&d))
248 .map(|v| v.into_iter().collect())
249 .ok();
250 let m = Rc::new(m);
251 self.tables.insert(table.to_string(), m.clone());
252 m
253 }
254 };
255 match map.as_ref() {
258 Some(m) => m.get(key).cloned().unwrap_or_default(),
259 None => key.to_string(),
260 }
261 }
262}