unity_asset_binary/asset/
types.rs1use crate::error::{BinaryError, Result};
6use crate::reader::BinaryReader;
7use crate::typetree::{TypeTree, TypeTreeParser};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct SerializedType {
17 pub class_id: i32,
19 pub is_stripped_type: bool,
21 pub script_type_index: i16,
23 pub type_tree: TypeTree,
25 pub script_id: [u8; 16],
27 pub old_type_hash: [u8; 16],
29 pub type_dependencies: Vec<i32>,
31 pub class_name: String,
33 pub namespace: String,
35 pub assembly_name: String,
37}
38
39impl SerializedType {
40 pub fn new(class_id: i32) -> Self {
42 Self {
43 class_id,
44 is_stripped_type: false,
45 script_type_index: -1,
46 type_tree: TypeTree::new(),
47 script_id: [0; 16],
48 old_type_hash: [0; 16],
49 type_dependencies: Vec::new(),
50 class_name: String::new(),
51 namespace: String::new(),
52 assembly_name: String::new(),
53 }
54 }
55
56 pub fn from_reader(
58 reader: &mut BinaryReader,
59 version: u32,
60 enable_type_tree: bool,
61 is_ref_type: bool,
62 ) -> Result<Self> {
63 let class_id = reader.read_i32()?;
64 let mut serialized_type = Self::new(class_id);
65
66 if version >= 16 {
67 serialized_type.is_stripped_type = reader.read_bool()?;
68 }
69
70 if version >= 17 {
71 serialized_type.script_type_index = reader.read_i16()?;
72 }
73
74 if version >= 13 {
75 let should_read_script_id = (is_ref_type && serialized_type.script_type_index >= 0)
77 || (version < 16 && class_id < 0)
78 || (version >= 16 && class_id == 114); if should_read_script_id {
81 let script_id_bytes = reader.read_bytes(16)?;
83 serialized_type.script_id.copy_from_slice(&script_id_bytes);
84 }
85
86 let old_type_hash_bytes = reader.read_bytes(16)?;
88 serialized_type
89 .old_type_hash
90 .copy_from_slice(&old_type_hash_bytes);
91 }
92
93 if enable_type_tree {
94 if version >= 12 || version == 10 {
96 serialized_type.type_tree = TypeTreeParser::from_reader_blob(reader, version)?;
97 } else {
98 serialized_type.type_tree = TypeTreeParser::from_reader(reader, version)?;
99 }
100
101 if version >= 21 {
102 if is_ref_type {
103 serialized_type.class_name = reader.read_cstring()?;
104 serialized_type.namespace = reader.read_cstring()?;
105 serialized_type.assembly_name = reader.read_cstring()?;
106 } else {
107 serialized_type.type_dependencies = read_i32_array(reader)?;
108 }
109 }
110 }
111
112 Ok(serialized_type)
113 }
114
115 pub fn is_script_type(&self) -> bool {
117 self.class_id == 114 || self.script_type_index >= 0
118 }
119
120 pub fn has_type_tree(&self) -> bool {
122 !self.type_tree.is_empty()
123 }
124
125 pub fn type_name(&self) -> String {
127 if !self.class_name.is_empty() {
128 self.class_name.clone()
129 } else {
130 format!("Class_{}", self.class_id)
131 }
132 }
133
134 pub fn full_type_name(&self) -> String {
136 if !self.namespace.is_empty() {
137 format!("{}.{}", self.namespace, self.type_name())
138 } else {
139 self.type_name()
140 }
141 }
142
143 pub fn validate(&self) -> Result<()> {
145 if self.class_id == 0 {
146 return Err(BinaryError::invalid_data("Class ID cannot be zero"));
147 }
148
149 if self.is_script_type() && self.script_id == [0; 16] {
150 return Err(BinaryError::invalid_data(
151 "Script type must have valid script ID",
152 ));
153 }
154
155 Ok(())
156 }
157}
158
159fn read_i32_array(reader: &mut BinaryReader) -> Result<Vec<i32>> {
160 let count = reader.read_i32()?;
161 if count < 0 {
162 return Err(BinaryError::invalid_data(format!(
163 "Negative array length: {}",
164 count
165 )));
166 }
167 let count = count as usize;
168 let mut values = Vec::with_capacity(count);
169 for _ in 0..count {
170 values.push(reader.read_i32()?);
171 }
172 Ok(values)
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, Default)]
180pub struct FileIdentifier {
181 pub temp_empty: String,
183 pub guid: [u8; 16],
185 pub type_: i32,
187 pub path: String,
189}
190
191impl FileIdentifier {
192 pub fn from_reader(reader: &mut BinaryReader, version: u32) -> Result<Self> {
194 let temp_empty = if version >= 6 {
195 reader.read_cstring()?
196 } else {
197 String::new()
198 };
199
200 let mut guid = [0u8; 16];
201 let mut type_ = 0i32;
202
203 if version >= 5 {
204 let guid_bytes = reader.read_bytes(16)?;
205 guid.copy_from_slice(&guid_bytes);
206 type_ = reader.read_i32()?;
207 }
208
209 let path = reader.read_cstring()?;
210
211 Ok(Self {
212 temp_empty,
213 guid,
214 type_,
215 path,
216 })
217 }
218
219 pub fn new(guid: [u8; 16], type_: i32, path: String) -> Self {
221 Self {
222 temp_empty: String::new(),
223 guid,
224 type_,
225 path,
226 }
227 }
228
229 pub fn is_valid(&self) -> bool {
231 self.guid != [0; 16] || !self.path.is_empty()
232 }
233
234 pub fn guid_string(&self) -> String {
236 format!(
237 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
238 self.guid[0],
239 self.guid[1],
240 self.guid[2],
241 self.guid[3],
242 self.guid[4],
243 self.guid[5],
244 self.guid[6],
245 self.guid[7],
246 self.guid[8],
247 self.guid[9],
248 self.guid[10],
249 self.guid[11],
250 self.guid[12],
251 self.guid[13],
252 self.guid[14],
253 self.guid[15]
254 )
255 }
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct ObjectInfo {
264 pub path_id: i64,
266 pub byte_start: u64,
268 pub byte_size: u32,
270 pub type_id: i32,
272 pub type_index: i32,
274 pub data: Vec<u8>,
276}
277
278impl ObjectInfo {
279 pub fn new(
281 path_id: i64,
282 byte_start: u64,
283 byte_size: u32,
284 type_id: i32,
285 type_index: i32,
286 ) -> Self {
287 Self {
288 path_id,
289 byte_start,
290 byte_size,
291 type_id,
292 type_index,
293 data: Vec::new(),
294 }
295 }
296
297 pub fn has_data(&self) -> bool {
299 !self.data.is_empty()
300 }
301
302 pub fn byte_end(&self) -> u64 {
304 self.byte_start + self.byte_size as u64
305 }
306
307 pub fn validate(&self) -> Result<()> {
309 if self.path_id == 0 {
310 return Err(BinaryError::invalid_data("Path ID cannot be zero"));
311 }
312
313 if self.byte_size == 0 {
314 return Err(BinaryError::invalid_data("Byte size cannot be zero"));
315 }
316
317 if self.type_id == 0 {
318 return Err(BinaryError::invalid_data("Type ID cannot be zero"));
319 }
320
321 Ok(())
322 }
323}
324
325#[derive(Debug, Clone, Default)]
330pub struct TypeRegistry {
331 types: HashMap<i32, SerializedType>,
332 script_types: HashMap<i16, SerializedType>,
333}
334
335impl TypeRegistry {
336 pub fn new() -> Self {
338 Self {
339 types: HashMap::new(),
340 script_types: HashMap::new(),
341 }
342 }
343
344 pub fn add_type(&mut self, serialized_type: SerializedType) {
346 let class_id = serialized_type.class_id;
347
348 if serialized_type.script_type_index >= 0 {
350 self.script_types
351 .insert(serialized_type.script_type_index, serialized_type.clone());
352 }
353
354 self.types.insert(class_id, serialized_type);
355 }
356
357 pub fn get_type(&self, class_id: i32) -> Option<&SerializedType> {
359 self.types.get(&class_id)
360 }
361
362 pub fn get_script_type(&self, script_index: i16) -> Option<&SerializedType> {
364 self.script_types.get(&script_index)
365 }
366
367 pub fn class_ids(&self) -> Vec<i32> {
369 self.types.keys().copied().collect()
370 }
371
372 pub fn script_indices(&self) -> Vec<i16> {
374 self.script_types.keys().copied().collect()
375 }
376
377 pub fn has_type(&self, class_id: i32) -> bool {
379 self.types.contains_key(&class_id)
380 }
381
382 pub fn has_script_type(&self, script_index: i16) -> bool {
384 self.script_types.contains_key(&script_index)
385 }
386
387 pub fn len(&self) -> usize {
389 self.types.len()
390 }
391
392 pub fn is_empty(&self) -> bool {
394 self.types.is_empty()
395 }
396
397 pub fn clear(&mut self) {
399 self.types.clear();
400 self.script_types.clear();
401 }
402
403 pub fn find_types<F>(&self, predicate: F) -> Vec<&SerializedType>
405 where
406 F: Fn(&SerializedType) -> bool,
407 {
408 self.types.values().filter(|t| predicate(t)).collect()
409 }
410
411 pub fn script_types(&self) -> Vec<&SerializedType> {
413 self.script_types.values().collect()
414 }
415
416 pub fn non_script_types(&self) -> Vec<&SerializedType> {
418 self.types
419 .values()
420 .filter(|t| !t.is_script_type())
421 .collect()
422 }
423}
424
425pub use unity_asset_core::class_ids;
427
428#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct LocalSerializedObjectIdentifier {
431 pub local_serialized_file_index: i32,
432 pub local_identifier_in_file: i64,
433}
434
435impl LocalSerializedObjectIdentifier {
436 pub fn from_reader(reader: &mut BinaryReader, version: u32) -> Result<Self> {
437 let local_serialized_file_index = reader.read_i32()?;
438 let local_identifier_in_file = if version < 14 {
439 reader.read_i32()? as i64
440 } else {
441 reader.align()?;
442 reader.read_i64()?
443 };
444 Ok(Self {
445 local_serialized_file_index,
446 local_identifier_in_file,
447 })
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454
455 #[test]
456 fn test_serialized_type_creation() {
457 let stype = SerializedType::new(114);
458 assert_eq!(stype.class_id, 114);
459 assert!(stype.is_script_type());
460 }
461
462 #[test]
463 fn test_file_identifier_guid() {
464 let guid = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
465 let file_id = FileIdentifier::new(guid, 0, "test.unity".to_string());
466 let guid_str = file_id.guid_string();
467 assert!(guid_str.contains("01020304"));
468 }
469
470 #[test]
471 fn test_type_registry() {
472 let mut registry = TypeRegistry::new();
473 let stype = SerializedType::new(28); registry.add_type(stype);
476 assert!(registry.has_type(28));
477 assert_eq!(registry.len(), 1);
478 }
479}