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