Skip to content

Commit c9be914

Browse files
authored
Added Generic Method Macro, Unity Class Method Macro, and delegate classes (#4)
* Added useful functions for Il2CppClass * Filling out fields in Il2CppClass2 * Functions to get class in the hierarchy * macro to quickly generate Il2CppClassData for common types * Added unity class_method, get_generic_method macros and added some delegate classes. * Added unity class_method, get_generic_method macros and added some delegate classes. * fixed pathing for create_generic_method_info * using skyline from_offset instead of unity
1 parent 4fa8b74 commit c9be914

10 files changed

Lines changed: 454 additions & 28 deletions

File tree

src/il2cpp/class.rs

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use super::{
88
Il2CppType,
99
};
1010
use crate::{Il2CppResult, Il2CppError, system::{SystemType, runtime_type_make_generic_type}};
11+
use crate::il2cpp::generic::{Il2CppGenericContainer, Il2CppGenericContext};
1112

1213
#[repr(C)]
1314
pub struct Il2CppClass1 {
@@ -33,7 +34,9 @@ pub struct Il2CppClass1 {
3334

3435
#[repr(C)]
3536
pub struct Il2CppClass2 {
36-
_2_start: [u8; 0x30],
37+
pub type_hierarchy: *const &'static Il2CppClass,
38+
_2_start: [u8; 0x20],
39+
pub generic_handle: Option<&'static Il2CppGenericContainer>,
3740
pub instance_size: u32,
3841
pub actual_size: u32,
3942
__: [u8; 0x18],
@@ -128,9 +131,9 @@ pub struct Il2CppRGCTXData {
128131

129132
#[repr(C)]
130133
pub struct Il2CppGenericClass {
131-
type_definition_idx: i32,
132-
class_inst: *const u8,
133-
method_inst: *const u8,
134+
// type_definition_idx: i32,
135+
pub ty: &'static Il2CppType, // Union of Il2CppType / TypeDefinitionIndex
136+
pub context: Il2CppGenericContext,
134137
pub cached_class: *const Il2CppClass,
135138
}
136139

@@ -144,6 +147,9 @@ impl Il2CppClass {
144147
pub fn from_name(namespace: impl AsRef<str>, name: impl AsRef<str>) -> Il2CppResult<&'static mut Self> {
145148
get_class_from_name(namespace, name)
146149
}
150+
pub fn get_system_type(&self) -> &'static SystemType {
151+
unsafe { reflection_get_type_object(self.get_type()) }
152+
}
147153

148154
pub fn from_il2cpptype(ty: &Il2CppType) -> Il2CppResult<&'static mut Self> {
149155
class_from_il2cpptype(ty)
@@ -250,6 +256,21 @@ impl Il2CppClass {
250256

251257
unsafe { api::get_method_from_name_flags(self, name.as_ptr() as _, args_count, flag) }.ok_or(Il2CppError::MissingMethod)
252258
}
259+
pub fn get_class_hierarchy(&self) -> &[&'static Il2CppClass] {
260+
unsafe { std::slice::from_raw_parts(self._2.type_hierarchy, (self._2.type_hierarchy_depth) as _ ) }
261+
}
262+
263+
pub fn find_class_in_hierarchy(&self, class: &Il2CppClass) -> Option<&'static Il2CppClass>{
264+
self.find_class_in_hierarchy_by_name(class.get_namespace().as_str(), class.get_name().as_str())
265+
}
266+
pub fn find_class_in_hierarchy_by_name(&self, namespace: impl AsRef<str>, name: impl AsRef<str>) -> Option<&'static Il2CppClass> {
267+
self.get_class_hierarchy().iter()
268+
.find(|klass| klass.get_name() == name.as_ref() && klass.get_namespace() == namespace.as_ref())
269+
.map(|v| *v)
270+
}
271+
pub fn instantiate_as<T: 'static>(&self) -> Il2CppResult<&'static mut T> {
272+
super::instantiate_class(self)
273+
}
253274
}
254275

255276
#[repr(C)]
@@ -370,24 +391,33 @@ pub trait Il2CppClassData {
370391
super::instantiate_class(Self::class())
371392
}
372393
}
373-
374-
impl Il2CppClassData for u8 {
375-
const NAMESPACE: &'static str = "System";
376-
const CLASS: &'static str = "Byte";
377-
378-
fn class() -> &'static Il2CppClass {
379-
static CLASS_TYPE: std::sync::LazyLock<&'static mut Il2CppClass> = std::sync::LazyLock::new(|| {
380-
Il2CppClass::from_name("System", "Byte")
381-
.expect(&format!("Failed to find class {}.{}", "System", "Byte"))
382-
});
383-
384-
&CLASS_TYPE
385-
}
386-
387-
fn class_mut() -> &'static mut Il2CppClass {
388-
Self::class().clone()
394+
macro_rules! make_class_data {
395+
($name:ty, $namespace:expr, $class:expr) => {
396+
impl Il2CppClassData for $name {
397+
const NAMESPACE: &'static str = $namespace ;
398+
const CLASS: &'static str = $class ;
399+
400+
fn class() -> &'static Il2CppClass {
401+
static CLASS_TYPE: std::sync::LazyLock<&'static mut Il2CppClass> = std::sync::LazyLock::new(|| {
402+
Il2CppClass::from_name($namespace, $class)
403+
.expect(&format!("Failed to find class {}.{}", $namespace, $class))
404+
});
405+
&CLASS_TYPE
406+
}
407+
fn class_mut() -> &'static mut Il2CppClass { Self::class().clone()}
408+
}
389409
}
390410
}
411+
make_class_data!(u8, "System", "Byte");
412+
make_class_data!(i8, "System", "SByte");
413+
make_class_data!(i16, "System", "Int16");
414+
make_class_data!(u16, "System", "UInt16");
415+
make_class_data!(i32, "System", "Int32");
416+
make_class_data!(u32, "System", "UInt32");
417+
make_class_data!(i64, "System", "Int64");
418+
make_class_data!(u64, "System", "UInt64");
419+
make_class_data!(f32, "System", "Single");
420+
make_class_data!(f64, "System", "Double");
391421

