schematic_mesher/resource_pack/
mod.rs

1//! Resource pack loading and parsing.
2//!
3//! This module handles loading Minecraft resource packs (ZIP files or directories)
4//! and parsing their contents including blockstates, models, and textures.
5
6pub mod loader;
7pub mod blockstate;
8pub mod model;
9pub mod texture;
10
11pub use blockstate::{BlockstateDefinition, ModelVariant, MultipartCase, MultipartCondition};
12pub use model::{BlockModel, ModelElement, ModelFace};
13pub use texture::TextureData;
14
15use std::collections::HashMap;
16
17/// A loaded Minecraft resource pack.
18#[derive(Debug, Default, Clone)]
19pub struct ResourcePack {
20    /// Blockstate definitions by namespace and block ID.
21    /// Key: namespace (e.g., "minecraft"), Value: map of block_id to definition.
22    pub blockstates: HashMap<String, HashMap<String, BlockstateDefinition>>,
23
24    /// Model definitions by namespace and model path.
25    /// Key: namespace, Value: map of model_path to model.
26    pub models: HashMap<String, HashMap<String, BlockModel>>,
27
28    /// Texture data by namespace and texture path.
29    /// Key: namespace, Value: map of texture_path to data.
30    pub textures: HashMap<String, HashMap<String, TextureData>>,
31}
32
33impl ResourcePack {
34    pub fn new() -> Self {
35        Self::default()
36    }
37
38    /// Get a blockstate definition by full resource location (e.g., "minecraft:stone").
39    pub fn get_blockstate(&self, resource_location: &str) -> Option<&BlockstateDefinition> {
40        let (namespace, path) = parse_resource_location(resource_location);
41        self.blockstates
42            .get(namespace)
43            .and_then(|ns| ns.get(path))
44    }
45
46    /// Get a model by full resource location (e.g., "minecraft:block/stone").
47    pub fn get_model(&self, resource_location: &str) -> Option<&BlockModel> {
48        let (namespace, path) = parse_resource_location(resource_location);
49        self.models.get(namespace).and_then(|ns| ns.get(path))
50    }
51
52    /// Get a texture by full resource location (e.g., "minecraft:block/stone").
53    pub fn get_texture(&self, resource_location: &str) -> Option<&TextureData> {
54        let (namespace, path) = parse_resource_location(resource_location);
55        self.textures.get(namespace).and_then(|ns| ns.get(path))
56    }
57
58    /// Add a blockstate definition.
59    pub fn add_blockstate(
60        &mut self,
61        namespace: &str,
62        block_id: &str,
63        definition: BlockstateDefinition,
64    ) {
65        self.blockstates
66            .entry(namespace.to_string())
67            .or_default()
68            .insert(block_id.to_string(), definition);
69    }
70
71    /// Add a model.
72    pub fn add_model(&mut self, namespace: &str, model_path: &str, model: BlockModel) {
73        self.models
74            .entry(namespace.to_string())
75            .or_default()
76            .insert(model_path.to_string(), model);
77    }
78
79    /// Add a texture.
80    pub fn add_texture(&mut self, namespace: &str, texture_path: &str, texture: TextureData) {
81        self.textures
82            .entry(namespace.to_string())
83            .or_default()
84            .insert(texture_path.to_string(), texture);
85    }
86
87    /// Get the total number of blockstate definitions.
88    pub fn blockstate_count(&self) -> usize {
89        self.blockstates.values().map(|m| m.len()).sum()
90    }
91
92    /// Get the total number of models.
93    pub fn model_count(&self) -> usize {
94        self.models.values().map(|m| m.len()).sum()
95    }
96
97    /// Get the total number of textures.
98    pub fn texture_count(&self) -> usize {
99        self.textures.values().map(|m| m.len()).sum()
100    }
101
102    /// Get all namespaces in the resource pack.
103    pub fn namespaces(&self) -> Vec<&str> {
104        let mut namespaces: Vec<_> = self.blockstates.keys()
105            .chain(self.models.keys())
106            .chain(self.textures.keys())
107            .map(|s| s.as_str())
108            .collect();
109        namespaces.sort();
110        namespaces.dedup();
111        namespaces
112    }
113}
114
115/// Parse a resource location into namespace and path.
116/// "minecraft:block/stone" -> ("minecraft", "block/stone")
117/// "block/stone" -> ("minecraft", "block/stone")
118fn parse_resource_location(resource_location: &str) -> (&str, &str) {
119    if let Some((namespace, path)) = resource_location.split_once(':') {
120        (namespace, path)
121    } else {
122        ("minecraft", resource_location)
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn test_parse_resource_location() {
132        assert_eq!(
133            parse_resource_location("minecraft:block/stone"),
134            ("minecraft", "block/stone")
135        );
136        assert_eq!(
137            parse_resource_location("mymod:block/custom"),
138            ("mymod", "block/custom")
139        );
140        assert_eq!(
141            parse_resource_location("block/stone"),
142            ("minecraft", "block/stone")
143        );
144    }
145}