il2cpp_bridge_rs/structs/core/hierarchy/
class.rs1use crate::api::{self, cache};
4use crate::structs::collections::Il2cppArray;
5use crate::structs::core::{Field, Method, Property};
6use std::ffi::c_void;
7use std::sync::Arc;
8
9use super::object::Object;
10
11#[derive(Debug, Clone)]
18pub struct Class {
19 pub address: *mut c_void,
21 pub image: *mut c_void,
23 pub token: u32,
25 pub name: String,
27 pub parent: Option<String>,
29 pub namespace: String,
31 pub is_enum: bool,
33 pub is_generic: bool,
35 pub is_inflated: bool,
37 pub is_interface: bool,
39 pub is_abstract: bool,
41 pub is_blittable: bool,
43 pub is_valuetype: bool,
45 pub flags: i32,
47 pub rank: i32,
49 pub instance_size: i32,
51 pub array_element_size: i32,
53 pub num_fields_count: usize,
55 pub enum_basetype: *mut c_void,
57 pub static_field_data: *mut c_void,
59 pub assembly_name: String,
61 pub assembly: Option<std::sync::Arc<crate::structs::Assembly>>,
63 pub fields: Vec<Field>,
65
66 pub methods: Vec<Method>,
68 pub properties: Vec<Property>,
70 pub interfaces: Vec<*mut c_void>,
72 pub nested_types: Vec<*mut c_void>,
74 pub element_class: *mut c_void, pub declaring_type: *mut c_void, pub ty: *mut c_void,
80 pub object: *mut c_void,
82}
83
84unsafe impl Send for Class {}
85unsafe impl Sync for Class {}
86
87pub trait MethodSelector {
89 fn matches(&self, method: &Method) -> bool;
91}
92
93impl MethodSelector for &str {
95 fn matches(&self, method: &Method) -> bool {
96 method.name == *self
97 }
98}
99
100impl MethodSelector for (&str, &[&str]) {
102 fn matches(&self, method: &Method) -> bool {
103 if method.name != self.0 {
104 return false;
105 }
106
107 if method.args.len() != self.1.len() {
108 return false;
109 }
110
111 for (i, param_name) in self.1.iter().enumerate() {
112 let arg_type = &method.args[i].type_info;
113 if arg_type.name != *param_name && arg_type.cpp_name() != *param_name {
114 return false;
115 }
116 }
117
118 true
119 }
120}
121
122impl<const N: usize> MethodSelector for (&str, [&str; N]) {
124 fn matches(&self, method: &Method) -> bool {
125 (self.0, self.1.as_slice()).matches(method)
126 }
127}
128
129impl MethodSelector for (&str, usize) {
131 fn matches(&self, method: &Method) -> bool {
132 method.name == self.0 && method.args.len() == self.1
133 }
134}
135
136impl std::fmt::Display for Class {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 write!(f, "{}", self.dump_string())
139 }
140}
141
142impl Class {
143 pub fn dump_string(&self) -> String {
148 let mut s = String::new();
149
150 let ns_part = if !self.namespace.is_empty() {
152 self.namespace.clone()
153 } else {
154 String::new()
155 };
156
157 let dll_part = if !self.assembly_name.is_empty() {
158 if self.assembly_name.ends_with(".dll") {
159 self.assembly_name.clone()
160 } else {
161 format!("{}.dll", self.assembly_name)
162 }
163 } else {
164 String::new()
165 };
166
167 match (!ns_part.is_empty(), !dll_part.is_empty()) {
168 (true, true) => s.push_str(&format!(
169 "// Namespace: {} \u{2014} {}\n",
170 ns_part, dll_part
171 )),
172 (true, false) => s.push_str(&format!("// Namespace: {}\n", ns_part)),
173 (false, true) => s.push_str(&format!("// Image: {}\n", dll_part)),
174 (false, false) => {}
175 }
176
177 let abstract_kw = if self.is_abstract && !self.is_interface {
178 "abstract "
179 } else {
180 ""
181 };
182
183 let type_kw = if self.is_enum {
184 "enum"
185 } else if self.is_interface {
186 "interface"
187 } else if self.is_valuetype {
188 "struct"
189 } else {
190 "class"
191 };
192
193 let typedef_index = self.token & 0x00FF_FFFF;
195
196 let inheritance = if let Some(parent) = &self.parent {
197 format!(" : {}", parent)
198 } else {
199 String::new()
200 };
201
202 if self.is_enum {
203 let underlying_type = self.enum_underlying_type();
204 s.push_str(&format!(
205 "public {} {} : {} // TypeDefIndex: {}\n{{\n",
206 type_kw, self.name, underlying_type, typedef_index
207 ));
208
209 let mut has_variants = false;
210 for field in self
211 .fields
212 .iter()
213 .filter(|field| field.is_static && field.name != "value__")
214 {
215 has_variants = true;
216 if let Some(value) = Self::enum_value_string(field, underlying_type) {
217 s.push_str(&format!(" {} = {},\n", field.name, value));
218 } else {
219 s.push_str(&format!(" {},\n", field.name));
220 }
221 }
222
223 if !has_variants {
224 s.push_str(" // Empty enum\n");
225 }
226
227 s.push_str("}\n");
228 return s;
229 }
230
231 s.push_str(&format!(
232 "public {}{} {}{} // TypeDefIndex: {}\n{{\n",
233 abstract_kw, type_kw, self.name, inheritance, typedef_index
234 ));
235
236 if !self.fields.is_empty() {
238 s.push_str(" // Fields\n");
239 for field in &self.fields {
240 s.push_str(&format!(" {}\n", field));
241 }
242 }
243
244 if !self.properties.is_empty() {
246 if !self.fields.is_empty() {
247 s.push('\n');
248 }
249 s.push_str(" // Properties\n");
250 for prop in &self.properties {
251 s.push_str(&format!(" {}\n", prop));
252 }
253 }
254
255 let constructors: Vec<_> = self
257 .methods
258 .iter()
259 .filter(|m| m.name == ".ctor" || m.name == ".cctor")
260 .collect();
261
262 let regular_methods: Vec<_> = self
263 .methods
264 .iter()
265 .filter(|m| m.name != ".ctor" && m.name != ".cctor")
266 .collect();
267
268 if !constructors.is_empty() {
270 if !self.fields.is_empty() || !self.properties.is_empty() {
271 s.push('\n');
272 }
273 s.push_str(" // Constructors\n");
274 for method in &constructors {
275 for line in method.to_string().lines() {
276 s.push_str(&format!(" {}\n", line));
277 }
278 s.push('\n');
279 }
280 }
281
282 if !regular_methods.is_empty() {
284 if constructors.is_empty() && (!self.fields.is_empty() || !self.properties.is_empty()) {
285 s.push('\n');
286 }
287 s.push_str(" // Methods\n");
288 for method in ®ular_methods {
289 for line in method.to_string().lines() {
290 s.push_str(&format!(" {}\n", line));
291 }
292 s.push('\n');
293 }
294 }
295
296 s.push_str("}\n");
297 s
298 }
299
300 pub fn create_instance(&self) -> Result<Object, String> {
304 unsafe {
305 if self.ty.is_null() {
306 return Err(format!("Could not get Type for class '{}'", self.name));
307 }
308
309 let corlib = cache::mscorlib();
310 let activator_class = corlib
311 .class("System.Activator")
312 .ok_or_else(|| "Could not find System.Activator class".to_string())?;
313
314 let method = activator_class
315 .method(("CreateInstance", ["System.Type"]))
316 .ok_or_else(|| "Could not find CreateInstance(Type) method".to_string())?;
317
318 if self.object.is_null() {
319 return Err("Could not get Type object".to_string());
320 }
321
322 match method.call::<Object>(&[self.object]) {
323 Ok(result_handle) => {
324 if result_handle.ptr.is_null() {
325 return Err("CreateInstance returned null".to_string());
326 }
327 Ok(result_handle)
328 }
329 Err(e) => Err(format!("CreateInstance invocation failed: {}", e)),
330 }
331 }
332 }
333
334 pub fn new_object(&self) -> Result<Object, String> {
339 unsafe {
340 let obj = Object::from_ptr(api::object_new(self.address));
341 if obj.ptr.is_null() {
342 return Err("Could not create object".to_string());
343 }
344 Ok(obj)
345 }
346 }
347
348 pub fn create_scriptable_instance(&self) -> Result<Object, String> {
350 unsafe {
351 if self.object.is_null() {
352 return Err(format!(
353 "Could not get Type object for class '{}'",
354 self.name
355 ));
356 }
357
358 let core_module = cache::coremodule();
359 let so_class = core_module
360 .class("UnityEngine.ScriptableObject")
361 .ok_or("Class 'UnityEngine.ScriptableObject' not found")?;
362
363 let method = so_class
364 .method(("CreateInstance", ["System.Type"]))
365 .ok_or("Method 'CreateInstance' not found in ScriptableObject")?;
366
367 let result = method.call::<Object>(&[self.object])?;
368
369 if result.ptr.is_null() {
370 return Err("ScriptableObject.CreateInstance returned null".to_string());
371 }
372
373 Ok(result)
374 }
375 }
376
377 pub fn find_objects_of_type(&self, include_inactive: bool) -> Vec<Object> {
382 if self.object.is_null() {
383 return Vec::new();
384 }
385
386 let type_object = self.object;
387
388 let unity_engine_core = cache::coremodule();
389 let unity_object = match unity_engine_core.class("UnityEngine.Object") {
390 Some(c) => c,
391 None => return Vec::new(),
392 };
393
394 let method = unity_object
395 .method(("FindObjectsOfType", ["System.Type", "System.Boolean"]))
396 .or_else(|| unity_object.method(("FindObjectsOfType", ["System.Type"])));
397
398 match method {
399 Some(method) => unsafe {
400 let params = if method.args.len() == 2 {
401 vec![type_object, &include_inactive as *const bool as *mut c_void]
402 } else {
403 vec![type_object]
404 };
405
406 match method.call::<*mut Il2cppArray<Object>>(¶ms) {
407 Ok(array) => {
408 if array.is_null() {
409 return Vec::new();
410 }
411 (*array).to_vector().into_iter().collect()
412 }
413 Err(_) => Vec::new(),
414 }
415 },
416 None => Vec::new(),
417 }
418 }
419
420 pub fn method<S: MethodSelector>(&self, selector: S) -> Option<Method> {
425 if let Some(method) = self.methods.iter().find(|m| selector.matches(m)).cloned() {
426 return Some(method);
427 }
428
429 if self.parent.is_some() {
430 return self
431 .get_parent_class()
432 .and_then(|parent| parent.method(selector));
433 }
434
435 None
436 }
437
438 pub fn field(&self, name: &str) -> Option<Field> {
440 if let Some(field) = self.fields.iter().find(|f| f.name == name).cloned() {
441 return Some(field);
442 }
443
444 if self.parent.is_some() {
445 return self
446 .get_parent_class()
447 .and_then(|parent| parent.field(name));
448 }
449
450 None
451 }
452
453 pub fn property(&self, name: &str) -> Option<Property> {
455 if let Some(prop) = self.properties.iter().find(|p| p.name == name).cloned() {
456 return Some(prop);
457 }
458
459 if self.parent.is_some() {
460 return self
461 .get_parent_class()
462 .and_then(|parent| parent.property(name));
463 }
464
465 None
466 }
467
468 fn get_parent_class(&self) -> Option<Class> {
470 unsafe {
471 let parent_ptr = crate::api::class_get_parent(self.address);
472 if parent_ptr.is_null() {
473 return None;
474 }
475 cache::class_from_ptr(parent_ptr)
476 }
477 }
478
479 pub fn init(&self) {
483 unsafe { api::runtime_class_init(self.address) }
484 }
485
486 pub fn has_references(&self) -> bool {
491 unsafe { api::class_has_references(self.address) }
492 }
493
494 pub fn is_assignable_from(&self, other: &Class) -> bool {
502 unsafe { api::class_is_assignable_from(self.address, other.address) }
503 }
504
505 pub fn is_subclass_of(&self, other: &Class, check_interfaces: bool) -> bool {
514 unsafe { api::class_is_subclass_of(self.address, other.address, check_interfaces) }
515 }
516
517 pub fn get_value_size(&self) -> i32 {
522 unsafe { api::class_value_size(self.address, std::ptr::null_mut()) }
523 }
524
525 pub fn has_parent(&self, parent: &Class) -> bool {
533 unsafe { api::class_has_parent(self.address, parent.address) }
534 }
535
536 pub fn has_attribute(&self, attr_class: &Class) -> bool {
544 unsafe { api::class_has_attribute(self.address, attr_class.address) }
545 }
546
547 pub fn inflate(&self, classes: &[&Class]) -> Result<Arc<Class>, String> {
555 unsafe {
556 if !self.is_generic {
557 return Err(format!(
558 "Class '{}' is not a generic type definition and cannot be inflated",
559 self.name
560 ));
561 }
562
563 if self.object.is_null() {
564 return Err(format!(
565 "Could not get Type object for class '{}'",
566 self.name
567 ));
568 }
569
570 if classes.is_empty() {
571 return Err("No type arguments provided".to_string());
572 }
573
574 let mut type_args = Vec::with_capacity(classes.len());
575 for (i, cls) in classes.iter().enumerate() {
576 if cls.object.is_null() {
577 return Err(format!(
578 "Class '{}' (arg {}) has no Type object",
579 cls.name, i
580 ));
581 }
582 type_args.push(cls.object);
583 }
584
585 let corlib = cache::mscorlib();
586 let type_class = corlib
587 .class("System.Type")
588 .ok_or_else(|| "Could not find System.Type class".to_string())?;
589
590 let type_array = Il2cppArray::<*mut c_void>::new(&type_class, type_args.len());
591 if type_array.is_null() {
592 return Err("Could not create Type[] array".to_string());
593 }
594
595 let array_ref = &mut *type_array;
596 for (i, &type_arg) in type_args.iter().enumerate() {
597 array_ref.set(i, type_arg);
598 }
599
600 let type_obj = Object::from_ptr(self.object);
601
602 let make_generic_type_method = type_obj
603 .method(("MakeGenericType", ["System.Type[]"]))
604 .ok_or_else(|| "Could not find MakeGenericType(Type[]) method".to_string())?;
605
606 let inflated_type_obj =
607 make_generic_type_method.call::<*mut c_void>(&[type_array as *mut c_void])?;
608
609 if inflated_type_obj.is_null() {
610 return Err("MakeGenericType returned null".to_string());
611 }
612
613 let inflated_class_ptr = api::class_from_system_type(inflated_type_obj);
614 if inflated_class_ptr.is_null() {
615 return Err("Could not get Il2CppClass from inflated Type".to_string());
616 }
617
618 api::runtime_class_init(inflated_class_ptr);
619
620 cache::class_from_ptr(inflated_class_ptr)
621 .map(Arc::new)
622 .ok_or_else(|| {
623 "Could not convert inflated class pointer to Class struct".to_string()
624 })
625 }
626 }
627
628 fn enum_underlying_type(&self) -> &'static str {
630 let type_name = self
631 .fields
632 .iter()
633 .find(|field| field.name == "value__")
634 .map(|field| field.type_info.name.as_str())
635 .unwrap_or("System.Int32");
636
637 match type_name {
638 "System.Byte" | "Byte" | "byte" => "byte",
639 "System.SByte" | "SByte" | "sbyte" => "sbyte",
640 "System.Int16" | "Int16" | "short" => "short",
641 "System.UInt16" | "UInt16" | "ushort" => "ushort",
642 "System.Int32" | "Int32" | "int" => "int",
643 "System.UInt32" | "UInt32" | "uint" => "uint",
644 "System.Int64" | "Int64" | "long" => "long",
645 "System.UInt64" | "UInt64" | "ulong" => "ulong",
646 _ => "int",
647 }
648 }
649
650 fn enum_value_string(field: &Field, underlying_type: &str) -> Option<String> {
652 unsafe {
653 match underlying_type {
654 "byte" => field.get_value::<u8>().ok().map(|value| value.to_string()),
655 "sbyte" => field.get_value::<i8>().ok().map(|value| value.to_string()),
656 "short" => field.get_value::<i16>().ok().map(|value| value.to_string()),
657 "ushort" => field.get_value::<u16>().ok().map(|value| value.to_string()),
658 "int" => field.get_value::<i32>().ok().map(|value| value.to_string()),
659 "uint" => field.get_value::<u32>().ok().map(|value| value.to_string()),
660 "long" => field.get_value::<i64>().ok().map(|value| value.to_string()),
661 "ulong" => field.get_value::<u64>().ok().map(|value| value.to_string()),
662 _ => None,
663 }
664 }
665 }
666}