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: Option<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: None,
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 ) -> Result<Self> {
62 let class_id = reader.read_i32()?;
63 let mut serialized_type = Self::new(class_id);
64
65 if version >= 16 {
66 serialized_type.is_stripped_type = reader.read_bool()?;
67 }
68
69 if version >= 17 {
70 let script_type_index = reader.read_i16()?;
71 serialized_type.script_type_index = Some(script_type_index);
72 }
73
74 if version >= 13 {
75 let should_read_script_id = if version < 16 {
77 class_id < 0
78 } else {
79 class_id == 114 };
81
82 if should_read_script_id {
83 let script_id_bytes = reader.read_bytes(16)?;
85 serialized_type.script_id.copy_from_slice(&script_id_bytes);
86 }
87
88 let old_type_hash_bytes = reader.read_bytes(16)?;
90 serialized_type
91 .old_type_hash
92 .copy_from_slice(&old_type_hash_bytes);
93 }
94
95 if enable_type_tree {
96 if version >= 12 || version == 10 {
98 serialized_type.type_tree = TypeTreeParser::from_reader_blob(reader, version)?;
99 } else {
100 serialized_type.type_tree = TypeTreeParser::from_reader(reader, version)?;
101 }
102 }
103
104 Ok(serialized_type)
105 }
106
107 pub fn is_script_type(&self) -> bool {
109 self.class_id == 114 || self.script_type_index.is_some()
110 }
111
112 pub fn has_type_tree(&self) -> bool {
114 !self.type_tree.is_empty()
115 }
116
117 pub fn type_name(&self) -> String {
119 if !self.class_name.is_empty() {
120 self.class_name.clone()
121 } else {
122 format!("Class_{}", self.class_id)
123 }
124 }
125
126 pub fn full_type_name(&self) -> String {
128 if !self.namespace.is_empty() {
129 format!("{}.{}", self.namespace, self.type_name())
130 } else {
131 self.type_name()
132 }
133 }
134
135 pub fn validate(&self) -> Result<()> {
137 if self.class_id == 0 {
138 return Err(BinaryError::invalid_data("Class ID cannot be zero"));
139 }
140
141 if self.is_script_type() && self.script_id == [0; 16] {
142 return Err(BinaryError::invalid_data(
143 "Script type must have valid script ID",
144 ));
145 }
146
147 Ok(())
148 }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize, Default)]
156pub struct FileIdentifier {
157 pub guid: [u8; 16],
159 pub type_: i32,
161 pub path: String,
163}
164
165impl FileIdentifier {
166 pub fn from_reader(reader: &mut BinaryReader, _version: u32) -> Result<Self> {
168 let mut guid = [0u8; 16];
169 let guid_bytes = reader.read_bytes(16)?;
170 guid.copy_from_slice(&guid_bytes);
171
172 let type_ = reader.read_i32()?;
173 let path = reader.read_aligned_string()?;
174
175 Ok(Self { guid, type_, path })
176 }
177
178 pub fn new(guid: [u8; 16], type_: i32, path: String) -> Self {
180 Self { guid, type_, path }
181 }
182
183 pub fn is_valid(&self) -> bool {
185 self.guid != [0; 16] || !self.path.is_empty()
186 }
187
188 pub fn guid_string(&self) -> String {
190 format!(
191 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
192 self.guid[0],
193 self.guid[1],
194 self.guid[2],
195 self.guid[3],
196 self.guid[4],
197 self.guid[5],
198 self.guid[6],
199 self.guid[7],
200 self.guid[8],
201 self.guid[9],
202 self.guid[10],
203 self.guid[11],
204 self.guid[12],
205 self.guid[13],
206 self.guid[14],
207 self.guid[15]
208 )
209 }
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct ObjectInfo {
218 pub path_id: i64,
220 pub byte_start: u64,
222 pub byte_size: u32,
224 pub type_id: i32,
226 pub data: Vec<u8>,
228}
229
230impl ObjectInfo {
231 pub fn new(path_id: i64, byte_start: u64, byte_size: u32, type_id: i32) -> Self {
233 Self {
234 path_id,
235 byte_start,
236 byte_size,
237 type_id,
238 data: Vec::new(),
239 }
240 }
241
242 pub fn has_data(&self) -> bool {
244 !self.data.is_empty()
245 }
246
247 pub fn byte_end(&self) -> u64 {
249 self.byte_start + self.byte_size as u64
250 }
251
252 pub fn validate(&self) -> Result<()> {
254 if self.path_id == 0 {
255 return Err(BinaryError::invalid_data("Path ID cannot be zero"));
256 }
257
258 if self.byte_size == 0 {
259 return Err(BinaryError::invalid_data("Byte size cannot be zero"));
260 }
261
262 if self.type_id == 0 {
263 return Err(BinaryError::invalid_data("Type ID cannot be zero"));
264 }
265
266 Ok(())
267 }
268}
269
270#[derive(Debug, Clone, Default)]
275pub struct TypeRegistry {
276 types: HashMap<i32, SerializedType>,
277 script_types: HashMap<i16, SerializedType>,
278}
279
280impl TypeRegistry {
281 pub fn new() -> Self {
283 Self {
284 types: HashMap::new(),
285 script_types: HashMap::new(),
286 }
287 }
288
289 pub fn add_type(&mut self, serialized_type: SerializedType) {
291 let class_id = serialized_type.class_id;
292
293 if let Some(script_index) = serialized_type.script_type_index {
295 self.script_types
296 .insert(script_index, serialized_type.clone());
297 }
298
299 self.types.insert(class_id, serialized_type);
300 }
301
302 pub fn get_type(&self, class_id: i32) -> Option<&SerializedType> {
304 self.types.get(&class_id)
305 }
306
307 pub fn get_script_type(&self, script_index: i16) -> Option<&SerializedType> {
309 self.script_types.get(&script_index)
310 }
311
312 pub fn class_ids(&self) -> Vec<i32> {
314 self.types.keys().copied().collect()
315 }
316
317 pub fn script_indices(&self) -> Vec<i16> {
319 self.script_types.keys().copied().collect()
320 }
321
322 pub fn has_type(&self, class_id: i32) -> bool {
324 self.types.contains_key(&class_id)
325 }
326
327 pub fn has_script_type(&self, script_index: i16) -> bool {
329 self.script_types.contains_key(&script_index)
330 }
331
332 pub fn len(&self) -> usize {
334 self.types.len()
335 }
336
337 pub fn is_empty(&self) -> bool {
339 self.types.is_empty()
340 }
341
342 pub fn clear(&mut self) {
344 self.types.clear();
345 self.script_types.clear();
346 }
347
348 pub fn find_types<F>(&self, predicate: F) -> Vec<&SerializedType>
350 where
351 F: Fn(&SerializedType) -> bool,
352 {
353 self.types.values().filter(|t| predicate(t)).collect()
354 }
355
356 pub fn script_types(&self) -> Vec<&SerializedType> {
358 self.script_types.values().collect()
359 }
360
361 pub fn non_script_types(&self) -> Vec<&SerializedType> {
363 self.types
364 .values()
365 .filter(|t| !t.is_script_type())
366 .collect()
367 }
368}
369
370pub mod class_ids {
372 pub const OBJECT: i32 = 1;
373 pub const COMPONENT: i32 = 2;
374 pub const BEHAVIOUR: i32 = 3;
375 pub const UNITY_ENGINE_OBJECT: i32 = 4;
376 pub const GAME_OBJECT: i32 = 1;
377 pub const TRANSFORM: i32 = 4;
378 pub const MONO_BEHAVIOUR: i32 = 114;
379 pub const TEXTURE_2D: i32 = 28;
380 pub const SPRITE: i32 = 213;
381 pub const MESH: i32 = 43;
382 pub const AUDIO_CLIP: i32 = 83;
383 pub const MATERIAL: i32 = 21;
384 pub const SHADER: i32 = 48;
385 pub const ANIMATION_CLIP: i32 = 74;
386 pub const ANIMATOR_CONTROLLER: i32 = 91;
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn test_serialized_type_creation() {
395 let stype = SerializedType::new(114);
396 assert_eq!(stype.class_id, 114);
397 assert!(stype.is_script_type());
398 }
399
400 #[test]
401 fn test_file_identifier_guid() {
402 let guid = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
403 let file_id = FileIdentifier::new(guid, 0, "test.unity".to_string());
404 let guid_str = file_id.guid_string();
405 assert!(guid_str.contains("01020304"));
406 }
407
408 #[test]
409 fn test_type_registry() {
410 let mut registry = TypeRegistry::new();
411 let stype = SerializedType::new(28); registry.add_type(stype);
414 assert!(registry.has_type(28));
415 assert_eq!(registry.len(), 1);
416 }
417}