392422
/// input: `SomeClass<Arg1, Arg2, ...>`
393423
#[macro_export]
@@ -402,3 +432,6 @@ macro_rules! get_generic_class {
402432

403433
#[skyline::from_offset(0x4503c0)]
404434
pub fn setup_gc_descriptor(class: &Il2CppClass);
435+
436+
#[skyline::from_offset(0x444dc8)]
437+
fn reflection_get_type_object(ty: &Il2CppType) -> &'static SystemType;

src/il2cpp/generic.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use crate::il2cpp::Il2CppType;
2+
use crate::macro_context::MethodInfo;
3+
4+
#[repr(C)]
5+
#[derive(Clone, Copy)]
6+
pub union GenericMethodInfo {
7+
pub generic_method: &'static Il2CppGenericMethod,
8+
pub generic_container: Il2CppGenericContainer,
9+
}
10+
#[repr(C)]
11+
pub struct Il2CppGenericMethod {
12+
pub method_definition: &'static mut MethodInfo,
13+
pub generic_context: Il2CppGenericContext,
14+
}
15+
16+
#[repr(C)]
17+
pub struct Il2CppGenericContext {
18+
pub class_inst: Option<&'static Il2CppGenericInst>,
19+
pub method_inst: Option<&'static Il2CppGenericInst>,
20+
}
21+
22+
#[repr(C)]
23+
pub struct Il2CppGenericInst {
24+
pub type_argc: u32,
25+
pub type_argv: *const &'static Il2CppType,
26+
}
27+
impl Il2CppGenericInst {
28+
pub fn get_types(&self) -> &[&'static Il2CppType] {
29+
unsafe { std::slice::from_raw_parts(self.type_argv, self.type_argc as _) }
30+
}
31+
}
32+
#[repr(C)]
33+
#[derive(Clone, Copy)]
34+
pub struct Il2CppGenericContainer {
35+
pub owner: i32,
36+
pub type_argc: i32,
37+
pub is_method: i32,
38+
pub generic_parameter_start: i32,
39+
}
40+
41+
pub fn create_generic_method_info(method_info: &MethodInfo, types: &[&Il2CppType]) -> &'static MethodInfo {
42+
let len = types.len();
43+
let ty = types.as_ptr();
44+
let generic_method_inst = unsafe { create_generic_inst(ty, len) };
45+
let generic_class_inst: Option<&Il2CppGenericInst> =
46+
if method_info.bitflags & 2 == 0 { None }
47+
else {
48+
method_info.class.and_then(|klass|klass._1.generic_class)
49+
.and_then(|generic_class| generic_class.context.class_inst)
50+
};
51+
let generic_method = unsafe { create_generic_method(method_info, generic_class_inst, Some(generic_method_inst)) };
52+
let method = unsafe { generic_method_create_method_info(generic_method, 0) };
53+
method
54+
}
55+
56+
57+
/// Macro to generate an instantiated generic MethodInfo
58+
/// input: `MethodInfo<Class1, Class2, ...>`
59+
#[macro_export]
60+
macro_rules! get_generic_method {
61+
($method:ident<$($ty:ident),+>) => {
62+
unity::il2cpp::generic::create_generic_method_info($method, &[$($ty::class().get_type()),+])
63+
};
64+
}
65+
66+
#[skyline::from_offset(0x43e2bc)]
67+
fn create_generic_inst(types: *const &Il2CppType, len: usize) -> &'static Il2CppGenericInst;
68+
69+
#[skyline::from_offset(0x47c0d4)]
70+
fn generic_method_create_method_info(generic_method: &Il2CppGenericMethod, flags: i32) -> &'static MethodInfo;
71+
72+
#[skyline::from_offset(0x439a48)]
73+
fn create_generic_method(
74+
method_definition: &MethodInfo,
75+
generic_class_inst: Option<&Il2CppGenericInst>,
76+
generic_method_inst: Option<&Il2CppGenericInst>
77+
) -> &'static Il2CppGenericMethod;

