il2cpp_bridge_rs/structs/core/hierarchy/
object.rs1use super::class::MethodSelector;
3use crate::api::{self, cache};
4use crate::structs::components::GameObject;
5use crate::structs::core::{Class, Field, Method, Property};
6use crate::structs::Il2cppString;
7use std::ffi::c_void;
8
9#[repr(C)]
11#[derive(Debug, Clone, Copy)]
12pub struct Il2cppObject {
13 pub klass: *mut c_void,
15 pub monitor: *mut c_void,
17}
18
19#[repr(transparent)]
24#[derive(Debug, Clone, Copy)]
25pub struct Object {
26 pub ptr: *mut Il2cppObject,
28}
29
30impl Object {
31 pub unsafe fn from_ptr(ptr: *mut c_void) -> Self {
36 Self {
37 ptr: ptr as *mut Il2cppObject,
38 }
39 }
40
41 pub fn as_ptr(&self) -> *mut c_void {
46 self.ptr as *mut c_void
47 }
48
49 pub fn field(&self, name: &str) -> Option<Field> {
55 let class_ptr = unsafe { api::object_get_class(self.as_ptr()) };
56
57 if class_ptr.is_null() {
58 return None;
59 }
60
61 match cache::class_from_ptr(class_ptr) {
62 Some(class) => match class.field(name) {
63 Some(mut field) => {
64 field.instance = Some(self.as_ptr());
65 Some(field)
66 }
67 None => None,
68 },
69 None => None,
70 }
71 }
72
73 pub fn method<S: MethodSelector>(&self, selector: S) -> Option<Method> {
78 unsafe {
79 let class_ptr = api::object_get_class(self.as_ptr());
80 if class_ptr.is_null() {
81 return None;
82 }
83 cache::class_from_ptr(class_ptr).and_then(|class| {
84 class.method(selector).map(|mut method| {
85 method.instance = Some(self.as_ptr());
86 method
87 })
88 })
89 }
90 }
91
92 pub fn property(&self, name: &str) -> Option<Property> {
94 let class_ptr = unsafe { api::object_get_class(self.as_ptr()) };
95
96 if class_ptr.is_null() {
97 return None;
98 }
99
100 cache::class_from_ptr(class_ptr).and_then(|class| {
101 class
102 .property(name)
103 .map(|prop| prop.with_instance(self.as_ptr()))
104 })
105 }
106
107 pub fn il2cpp_to_string(&self) -> String {
112 unsafe {
113 if let Some(method) = self.method("ToString") {
114 if let Ok(result) = method.call::<*mut Il2cppString>(&[]) {
115 if !result.is_null() {
116 return (*result).to_string().unwrap_or_else(|| "null".to_string());
117 }
118 }
119 }
120 "null".to_string()
121 }
122 }
123
124 pub fn get_game_object(&self) -> Result<GameObject, String> {
129 unsafe {
130 let method = self
131 .method("get_gameObject")
132 .ok_or("Method 'get_gameObject' not found")?;
133 let result = method.call::<*mut c_void>(&[])?;
134
135 if result.is_null() {
136 return Err("GameObject is null".to_string());
137 }
138
139 Ok(GameObject::from_ptr(result))
140 }
141 }
142
143 pub fn get_header_size() -> usize {
150 use std::sync::OnceLock;
151 static HEADER_SIZE: OnceLock<usize> = OnceLock::new();
152
153 *HEADER_SIZE.get_or_init(|| unsafe {
154 let system_object_class = cache::mscorlib().class("System.Object");
155
156 if let Some(class) = system_object_class {
157 let size = api::class_instance_size(class.address);
158 if size > 0 {
159 return size as usize;
160 }
161 }
162
163 std::mem::size_of::<Il2cppObject>()
164 })
165 }
166
167 pub fn get_class(&self) -> Option<Class> {
172 let class_ptr = unsafe { api::object_get_class(self.as_ptr()) };
173 cache::class_from_ptr(class_ptr)
174 }
175
176 pub fn get_virtual_method(&self, method: &Method) -> *mut c_void {
184 unsafe { api::object_get_virtual_method(self.as_ptr(), method.address) }
185 }
186
187 pub fn init_exception(&self, exc: &mut c_void) {
192 unsafe { api::runtime_object_init_exception(self.as_ptr(), exc) }
193 }
194
195 pub fn get_size(&self) -> u32 {
200 unsafe { api::object_get_size(self.as_ptr()) }
201 }
202
203 pub fn unbox(&self) -> *mut c_void {
208 unsafe { api::object_unbox(self.as_ptr()) }
209 }
210}