unity_asset_binary/asset/
parser.rs1use super::header::SerializedFileHeader;
6use super::types::{FileIdentifier, ObjectInfo, SerializedType, TypeRegistry};
7use crate::error::{BinaryError, Result};
8use crate::reader::{BinaryReader, ByteOrder};
9
10pub struct SerializedFileParser;
15
16impl SerializedFileParser {
17 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 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 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 Self::parse_metadata(&mut file, &mut reader)?;
49
50 Ok(file)
51 }
52
53 #[cfg(feature = "async")]
55 pub async fn from_bytes_async(data: Vec<u8>) -> Result<SerializedFile> {
56 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 fn parse_metadata(file: &mut SerializedFile, reader: &mut BinaryReader) -> Result<()> {
66 if file.header.version >= 7 {
68 file.unity_version = reader.read_cstring()?;
69 }
70
71 if file.header.version >= 8 {
73 file.target_platform = reader.read_i32()?;
74 }
75
76 if file.header.version >= 13 {
78 file.enable_type_tree = reader.read_bool()?;
79 }
80
81 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 if file.header.version >= 7 && file.header.version < 14 {
91 file.big_id_enabled = reader.read_bool()?;
92 }
93
94 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 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 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 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 if file.header.version >= 5 {
136 file.user_information = reader.read_cstring()?;
137 }
138
139 Ok(())
140 }
141
142 fn parse_object_info(file: &SerializedFile, reader: &mut BinaryReader) -> Result<ObjectInfo> {
144 let path_id = if file.header.version < 14 {
146 reader.read_i32()? as i64
147 } else {
148 reader.read_i64()?
149 };
150
151 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 let byte_start = byte_start + file.header.data_offset as u64;
160
161 let byte_size = reader.read_u32()?;
163
164 let type_id = reader.read_i32()?;
166
167 Ok(ObjectInfo::new(path_id, byte_start, byte_size, type_id))
168 }
169
170 pub fn validate(file: &SerializedFile) -> Result<()> {
172 file.header.validate()?;
174
175 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 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 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#[derive(Debug)]
214pub struct SerializedFile {
215 pub header: SerializedFileHeader,
217 pub unity_version: String,
219 pub target_platform: i32,
221 pub enable_type_tree: bool,
223 pub types: Vec<SerializedType>,
225 pub big_id_enabled: bool,
227 pub objects: Vec<ObjectInfo>,
229 pub script_types: Vec<SerializedType>,
231 pub externals: Vec<FileIdentifier>,
233 pub ref_types: Vec<SerializedType>,
235 pub user_information: String,
237 data: Vec<u8>,
239}
240
241impl SerializedFile {
242 pub fn data(&self) -> &[u8] {
244 &self.data
245 }
246
247 pub fn object_count(&self) -> usize {
249 self.objects.len()
250 }
251
252 pub fn type_count(&self) -> usize {
254 self.types.len()
255 }
256
257 pub fn find_object(&self, path_id: i64) -> Option<&ObjectInfo> {
259 self.objects.iter().find(|obj| obj.path_id == path_id)
260 }
261
262 pub fn find_type(&self, class_id: i32) -> Option<&SerializedType> {
264 self.types.iter().find(|t| t.class_id == class_id)
265 }
266
267 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 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 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 pub fn validate(&self) -> Result<()> {
307 SerializedFileParser::validate(self)
308 }
309}
310
311#[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#[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 let _dummy = 1 + 1;
347 assert_eq!(_dummy, 2);
348 }
349}