unity_asset_decode/sprite/
parser.rs

1//! Sprite parsing implementation
2//!
3//! This module provides the main parsing logic for Unity Sprite objects.
4
5use super::types::*;
6use crate::error::Result;
7use crate::object::UnityObject;
8use crate::reader::BinaryReader;
9use crate::unity_version::UnityVersion;
10use indexmap::IndexMap;
11use unity_asset_core::UnityValue;
12
13/// Sprite parser
14///
15/// This struct provides methods for parsing Unity Sprite objects from
16/// various data sources including TypeTree and binary data.
17pub struct SpriteParser {
18    version: UnityVersion,
19}
20
21impl SpriteParser {
22    /// Create a new sprite parser
23    pub fn new(version: UnityVersion) -> Self {
24        Self { version }
25    }
26
27    /// Parse Sprite from UnityObject
28    pub fn parse_from_unity_object(&self, obj: &UnityObject) -> Result<SpriteResult> {
29        let sprite = self
30            .parse_from_typetree(obj.class.properties())
31            .or_else(|_| self.parse_from_binary_data(obj.raw_data()))?;
32
33        Ok(SpriteResult::new(sprite))
34    }
35
36    /// Parse Sprite from TypeTree properties
37    pub fn parse_from_typetree(&self, properties: &IndexMap<String, UnityValue>) -> Result<Sprite> {
38        let mut sprite = Sprite::default();
39
40        // Extract name
41        if let Some(UnityValue::String(name)) = properties.get("m_Name") {
42            sprite.name = name.clone();
43        }
44
45        // Extract rect
46        if let Some(rect_value) = properties.get("m_Rect") {
47            self.extract_rect(&mut sprite, rect_value)?;
48        }
49
50        // Extract offset
51        if let Some(offset_value) = properties.get("m_Offset") {
52            self.extract_offset(&mut sprite, offset_value)?;
53        }
54
55        // Extract border
56        if let Some(border_value) = properties.get("m_Border") {
57            self.extract_border(&mut sprite, border_value)?;
58        }
59
60        // Extract pixels to units
61        if let Some(UnityValue::Float(pixels_to_units)) = properties.get("m_PixelsToUnits") {
62            sprite.pixels_to_units = *pixels_to_units as f32;
63        }
64
65        // Extract pivot
66        if let Some(pivot_value) = properties.get("m_Pivot") {
67            self.extract_pivot(&mut sprite, pivot_value)?;
68        }
69
70        // Extract extrude
71        if let Some(UnityValue::Integer(extrude)) = properties.get("m_Extrude") {
72            sprite.extrude = *extrude as u8;
73        }
74
75        // Extract polygon flag
76        if let Some(UnityValue::Bool(is_polygon)) = properties.get("m_IsPolygon") {
77            sprite.is_polygon = *is_polygon;
78        }
79
80        // Extract render data
81        if let Some(render_data_value) = properties.get("m_RD") {
82            self.extract_render_data(&mut sprite, render_data_value)?;
83        }
84
85        // Extract atlas tags
86        if let Some(atlas_tags_value) = properties.get("m_AtlasTags") {
87            self.extract_atlas_tags(&mut sprite, atlas_tags_value)?;
88        }
89
90        // Extract sprite atlas reference
91        if let Some(sprite_atlas_value) = properties.get("m_SpriteAtlas") {
92            self.extract_sprite_atlas(&mut sprite, sprite_atlas_value)?;
93        }
94
95        Ok(sprite)
96    }
97
98    /// Parse Sprite from raw binary data (fallback method)
99    #[allow(clippy::field_reassign_with_default)]
100    pub fn parse_from_binary_data(&self, data: &[u8]) -> Result<Sprite> {
101        let mut reader = BinaryReader::new(data, crate::reader::ByteOrder::Little);
102        let mut sprite = Sprite::default();
103
104        // Read name (aligned string)
105        sprite.name = reader.read_aligned_string()?;
106
107        // Read rect
108        sprite.rect_x = reader.read_f32()?;
109        sprite.rect_y = reader.read_f32()?;
110        sprite.rect_width = reader.read_f32()?;
111        sprite.rect_height = reader.read_f32()?;
112
113        // Read offset
114        sprite.offset_x = reader.read_f32()?;
115        sprite.offset_y = reader.read_f32()?;
116
117        // Read border
118        sprite.border_x = reader.read_f32()?;
119        sprite.border_y = reader.read_f32()?;
120        sprite.border_z = reader.read_f32()?;
121        sprite.border_w = reader.read_f32()?;
122
123        // Read pixels to units
124        sprite.pixels_to_units = reader.read_f32()?;
125
126        // Read pivot
127        sprite.pivot_x = reader.read_f32()?;
128        sprite.pivot_y = reader.read_f32()?;
129
130        // Read extrude
131        sprite.extrude = reader.read_u8()?;
132        reader.align_to(4)?; // Align after reading byte
133
134        // Read polygon flag
135        sprite.is_polygon = reader.read_bool()?;
136        reader.align_to(4)?; // Align after reading bool
137
138        // Try to read render data if there's more data
139        if reader.remaining() > 0 {
140            self.read_render_data_binary(&mut sprite, &mut reader)?;
141        }
142
143        Ok(sprite)
144    }
145
146    /// Extract rectangle from UnityValue
147    fn extract_rect(&self, sprite: &mut Sprite, rect_value: &UnityValue) -> Result<()> {
148        if let UnityValue::Object(rect_obj) = rect_value {
149            if let Some(UnityValue::Float(x)) = rect_obj.get("x") {
150                sprite.rect_x = *x as f32;
151            }
152            if let Some(UnityValue::Float(y)) = rect_obj.get("y") {
153                sprite.rect_y = *y as f32;
154            }
155            if let Some(UnityValue::Float(width)) = rect_obj.get("width") {
156                sprite.rect_width = *width as f32;
157            }
158            if let Some(UnityValue::Float(height)) = rect_obj.get("height") {
159                sprite.rect_height = *height as f32;
160            }
161        }
162        Ok(())
163    }
164
165    /// Extract offset from UnityValue
166    fn extract_offset(&self, sprite: &mut Sprite, offset_value: &UnityValue) -> Result<()> {
167        if let UnityValue::Object(offset_obj) = offset_value {
168            if let Some(UnityValue::Float(x)) = offset_obj.get("x") {
169                sprite.offset_x = *x as f32;
170            }
171            if let Some(UnityValue::Float(y)) = offset_obj.get("y") {
172                sprite.offset_y = *y as f32;
173            }
174        }
175        Ok(())
176    }
177
178    /// Extract border from UnityValue
179    fn extract_border(&self, sprite: &mut Sprite, border_value: &UnityValue) -> Result<()> {
180        if let UnityValue::Object(border_obj) = border_value {
181            if let Some(UnityValue::Float(x)) = border_obj.get("x") {
182                sprite.border_x = *x as f32;
183            }
184            if let Some(UnityValue::Float(y)) = border_obj.get("y") {
185                sprite.border_y = *y as f32;
186            }
187            if let Some(UnityValue::Float(z)) = border_obj.get("z") {
188                sprite.border_z = *z as f32;
189            }
190            if let Some(UnityValue::Float(w)) = border_obj.get("w") {
191                sprite.border_w = *w as f32;
192            }
193        }
194        Ok(())
195    }
196
197    /// Extract pivot from UnityValue
198    fn extract_pivot(&self, sprite: &mut Sprite, pivot_value: &UnityValue) -> Result<()> {
199        if let UnityValue::Object(pivot_obj) = pivot_value {
200            if let Some(UnityValue::Float(x)) = pivot_obj.get("x") {
201                sprite.pivot_x = *x as f32;
202            }
203            if let Some(UnityValue::Float(y)) = pivot_obj.get("y") {
204                sprite.pivot_y = *y as f32;
205            }
206        }
207        Ok(())
208    }
209
210    /// Extract render data from UnityValue
211    fn extract_render_data(
212        &self,
213        sprite: &mut Sprite,
214        render_data_value: &UnityValue,
215    ) -> Result<()> {
216        if let UnityValue::Object(rd_obj) = render_data_value {
217            // Extract texture reference
218            if let Some(UnityValue::Object(texture_obj)) = rd_obj.get("texture") {
219                if let Some(UnityValue::Integer(path_id)) = texture_obj.get("m_PathID") {
220                    sprite.render_data.texture_path_id = *path_id;
221                }
222            }
223
224            // Extract texture rect
225            if let Some(UnityValue::Object(rect_obj)) = rd_obj.get("textureRect") {
226                if let Some(UnityValue::Float(x)) = rect_obj.get("x") {
227                    sprite.render_data.texture_rect_x = *x as f32;
228                }
229                if let Some(UnityValue::Float(y)) = rect_obj.get("y") {
230                    sprite.render_data.texture_rect_y = *y as f32;
231                }
232                if let Some(UnityValue::Float(width)) = rect_obj.get("width") {
233                    sprite.render_data.texture_rect_width = *width as f32;
234                }
235                if let Some(UnityValue::Float(height)) = rect_obj.get("height") {
236                    sprite.render_data.texture_rect_height = *height as f32;
237                }
238            }
239
240            // Extract other render data fields
241            if let Some(UnityValue::Float(downscale)) = rd_obj.get("downscaleMultiplier") {
242                sprite.render_data.downscale_multiplier = *downscale as f32;
243            }
244        }
245        Ok(())
246    }
247
248    /// Extract atlas tags from UnityValue
249    fn extract_atlas_tags(&self, sprite: &mut Sprite, atlas_tags_value: &UnityValue) -> Result<()> {
250        if let UnityValue::Array(tags_array) = atlas_tags_value {
251            sprite.atlas_tags.clear();
252            for tag_value in tags_array {
253                if let UnityValue::String(tag) = tag_value {
254                    sprite.atlas_tags.push(tag.clone());
255                }
256            }
257        }
258        Ok(())
259    }
260
261    /// Extract sprite atlas reference from UnityValue
262    fn extract_sprite_atlas(
263        &self,
264        sprite: &mut Sprite,
265        sprite_atlas_value: &UnityValue,
266    ) -> Result<()> {
267        if let UnityValue::Object(atlas_obj) = sprite_atlas_value {
268            if let Some(UnityValue::Integer(path_id)) = atlas_obj.get("m_PathID") {
269                sprite.sprite_atlas_path_id = Some(*path_id);
270            }
271        }
272        Ok(())
273    }
274
275    /// Read render data from binary stream
276    fn read_render_data_binary(
277        &self,
278        sprite: &mut Sprite,
279        reader: &mut BinaryReader,
280    ) -> Result<()> {
281        // This is a simplified implementation
282        // The actual structure depends on the Unity version
283        if reader.remaining() >= 4 {
284            sprite.render_data.texture_path_id = reader.read_i64().unwrap_or(0);
285        }
286
287        if reader.remaining() >= 16 {
288            sprite.render_data.texture_rect_x = reader.read_f32().unwrap_or(0.0);
289            sprite.render_data.texture_rect_y = reader.read_f32().unwrap_or(0.0);
290            sprite.render_data.texture_rect_width = reader.read_f32().unwrap_or(0.0);
291            sprite.render_data.texture_rect_height = reader.read_f32().unwrap_or(0.0);
292        }
293
294        Ok(())
295    }
296
297    /// Get the Unity version
298    pub fn version(&self) -> &UnityVersion {
299        &self.version
300    }
301
302    /// Set the Unity version
303    pub fn set_version(&mut self, version: UnityVersion) {
304        self.version = version;
305    }
306}
307
308impl Default for SpriteParser {
309    fn default() -> Self {
310        Self::new(UnityVersion::default())
311    }
312}
313
314#[cfg(test)]
315mod tests {
316    use super::*;
317
318    #[test]
319    fn test_parser_creation() {
320        let parser = SpriteParser::new(UnityVersion::default());
321        assert_eq!(parser.version(), &UnityVersion::default());
322    }
323
324    #[test]
325    fn test_extract_rect() {
326        let parser = SpriteParser::default();
327        let mut sprite = Sprite::default();
328
329        let mut rect_obj = IndexMap::new();
330        rect_obj.insert("x".to_string(), UnityValue::Float(10.0));
331        rect_obj.insert("y".to_string(), UnityValue::Float(20.0));
332        rect_obj.insert("width".to_string(), UnityValue::Float(100.0));
333        rect_obj.insert("height".to_string(), UnityValue::Float(200.0));
334
335        let rect_value = UnityValue::Object(rect_obj);
336        parser.extract_rect(&mut sprite, &rect_value).unwrap();
337
338        assert_eq!(sprite.rect_x, 10.0);
339        assert_eq!(sprite.rect_y, 20.0);
340        assert_eq!(sprite.rect_width, 100.0);
341        assert_eq!(sprite.rect_height, 200.0);
342    }
343}