1use crate::error::{BinaryError, Result};
4use crate::reader::{BinaryReader, ByteOrder};
5use crate::typetree::{TypeTree, TypeTreeNode};
6use crate::unity_objects::{GameObject, Transform};
7use std::collections::HashMap;
9use unity_asset_core::{UnityClass, UnityValue};
10
11#[derive(Debug, Clone)]
13pub struct ObjectInfo {
14 pub path_id: i64,
16 pub byte_start: u64,
18 pub byte_size: u32,
20 pub class_id: i32,
22 pub type_id: i32,
24 pub byte_order: ByteOrder,
26 pub data: Vec<u8>,
28 pub type_tree: Option<TypeTree>,
30}
31
32impl ObjectInfo {
33 pub fn new(path_id: i64, byte_start: u64, byte_size: u32, class_id: i32) -> Self {
35 Self {
36 path_id,
37 byte_start,
38 byte_size,
39 class_id,
40 type_id: class_id, byte_order: ByteOrder::Little,
42 data: Vec::new(),
43 type_tree: None,
44 }
45 }
46
47 pub fn reader(&self) -> BinaryReader<'_> {
49 BinaryReader::new(&self.data, self.byte_order)
50 }
51
52 pub fn class_name(&self) -> String {
54 unity_asset_core::get_class_name(self.class_id)
55 .unwrap_or_else(|| format!("Class_{}", self.class_id))
56 }
57
58 pub fn parse_object(&self) -> Result<UnityClass> {
60 let mut unity_class =
61 UnityClass::new(self.class_id, self.class_name(), self.path_id.to_string());
62
63 if let Some(ref type_tree) = self.type_tree {
64 let mut reader = self.reader();
65 let properties = self.parse_with_typetree(&mut reader, type_tree)?;
66
67 for (key, value) in properties {
68 unity_class.set(key, value);
69 }
70 } else {
71 unity_class.set(
73 "_raw_data".to_string(),
74 UnityValue::Array(
75 self.data
76 .iter()
77 .map(|&b| UnityValue::Integer(b as i64))
78 .collect(),
79 ),
80 );
81 }
82
83 Ok(unity_class)
84 }
85
86 fn parse_with_typetree(
88 &self,
89 reader: &mut BinaryReader,
90 type_tree: &TypeTree,
91 ) -> Result<HashMap<String, UnityValue>> {
92 let mut properties = HashMap::new();
93
94 if let Some(root) = type_tree.nodes.first() {
95 self.parse_node(reader, root, &mut properties)?;
96 }
97
98 Ok(properties)
99 }
100
101 fn parse_node(
103 &self,
104 reader: &mut BinaryReader,
105 node: &TypeTreeNode,
106 properties: &mut HashMap<String, UnityValue>,
107 ) -> Result<()> {
108 if node.name.is_empty() || !node.name.starts_with("m_") {
109 return Ok(());
111 }
112
113 let value = match node.type_name.as_str() {
114 "bool" => UnityValue::Bool(reader.read_bool()?),
115 "SInt8" => UnityValue::Integer(reader.read_i8()? as i64),
116 "UInt8" => UnityValue::Integer(reader.read_u8()? as i64),
117 "SInt16" => UnityValue::Integer(reader.read_i16()? as i64),
118 "UInt16" => UnityValue::Integer(reader.read_u16()? as i64),
119 "SInt32" | "int" => UnityValue::Integer(reader.read_i32()? as i64),
120 "UInt32" => UnityValue::Integer(reader.read_u32()? as i64),
121 "SInt64" => UnityValue::Integer(reader.read_i64()?),
122 "UInt64" => UnityValue::Integer(reader.read_u64()? as i64),
123 "float" => UnityValue::Float(reader.read_f32()? as f64),
124 "double" => UnityValue::Float(reader.read_f64()?),
125 "string" => {
126 let length = reader.read_u32()? as usize;
127 let bytes = reader.read_bytes(length)?;
128 let string = String::from_utf8(bytes).map_err(|e| {
129 BinaryError::invalid_data(format!("Invalid UTF-8 string: {}", e))
130 })?;
131 reader.align()?; UnityValue::String(string)
133 }
134 "Array" => {
135 let size = reader.read_u32()? as usize;
137 let mut array = Vec::new();
138
139 for _ in 0..size {
142 if node.children.len() > 1 {
143 if let Some(element_node) = node.children.get(1) {
145 let element_value = self.parse_single_value(reader, element_node)?;
146 array.push(element_value);
147 }
148 } else {
149 array.push(UnityValue::Integer(reader.read_u8()? as i64));
151 }
152 }
153
154 UnityValue::Array(array)
155 }
156 _ => {
157 if node.byte_size > 0 && node.byte_size <= 1024 {
159 let bytes = reader.read_bytes(node.byte_size as usize)?;
161 UnityValue::Array(
162 bytes
163 .into_iter()
164 .map(|b| UnityValue::Integer(b as i64))
165 .collect(),
166 )
167 } else {
168 UnityValue::Null
170 }
171 }
172 };
173
174 properties.insert(node.name.clone(), value);
175 Ok(())
176 }
177
178 fn parse_single_value(
180 &self,
181 reader: &mut BinaryReader,
182 node: &TypeTreeNode,
183 ) -> Result<UnityValue> {
184 match node.type_name.as_str() {
185 "bool" => Ok(UnityValue::Bool(reader.read_bool()?)),
186 "SInt8" => Ok(UnityValue::Integer(reader.read_i8()? as i64)),
187 "UInt8" => Ok(UnityValue::Integer(reader.read_u8()? as i64)),
188 "SInt16" => Ok(UnityValue::Integer(reader.read_i16()? as i64)),
189 "UInt16" => Ok(UnityValue::Integer(reader.read_u16()? as i64)),
190 "SInt32" | "int" => Ok(UnityValue::Integer(reader.read_i32()? as i64)),
191 "UInt32" => Ok(UnityValue::Integer(reader.read_u32()? as i64)),
192 "SInt64" => Ok(UnityValue::Integer(reader.read_i64()?)),
193 "UInt64" => Ok(UnityValue::Integer(reader.read_u64()? as i64)),
194 "float" => Ok(UnityValue::Float(reader.read_f32()? as f64)),
195 "double" => Ok(UnityValue::Float(reader.read_f64()?)),
196 _ => {
197 if node.byte_size > 0 && node.byte_size <= 64 {
199 let bytes = reader.read_bytes(node.byte_size as usize)?;
200 Ok(UnityValue::Array(
201 bytes
202 .into_iter()
203 .map(|b| UnityValue::Integer(b as i64))
204 .collect(),
205 ))
206 } else {
207 Ok(UnityValue::Null)
208 }
209 }
210 }
211 }
212}
213
214#[derive(Debug, Clone)]
216pub struct UnityObject {
217 pub info: ObjectInfo,
219 pub class: UnityClass,
221}
222
223impl UnityObject {
224 pub fn new(info: ObjectInfo) -> Result<Self> {
226 let class = info.parse_object()?;
227 Ok(Self { info, class })
228 }
229
230 pub fn path_id(&self) -> i64 {
232 self.info.path_id
233 }
234
235 pub fn class_id(&self) -> i32 {
237 self.info.class_id
238 }
239
240 pub fn class_name(&self) -> &str {
242 &self.class.class_name
243 }
244
245 pub fn name(&self) -> Option<String> {
247 self.class.get("m_Name").and_then(|v| match v {
248 UnityValue::String(s) => Some(s.clone()),
249 _ => None,
250 })
251 }
252
253 pub fn get(&self, key: &str) -> Option<&UnityValue> {
255 self.class.get(key)
256 }
257
258 pub fn set(&mut self, key: String, value: UnityValue) {
260 self.class.set(key, value);
261 }
262
263 pub fn has_property(&self, key: &str) -> bool {
265 self.class.has_property(key)
266 }
267
268 pub fn property_names(&self) -> Vec<&String> {
270 self.class.properties().keys().collect()
271 }
272
273 pub fn as_unity_class(&self) -> &UnityClass {
275 &self.class
276 }
277
278 pub fn as_unity_class_mut(&mut self) -> &mut UnityClass {
280 &mut self.class
281 }
282
283 pub fn as_gameobject(&self) -> Result<GameObject> {
285 if self.class_id() != 1 {
286 return Err(BinaryError::invalid_data(format!(
287 "Object is not a GameObject (class_id: {})",
288 self.class_id()
289 )));
290 }
291 GameObject::from_typetree(self.class.properties())
292 }
293
294 pub fn as_transform(&self) -> Result<Transform> {
296 if self.class_id() != 4 {
297 return Err(BinaryError::invalid_data(format!(
298 "Object is not a Transform (class_id: {})",
299 self.class_id()
300 )));
301 }
302 Transform::from_typetree(self.class.properties())
303 }
304
305 pub fn is_gameobject(&self) -> bool {
307 self.class_id() == 1
308 }
309
310 pub fn is_transform(&self) -> bool {
312 self.class_id() == 4
313 }
314
315 pub fn describe(&self) -> String {
317 let name = self.name().unwrap_or_else(|| "<unnamed>".to_string());
318 format!(
319 "{} '{}' (ID:{}, PathID:{})",
320 self.class_name(),
321 name,
322 self.class_id(),
323 self.path_id()
324 )
325 }
326
327 pub fn parse_with_typetree(
329 &self,
330 typetree: &crate::typetree::TypeTree,
331 ) -> Result<indexmap::IndexMap<String, unity_asset_core::UnityValue>> {
332 let mut reader =
333 crate::reader::BinaryReader::new(&self.info.data, crate::reader::ByteOrder::Little);
334 let serializer = crate::typetree::TypeTreeSerializer::new(typetree);
335 serializer.parse_object(&mut reader)
336 }
337
338 pub fn raw_data(&self) -> &[u8] {
340 &self.info.data
341 }
342
343 pub fn byte_size(&self) -> u32 {
345 self.info.byte_size
346 }
347
348 pub fn byte_start(&self) -> u64 {
350 self.info.byte_start
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357
358 #[test]
359 fn test_object_info_creation() {
360 let info = ObjectInfo::new(12345, 1000, 256, 1);
361 assert_eq!(info.path_id, 12345);
362 assert_eq!(info.byte_start, 1000);
363 assert_eq!(info.byte_size, 256);
364 assert_eq!(info.class_id, 1);
365 }
366
367 #[test]
368 fn test_object_info_class_name() {
369 let info = ObjectInfo::new(1, 0, 0, 1);
370 assert_eq!(info.class_name(), "GameObject");
371
372 let info = ObjectInfo::new(1, 0, 0, 999999);
373 assert_eq!(info.class_name(), "Class_999999");
374 }
375
376 #[test]
377 fn test_object_creation() {
378 let mut info = ObjectInfo::new(1, 0, 4, 1);
379 info.data = vec![1, 0, 0, 0]; let result = UnityObject::new(info);
382 assert!(result.is_ok());
383
384 let object = result.unwrap();
385 assert_eq!(object.path_id(), 1);
386 assert_eq!(object.class_id(), 1);
387 assert_eq!(object.class_name(), "GameObject");
388 }
389}