use super::class::MethodSelector;
use crate::api::{self, cache};
use crate::structs::components::GameObject;
use crate::structs::core::{Class, Field, Method, Property};
use crate::structs::Il2cppString;
use std::ffi::c_void;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Il2cppObject {
pub klass: *mut c_void,
pub monitor: *mut c_void,
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct Object {
pub ptr: *mut Il2cppObject,
}
impl Object {
pub unsafe fn from_ptr(ptr: *mut c_void) -> Self {
Self {
ptr: ptr as *mut Il2cppObject,
}
}
pub fn as_ptr(&self) -> *mut c_void {
self.ptr as *mut c_void
}
pub fn field(&self, name: &str) -> Option<Field> {
let class_ptr = unsafe { api::object_get_class(self.as_ptr()) };
if class_ptr.is_null() {
return None;
}
match cache::class_from_ptr(class_ptr) {
Some(class) => match class.field(name) {
Some(mut field) => {
field.instance = Some(self.as_ptr());
Some(field)
}
None => None,
},
None => None,
}
}
pub fn method<S: MethodSelector>(&self, selector: S) -> Option<Method> {
unsafe {
let class_ptr = api::object_get_class(self.as_ptr());
if class_ptr.is_null() {
return None;
}
cache::class_from_ptr(class_ptr).and_then(|class| {
class.method(selector).map(|mut method| {
method.instance = Some(self.as_ptr());
method
})
})
}
}
pub fn property(&self, name: &str) -> Option<Property> {
let class_ptr = unsafe { api::object_get_class(self.as_ptr()) };
if class_ptr.is_null() {
return None;
}
cache::class_from_ptr(class_ptr).and_then(|class| {
class
.property(name)
.map(|prop| prop.with_instance(self.as_ptr()))
})
}
pub fn il2cpp_to_string(&self) -> String {
unsafe {
if let Some(method) = self.method("ToString") {
if let Ok(result) = method.call::<*mut Il2cppString>(&[]) {
if !result.is_null() {
return (*result).to_string().unwrap_or_else(|| "null".to_string());
}
}
}
"null".to_string()
}
}
pub fn get_game_object(&self) -> Result<GameObject, String> {
unsafe {
let method = self
.method("get_gameObject")
.ok_or("Method 'get_gameObject' not found")?;
let result = method.call::<*mut c_void>(&[])?;
if result.is_null() {
return Err("GameObject is null".to_string());
}
Ok(GameObject::from_ptr(result))
}
}
pub fn get_header_size() -> usize {
use std::sync::OnceLock;
static HEADER_SIZE: OnceLock<usize> = OnceLock::new();
*HEADER_SIZE.get_or_init(|| unsafe {
let system_object_class = cache::mscorlib().class("System.Object");
if let Some(class) = system_object_class {
let size = api::class_instance_size(class.address);
if size > 0 {
return size as usize;
}
}
std::mem::size_of::<Il2cppObject>()
})
}
pub fn get_class(&self) -> Option<Class> {
let class_ptr = unsafe { api::object_get_class(self.as_ptr()) };
cache::class_from_ptr(class_ptr)
}
pub fn get_virtual_method(&self, method: &Method) -> *mut c_void {
unsafe { api::object_get_virtual_method(self.as_ptr(), method.address) }
}
pub fn init_exception(&self, exc: &mut c_void) {
unsafe { api::runtime_object_init_exception(self.as_ptr(), exc) }
}
pub fn get_size(&self) -> u32 {
unsafe { api::object_get_size(self.as_ptr()) }
}
pub fn unbox(&self) -> *mut c_void {
unsafe { api::object_unbox(self.as_ptr()) }
}
}