unity_asset_binary/asset/
parser.rs

1//! SerializedFile parser implementation
2//!
3//! This module provides the main parsing logic for Unity SerializedFile structures.
4
5use super::header::SerializedFileHeader;
6use super::types::{FileIdentifier, ObjectInfo, SerializedType, TypeRegistry};
7use crate::error::{BinaryError, Result};
8use crate::reader::{BinaryReader, ByteOrder};
9
10/// SerializedFile parser
11///
12/// This struct handles the parsing of Unity SerializedFile structures,
13/// supporting different Unity versions and formats.
14pub struct SerializedFileParser;
15
16impl SerializedFileParser {
17    /// Parse SerializedFile from binary data
18    pub fn from_bytes(data: Vec<u8>) -> Result<SerializedFile> {
19        let data_clone = data.clone();
20        let mut reader = BinaryReader::new(&data_clone, ByteOrder::Big);
21
22        // Read header
23        let header = SerializedFileHeader::from_reader(&mut reader)?;
24
25        if !header.is_valid() {
26            return Err(BinaryError::invalid_data("Invalid SerializedFile header"));
27        }
28
29        // Switch to the correct byte order
30        reader.set_byte_order(header.byte_order());
31
32        let mut file = SerializedFile {
33            header,
34            unity_version: String::new(),
35            target_platform: 0,
36            enable_type_tree: false,
37            types: Vec::new(),
38            big_id_enabled: false,
39            objects: Vec::new(),
40            script_types: Vec::new(),
41            externals: Vec::new(),
42            ref_types: Vec::new(),
43            user_information: String::new(),
44            data: data.clone(),
45        };
46
47        // Parse metadata
48        Self::parse_metadata(&mut file, &mut reader)?;
49
50        Ok(file)
51    }
52
53    /// Parse SerializedFile from binary data asynchronously
54    #[cfg(feature = "async")]
55    pub async fn from_bytes_async(data: Vec<u8>) -> Result<SerializedFile> {
56        // For now, use spawn_blocking to run the sync version
57        let result = tokio::task::spawn_blocking(move || Self::from_bytes(data))
58            .await
59            .map_err(|e| BinaryError::generic(format!("Task join error: {}", e)))??;
60
61        Ok(result)
62    }
63
64    /// Parse the metadata section
65    fn parse_metadata(file: &mut SerializedFile, reader: &mut BinaryReader) -> Result<()> {
66        // Read Unity version (if version >= 7)
67        if file.header.version >= 7 {
68            file.unity_version = reader.read_cstring()?;
69        }
70
71        // Read target platform (if version >= 8)
72        if file.header.version >= 8 {
73            file.target_platform = reader.read_i32()?;
74        }
75
76        // Read enable type tree flag (if version >= 13)
77        if file.header.version >= 13 {
78            file.enable_type_tree = reader.read_bool()?;
79        }
80
81        // Read types
82        let type_count = reader.read_u32()? as usize;
83        for _ in 0..type_count {
84            let serialized_type =
85                SerializedType::from_reader(reader, file.header.version, file.enable_type_tree)?;
86            file.types.push(serialized_type);
87        }
88
89        // Read big ID enabled flag (if version 7-13)
90        if file.header.version >= 7 && file.header.version < 14 {
91            file.big_id_enabled = reader.read_bool()?;
92        }
93
94        // Read objects
95        let object_count = reader.read_u32()? as usize;
96        for _ in 0..object_count {
97            let object_info = Self::parse_object_info(file, reader)?;
98            file.objects.push(object_info);
99        }
100
101        // Read script types (if version >= 11)
102        if file.header.version >= 11 {
103            let script_count = reader.read_u32()? as usize;
104            for _ in 0..script_count {
105                let script_type = SerializedType::from_reader(
106                    reader,
107                    file.header.version,
108                    file.enable_type_tree,
109                )?;
110                file.script_types.push(script_type);
111            }
112        }
113
114        // Read externals
115        let external_count = reader.read_u32()? as usize;
116        for _ in 0..external_count {
117            let external = FileIdentifier::from_reader(reader, file.header.version)?;
118            file.externals.push(external);
119        }
120
121        // Read ref types (if version >= 20)
122        if file.header.version >= 20 {
123            let ref_type_count = reader.read_u32()? as usize;
124            for _ in 0..ref_type_count {
125                let ref_type = SerializedType::from_reader(
126                    reader,
127                    file.header.version,
128                    file.enable_type_tree,
129                )?;
130                file.ref_types.push(ref_type);
131            }
132        }
133
134        // Read user information (if version >= 5)
135        if file.header.version >= 5 {
136            file.user_information = reader.read_cstring()?;
137        }
138
139        Ok(())
140    }
141
142    /// Parse object information
143    fn parse_object_info(file: &SerializedFile, reader: &mut BinaryReader) -> Result<ObjectInfo> {
144        // Read path ID
145        let path_id = if file.header.version < 14 {
146            reader.read_i32()? as i64
147        } else {
148            reader.read_i64()?
149        };
150
151        // Read byte start
152        let byte_start = if file.header.version >= 22 {
153            reader.read_i64()? as u64
154        } else {
155            reader.read_u32()? as u64
156        };
157
158        // Add data offset
159        let byte_start = byte_start + file.header.data_offset as u64;
160
161        // Read byte size
162        let byte_size = reader.read_u32()?;
163
164        // Read type ID
165        let type_id = reader.read_i32()?;
166
167        Ok(ObjectInfo::new(path_id, byte_start, byte_size, type_id))
168    }
169
170    /// Validate parsed SerializedFile
171    pub fn validate(file: &SerializedFile) -> Result<()> {
172        // Validate header
173        file.header.validate()?;
174
175        // Validate objects
176        for (i, obj) in file.objects.iter().enumerate() {
177            obj.validate().map_err(|e| {
178                BinaryError::generic(format!("Object {} validation failed: {}", i, e))
179            })?;
180        }
181
182        // Validate types
183        for (i, stype) in file.types.iter().enumerate() {
184            stype.validate().map_err(|e| {
185                BinaryError::generic(format!("Type {} validation failed: {}", i, e))
186            })?;
187        }
188
189        Ok(())
190    }
191
192    /// Get parsing statistics
193    pub fn get_parsing_stats(file: &SerializedFile) -> ParsingStats {
194        ParsingStats {
195            version: file.header.version,
196            unity_version: file.unity_version.clone(),
197            target_platform: file.target_platform,
198            file_size: file.header.file_size,
199            object_count: file.objects.len(),
200            type_count: file.types.len(),
201            script_type_count: file.script_types.len(),
202            external_count: file.externals.len(),
203            has_type_tree: file.enable_type_tree,
204            big_id_enabled: file.big_id_enabled,
205        }
206    }
207}
208
209/// Complete SerializedFile structure
210///
211/// This structure represents a complete Unity SerializedFile with all its
212/// metadata, type information, and object data.
213#[derive(Debug)]
214pub struct SerializedFile {
215    /// File header
216    pub header: SerializedFileHeader,
217    /// Unity version string
218    pub unity_version: String,
219    /// Target platform
220    pub target_platform: i32,
221    /// Whether type tree is enabled
222    pub enable_type_tree: bool,
223    /// Type information
224    pub types: Vec<SerializedType>,
225    /// Whether big IDs are enabled
226    pub big_id_enabled: bool,
227    /// Object information
228    pub objects: Vec<ObjectInfo>,
229    /// Script types
230    pub script_types: Vec<SerializedType>,
231    /// External file references
232    pub externals: Vec<FileIdentifier>,
233    /// Reference types
234    pub ref_types: Vec<SerializedType>,
235    /// User information
236    pub user_information: String,
237    /// Raw file data
238    data: Vec<u8>,
239}
240
241impl SerializedFile {
242    /// Get the raw file data
243    pub fn data(&self) -> &[u8] {
244        &self.data
245    }
246
247    /// Get object count
248    pub fn object_count(&self) -> usize {
249        self.objects.len()
250    }
251
252    /// Get type count
253    pub fn type_count(&self) -> usize {
254        self.types.len()
255    }
256
257    /// Find object by path ID
258    pub fn find_object(&self, path_id: i64) -> Option<&ObjectInfo> {
259        self.objects.iter().find(|obj| obj.path_id == path_id)
260    }
261
262    /// Find type by class ID
263    pub fn find_type(&self, class_id: i32) -> Option<&SerializedType> {
264        self.types.iter().find(|t| t.class_id == class_id)
265    }
266
267    /// Get all objects of a specific type
268    pub fn objects_of_type(&self, type_id: i32) -> Vec<&ObjectInfo> {
269        self.objects
270            .iter()
271            .filter(|obj| obj.type_id == type_id)
272            .collect()
273    }
274
275    /// Create a type registry from this file
276    pub fn create_type_registry(&self) -> TypeRegistry {
277        let mut registry = TypeRegistry::new();
278
279        for stype in &self.types {
280            registry.add_type(stype.clone());
281        }
282
283        for script_type in &self.script_types {
284            registry.add_type(script_type.clone());
285        }
286
287        registry
288    }
289
290    /// Get file statistics
291    pub fn statistics(&self) -> FileStatistics {
292        FileStatistics {
293            version: self.header.version,
294            unity_version: self.unity_version.clone(),
295            file_size: self.header.file_size,
296            object_count: self.objects.len(),
297            type_count: self.types.len(),
298            script_type_count: self.script_types.len(),
299            external_count: self.externals.len(),
300            has_type_tree: self.enable_type_tree,
301            target_platform: self.target_platform,
302        }
303    }
304
305    /// Validate the entire file
306    pub fn validate(&self) -> Result<()> {
307        SerializedFileParser::validate(self)
308    }
309}
310
311/// Parsing statistics
312#[derive(Debug, Clone)]
313pub struct ParsingStats {
314    pub version: u32,
315    pub unity_version: String,
316    pub target_platform: i32,
317    pub file_size: u32,
318    pub object_count: usize,
319    pub type_count: usize,
320    pub script_type_count: usize,
321    pub external_count: usize,
322    pub has_type_tree: bool,
323    pub big_id_enabled: bool,
324}
325
326/// File statistics
327#[derive(Debug, Clone)]
328pub struct FileStatistics {
329    pub version: u32,
330    pub unity_version: String,
331    pub file_size: u32,
332    pub object_count: usize,
333    pub type_count: usize,
334    pub script_type_count: usize,
335    pub external_count: usize,
336    pub has_type_tree: bool,
337    pub target_platform: i32,
338}
339
340#[cfg(test)]
341mod tests {
342    #[test]
343    fn test_parser_creation() {
344        // Basic test to ensure parser methods exist
345        // This test verifies that the parser module compiles correctly
346        let _dummy = 1 + 1;
347        assert_eq!(_dummy, 2);
348    }
349}