src/il2cpp/method.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,4 @@ impl ParameterInfo {
9898
}
9999
}
100100
}
101+

src/il2cpp/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use method::*;
1414

1515
use crate::{Il2CppResult, Il2CppError};
1616
mod ffi;
17+
pub mod generic;
1718

1819
pub fn method_from_name(name: impl AsRef<str>) -> *const u8 {
1920
let name = std::ffi::CString::new(name.as_ref()).unwrap();

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub mod prelude {
4747
4848
pub use crate::{
4949
get_generic_class,
50+
get_generic_method,
5051
il2cpp,
5152
Il2CppResult,
5253
Il2CppError,

src/system.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use crate::prelude::{Il2CppArray, Il2CppClassData, MethodInfo};
22
use std::{ops::{Deref, DerefMut}};
33

44
pub mod string;
5+
pub mod action;
6+
57
pub use string::Il2CppString;
68

79
#[repr(C)]

src/system/action.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
use std::marker::PhantomData;
2+
use crate::il2cpp::object::Array;
3+
use crate::macro_context::MethodInfo;
4+
use crate::prelude::{Il2CppClass, Il2CppClassData, OptionalMethod};
5+
6+
#[crate::class("System", "Delegate")]
7+
pub struct Delegate<T: Sized + 'static> {
8+
pub method_ptr: *const u8,
9+
invoke_impl: *const u8,
10+
target: Option<&'static T>,
11+
method: *const MethodInfo,
12+
__: [u8; 0x38],
13+
}
14+
15+
#[crate::class("System", "MulticastDelegate")]
16+
pub struct MulticastDelegate<T: Sized + 'static> {
17+
pub parent: DelegateFields<T>,
18+
pub delegates: &'static Array<&'static mut Delegate<T>>,
19+
}
20+
21+
/// Action that takes no arguments
22+
/// Performs methods of fn(&Obj)
23+
#[crate::class("System", "Action")]
24+
pub struct Action{
25+
pub method_ptr: *const u8,
26+
invoke_impl: *const u8,
27+
pub target_obj: *const u8, // Reference to Obj
28+
pub method: Option<&'static MethodInfo>,
29+
}
30+
31+
impl Action{
32+
pub fn new<T: Il2CppClassData>(target: Option<&T>, method: fn(&T, OptionalMethod)) -> &'static mut Self {
33+
let mut method_info = MethodInfo::new();
34+
method_info.method_ptr = method as _;
35+
method_info.parameters_count = 0;
36+
let action = Action::instantiate().unwrap();
37+
action.ctor(target, &method_info);
38+
action
39+
}
40+
pub fn new_from_method_info<T>(target: Option<&T>, method_info: &MethodInfo) -> &'static mut Self {
41+
let action = Action::instantiate().unwrap();
42+
action.ctor(target, method_info);
43+
action
44+
}
45+
#[crate::class_method(1)] pub fn invoke(&self); // Offset: 0x33F42B0 Flags: 0
46+
}
47+
48+
/// Action that makes one argument
49+
/// Performs method of fn(&Obj, Arg)
50+
#[crate::class("System", "Action`1")]
51+
pub struct Action1<A>{
52+
pub method_ptr: *const u8,
53+
invoke_impl: *const u8,
54+
pub target_obj: *const u8, // Reference to Obj
55+
method: *const MethodInfo,
56+
__: [u8; 0x38],
57+
// delegates: &'static Array<&'static mut Delegate<T>>,
58+
phantom: PhantomData<A>
59+
}
60+
61+
impl<A> Action1<A>
62+
where
63+
A: Il2CppClassData + Sized,
64+
{
65+
pub fn new_with_method<T: Il2CppClassData>(object: Option<&T>, method: fn(&T, OptionalMethod)) -> &'static mut Self {
66+
let action_t_class = Il2CppClass::from_name("System", "Action`1").unwrap();
67+
let klass = crate::il2cpp::class::make_generic(action_t_class, &[A::class()])
68+
.expect(format!("Expect Action<{}>", A::class().get_name()).as_str());
69+
let action = klass.instantiate_as::<Action1<A>>().unwrap();
70+
let mut method_info = MethodInfo::new();
71+
method_info.method_ptr = method as _;
72+
method_info.parameters_count = 1;
73+
action.ctor(object, &method_info);
74+
action
75+
}
76+
pub fn new<T: Il2CppClassData>(object: Option<&T>, method_info: &MethodInfo) -> &'static mut Self {
77+
let action_t_class = Il2CppClass::from_name("System", "Action`1").unwrap();
78+
let klass = crate::il2cpp::class::make_generic(action_t_class, &[A::class()])
79+
.expect(format!("Expect Action<{}>", A::class().get_name()).as_str());
80+
81+
let action = klass.instantiate_as::<Action1<A>>().unwrap();
82+
action.ctor(object, method_info);
83+
action
84+
}
85+
pub fn invoke(&self, arg: A){
86+
let invoke = unsafe { std::mem::transmute::<_, fn(&Self, A, OptionalMethod)>(self.klass.get_methods()[1].method_ptr) };
87+
invoke(self, arg, None);
88+
}
89+
}
90+
/// Action that takes no arguments
91+
/// Performs methods of fn(&Obj) -> R
92+
#[crate::class("System", "Func`1")]
93+
pub struct Func<T: 'static , R>{
94+
pub method_ptr: *const u8,
95+
invoke_impl: *const u8,
96+
pub target: Option<&'static T>,
97+
method: *const MethodInfo,
98+
__: [u8; 0x38],
99+
delegates: &'static Array<&'static mut Delegate<T>>,
100+
phantom: PhantomData<R>,
101+
}
102+
impl<T, R> Func<T, R>
103+
where
104+
T: Il2CppClassData + Sized,
105+
R: Il2CppClassData + Sized + 'static,
106+
{
107+
pub fn new(object: Option<&T>, method_info: &MethodInfo) -> &'static mut Self {
108+
let func_r_class = Il2CppClass::from_name("System", "Func`1").unwrap();
109+
let klass = crate::il2cpp::class::make_generic(func_r_class, &[R::class()])
110+
.expect(format!("Expect Func<{}>", R::class().get_name()).as_str());
111+
112+
let action = klass.instantiate_as::<Func<T, R>>().unwrap();
113+
action.ctor(object, method_info);
114+
action
115+
}
116+
pub fn invoke(&self) -> R {
117+
let invoke = unsafe { std::mem::transmute::<_, fn(&Self, OptionalMethod) -> R >(self.klass.get_methods()[1].method_ptr) };
118+
invoke(self, None)
119+
}
120+
}
121+
122+
/// Action that takes no arguments
123+
/// Performs methods of fn(&Obj, A) -> R
124+
#[crate::class("System", "Func`2")]
125+
pub struct Func1<T: 'static, A, R>{
126+
pub method_ptr: *const u8,
127+
invoke_impl: *const u8,
128+
pub target: Option<&'static T>,
129+
method: *const MethodInfo,
130+
__: [u8; 0x38],
131+
delegates: &'static Array<&'static mut Delegate<T>>,
132+
phantom1: PhantomData<A>,
133+
phantom2: PhantomData<R>,
134+
}
135+
136+
/// Trait to use the .ctor for all delegate classes
137+
pub trait SystemDelegate: Il2CppClassData + Sized {
138+
fn ctor<T>(&self, object: Option<&T>, method_info: &MethodInfo) {
139+
unsafe { system_action_ctor(self, object, method_info); }
140+
}
141+
}
142+
impl<T> SystemDelegate for Delegate<T> {}
143+
impl<T> SystemDelegate for MulticastDelegate<T> {}
144+
impl SystemDelegate for Action {}
145+
impl<A> SystemDelegate for Action1<A> {}
146+
impl<T,R> SystemDelegate for Func<T,R> {}
147+
148+
#[skyline::from_offset(0x33f4290)]
149+
fn system_action_ctor<D, T>(this: &D, obj: Option<&T>, method_info: &MethodInfo)
150+
where D: SystemDelegate;

0 commit comments

Comments
 (0)