schematic_mesher/resource_pack/
loader.rs1use super::{BlockModel, BlockstateDefinition, ResourcePack, TextureData};
4use crate::error::{MesherError, Result};
5use crate::resource_pack::texture::load_texture_from_bytes;
6use std::io::Read;
7use std::path::Path;
8
9pub fn load_from_path<P: AsRef<Path>>(path: P) -> Result<ResourcePack> {
13 let path = path.as_ref();
14
15 if path.is_dir() {
16 load_from_directory(path)
17 } else {
18 let data = std::fs::read(path)?;
19 load_from_bytes(&data)
20 }
21}
22
23pub fn load_from_bytes(data: &[u8]) -> Result<ResourcePack> {
25 let cursor = std::io::Cursor::new(data);
26 let mut archive = zip::ZipArchive::new(cursor)?;
27
28 let mut pack = ResourcePack::new();
29
30 for i in 0..archive.len() {
32 let mut file = archive.by_index(i)?;
33 let file_path = file.name().to_string();
34
35 if file.is_dir() {
37 continue;
38 }
39
40 if let Some((namespace, asset_type, asset_path)) = parse_asset_path(&file_path) {
42 match asset_type {
43 "blockstates" => {
44 if asset_path.ends_with(".json") {
45 let mut contents = String::new();
46 file.read_to_string(&mut contents)?;
47
48 let block_id = asset_path.trim_end_matches(".json");
49 match serde_json::from_str::<BlockstateDefinition>(&contents) {
50 Ok(def) => {
51 pack.add_blockstate(namespace, block_id, def);
52 }
53 Err(e) => {
54 eprintln!(
56 "Warning: Failed to parse blockstate {}/{}: {}",
57 namespace, block_id, e
58 );
59 }
60 }
61 }
62 }
63 "models" => {
64 if asset_path.ends_with(".json") {
65 let mut contents = String::new();
66 file.read_to_string(&mut contents)?;
67
68 let model_path = asset_path.trim_end_matches(".json");
69 match serde_json::from_str::<BlockModel>(&contents) {
70 Ok(model) => {
71 pack.add_model(namespace, model_path, model);
72 }
73 Err(e) => {
74 eprintln!(
75 "Warning: Failed to parse model {}/{}: {}",
76 namespace, model_path, e
77 );
78 }
79 }
80 }
81 }
82 "textures" => {
83 if asset_path.ends_with(".png") {
84 let mut data = Vec::new();
85 file.read_to_end(&mut data)?;
86
87 let texture_path = asset_path.trim_end_matches(".png");
88 match load_texture_from_bytes(&data) {
89 Ok(texture) => {
90 pack.add_texture(namespace, texture_path, texture);
91 }
92 Err(e) => {
93 eprintln!(
94 "Warning: Failed to load texture {}/{}: {}",
95 namespace, texture_path, e
96 );
97 }
98 }
99 }
100 }
101 _ => {}
102 }
103 }
104 }
105
106 Ok(pack)
107}
108
109fn load_from_directory(path: &Path) -> Result<ResourcePack> {
111 let mut pack = ResourcePack::new();
112
113 let assets_path = path.join("assets");
115 if !assets_path.exists() {
116 return Err(MesherError::InvalidResourcePack(
117 "No assets directory found".to_string(),
118 ));
119 }
120
121 for namespace_entry in std::fs::read_dir(&assets_path)? {
123 let namespace_entry = namespace_entry?;
124 if !namespace_entry.file_type()?.is_dir() {
125 continue;
126 }
127
128 let namespace = namespace_entry
129 .file_name()
130 .to_string_lossy()
131 .to_string();
132 let namespace_path = namespace_entry.path();
133
134 let blockstates_path = namespace_path.join("blockstates");
136 if blockstates_path.exists() {
137 load_json_files(&blockstates_path, &namespace, |block_id, contents| {
138 if let Ok(def) = serde_json::from_str::<BlockstateDefinition>(contents) {
139 pack.add_blockstate(&namespace, block_id, def);
140 }
141 })?;
142 }
143
144 let models_path = namespace_path.join("models");
146 if models_path.exists() {
147 load_json_files_recursive(&models_path, &models_path, &namespace, &mut |model_path, contents| {
148 if let Ok(model) = serde_json::from_str::<BlockModel>(contents) {
149 pack.add_model(&namespace, model_path, model);
150 }
151 })?;
152 }
153
154 let textures_path = namespace_path.join("textures");
156 if textures_path.exists() {
157 load_texture_files_recursive(&textures_path, &textures_path, &namespace, &mut |texture_path, data| {
158 if let Ok(texture) = load_texture_from_bytes(data) {
159 pack.add_texture(&namespace, texture_path, texture);
160 }
161 })?;
162 }
163 }
164
165 Ok(pack)
166}
167
168fn parse_asset_path(file_path: &str) -> Option<(&str, &str, &str)> {
171 let parts: Vec<&str> = file_path.splitn(4, '/').collect();
173
174 if parts.len() >= 4 && parts[0] == "assets" {
175 Some((parts[1], parts[2], parts[3]))
176 } else {
177 None
178 }
179}
180
181fn load_json_files<F>(dir: &Path, namespace: &str, mut handler: F) -> Result<()>
183where
184 F: FnMut(&str, &str),
185{
186 for entry in std::fs::read_dir(dir)? {
187 let entry = entry?;
188 let path = entry.path();
189
190 if path.extension().map(|e| e == "json").unwrap_or(false) {
191 let file_name = path
192 .file_stem()
193 .unwrap()
194 .to_string_lossy()
195 .to_string();
196
197 let contents = std::fs::read_to_string(&path)?;
198 handler(&file_name, &contents);
199 }
200 }
201 Ok(())
202}
203
204fn load_json_files_recursive<F>(
206 base: &Path,
207 dir: &Path,
208 namespace: &str,
209 handler: &mut F,
210) -> Result<()>
211where
212 F: FnMut(&str, &str),
213{
214 for entry in std::fs::read_dir(dir)? {
215 let entry = entry?;
216 let path = entry.path();
217
218 if path.is_dir() {
219 load_json_files_recursive(base, &path, namespace, handler)?;
220 } else if path.extension().map(|e| e == "json").unwrap_or(false) {
221 let relative = path
222 .strip_prefix(base)
223 .unwrap()
224 .with_extension("")
225 .to_string_lossy()
226 .replace('\\', "/");
227
228 let contents = std::fs::read_to_string(&path)?;
229 handler(&relative, &contents);
230 }
231 }
232 Ok(())
233}
234
235fn load_texture_files_recursive<F>(
237 base: &Path,
238 dir: &Path,
239 namespace: &str,
240 handler: &mut F,
241) -> Result<()>
242where
243 F: FnMut(&str, &[u8]),
244{
245 for entry in std::fs::read_dir(dir)? {
246 let entry = entry?;
247 let path = entry.path();
248
249 if path.is_dir() {
250 load_texture_files_recursive(base, &path, namespace, handler)?;
251 } else if path.extension().map(|e| e == "png").unwrap_or(false) {
252 let relative = path
253 .strip_prefix(base)
254 .unwrap()
255 .with_extension("")
256 .to_string_lossy()
257 .replace('\\', "/");
258
259 let data = std::fs::read(&path)?;
260 handler(&relative, &data);
261 }
262 }
263 Ok(())
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn test_parse_asset_path() {
272 assert_eq!(
273 parse_asset_path("assets/minecraft/blockstates/stone.json"),
274 Some(("minecraft", "blockstates", "stone.json"))
275 );
276 assert_eq!(
277 parse_asset_path("assets/minecraft/models/block/stone.json"),
278 Some(("minecraft", "models", "block/stone.json"))
279 );
280 assert_eq!(
281 parse_asset_path("assets/mymod/textures/block/custom.png"),
282 Some(("mymod", "textures", "block/custom.png"))
283 );
284 assert_eq!(parse_asset_path("pack.mcmeta"), None);
285 assert_eq!(parse_asset_path("data/minecraft/recipes/test.json"), None);
286 }
287}