il2cpp_bridge_rs/structs/core/members/
method.rs1use crate::api::{self, cache, invoke_method};
3use crate::structs::collections::Il2cppArray;
4use crate::structs::core::{Class, Object, Type};
5use std::ffi::c_void;
6use std::ptr;
7
8#[derive(Debug, Clone)]
10pub struct Arg {
11 pub name: String,
13 pub type_info: Type,
15}
16
17#[derive(Debug, Clone)]
19pub struct Method {
20 pub address: *mut c_void,
22 pub token: u32,
24 pub name: String,
26 pub class: Option<*const Class>,
28 pub return_type: Type,
30 pub flags: i32,
32 pub is_static: bool,
34 pub function: *mut c_void,
36 pub rva: u64,
38 pub va: u64,
40 pub args: Vec<Arg>,
42 pub is_generic: bool,
44 pub is_inflated: bool,
46 pub is_instance: bool,
48 pub param_count: u8,
50 pub declaring_type: *mut c_void,
52 pub instance: Option<*mut c_void>,
54}
55
56unsafe impl Send for Method {}
57unsafe impl Sync for Method {}
58
59impl std::fmt::Display for Method {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 write!(f, "{}", self.fmt_method())
62 }
63}
64
65impl Method {
67 fn fmt_method(&self) -> String {
72 let access = self.get_attribute();
73 let flags = self.flags;
74
75 let is_abstract = (flags & api::METHOD_ATTRIBUTE_ABSTRACT) != 0;
76 let is_virtual = (flags & api::METHOD_ATTRIBUTE_VIRTUAL) != 0;
77 let is_final = (flags & api::METHOD_ATTRIBUTE_FINAL) != 0;
78
79 let qualifier = if self.is_static {
80 "static "
81 } else if is_abstract {
82 "abstract "
83 } else if is_virtual && !is_final {
84 "virtual "
85 } else {
86 ""
87 };
88
89 let args_str = self
90 .args
91 .iter()
92 .map(|arg| format!("{} {}", arg.type_info.cpp_name(), arg.name))
93 .collect::<Vec<_>>()
94 .join(", ");
95
96 let rva_comment = if self.rva == 0 {
97 "// RVA: -1 Offset: -1 VA: -1".to_string()
98 } else {
99 format!(
100 "// RVA: 0x{:X} Offset: 0x{:X} VA: 0x{:X}",
101 self.rva, self.rva, self.va
102 )
103 };
104
105 format!(
106 "{}\n{} {}{} {}({}) {{ }}",
107 rva_comment,
108 access,
109 qualifier,
110 self.return_type.cpp_name(),
111 self.name,
112 args_str,
113 )
114 }
115
116 pub unsafe fn call<T: Copy>(&self, params: &[*mut c_void]) -> Result<T, String> {
130 let instance = if self.is_static {
131 ptr::null_mut()
132 } else {
133 match self.instance {
134 Some(inst) => inst,
135 None => {
136 return Err(format!(
137 "Method '{}' is not static but no instance was provided. Use Object::method or set the instance manually.",
138 self.name
139 ));
140 }
141 }
142 };
143
144 if params.len() != self.args.len() {
145 return Err(format!(
146 "Argument count mismatch: expected {}, got {}",
147 self.args.len(),
148 params.len()
149 ));
150 }
151
152 let params_ptr = if params.is_empty() {
153 ptr::null()
154 } else {
155 params.as_ptr()
156 };
157
158 let result = invoke_method(self.address, instance, params_ptr)?;
159
160 if std::mem::size_of::<T>() == 0 {
161 return Ok(std::mem::zeroed());
162 }
163
164 let return_class = api::class_from_type(self.return_type.address);
165 if return_class.is_null() {
166 if std::mem::size_of::<T>() != std::mem::size_of::<*mut c_void>() {
167 return Err(format!(
168 "Method '{}' returns an unmanaged pointer-sized value, but caller requested {} bytes",
169 self.name,
170 std::mem::size_of::<T>()
171 ));
172 }
173 return Ok(std::mem::transmute_copy(&result));
174 }
175
176 if api::class_is_valuetype(return_class) {
177 if result.is_null() {
178 return Err(format!(
179 "Method '{}' returned null for value type '{}'",
180 self.name, self.return_type.name
181 ));
182 }
183
184 let unboxed = api::object_unbox(result);
185 if unboxed.is_null() {
186 return Err(format!(
187 "Method '{}' returned a non-unboxable value for '{}'",
188 self.name, self.return_type.name
189 ));
190 }
191
192 let expected_size = if self.return_type.size > 0 {
193 self.return_type.size as usize
194 } else {
195 api::class_value_size(return_class, ptr::null_mut()) as usize
196 };
197
198 if std::mem::size_of::<T>() != expected_size {
199 return Err(format!(
200 "Method '{}' returns '{}' ({} bytes), but caller requested {} bytes",
201 self.name,
202 self.return_type.name,
203 expected_size,
204 std::mem::size_of::<T>()
205 ));
206 }
207
208 let mut value = std::mem::MaybeUninit::<T>::uninit();
209 ptr::copy_nonoverlapping(
210 unboxed as *const u8,
211 value.as_mut_ptr() as *mut u8,
212 expected_size,
213 );
214 Ok(value.assume_init())
215 } else {
216 if std::mem::size_of::<T>() != std::mem::size_of::<*mut c_void>() {
217 return Err(format!(
218 "Method '{}' returns a reference type, but caller requested {} bytes",
219 self.name,
220 std::mem::size_of::<T>()
221 ));
222 }
223 Ok(std::mem::transmute_copy(&result))
224 }
225 }
226
227 pub fn get_attribute(&self) -> &'static str {
229 match self.flags & api::METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK {
230 api::METHOD_ATTRIBUTE_PRIVATE => "private",
231 api::METHOD_ATTRIBUTE_PUBLIC => "public",
232 api::METHOD_ATTRIBUTE_FAMILY => "protected",
233 api::METHOD_ATTRIBUTE_ASSEM => "internal",
234 api::METHOD_ATTRIBUTE_FAM_AND_ASSEM => "private protected",
235 api::METHOD_ATTRIBUTE_FAM_OR_ASSEM => "protected internal",
236 _ => "private",
237 }
238 }
239
240 pub fn get_object(&self) -> *mut c_void {
245 unsafe { api::method_get_object(self.address, ptr::null_mut()) }
246 }
247
248 pub fn inflate(&self, classes: &[&Class]) -> Result<Method, String> {
256 unsafe {
257 if !self.is_generic {
258 return Err(format!(
259 "Method '{}' is not a generic method definition",
260 self.name
261 ));
262 }
263
264 if classes.is_empty() {
265 return Err("No type arguments provided".to_string());
266 }
267
268 let mut type_args = Vec::with_capacity(classes.len());
269 for (i, cls) in classes.iter().enumerate() {
270 if cls.object.is_null() {
271 return Err(format!(
272 "Class '{}' (arg {}) has no Type object",
273 cls.name, i
274 ));
275 }
276 type_args.push(cls.object);
277 }
278
279 let method_object = self.get_object();
280 if method_object.is_null() {
281 return Err(format!(
282 "Could not get MethodInfo object for method '{}'",
283 self.name
284 ));
285 }
286
287 let method_obj = Object::from_ptr(method_object);
288
289 let corlib = cache::mscorlib();
290 let type_class = corlib
291 .class("System.Type")
292 .ok_or_else(|| "Could not find System.Type class".to_string())?;
293
294 let type_array = Il2cppArray::<*mut c_void>::new(&type_class, type_args.len());
295 if type_array.is_null() {
296 return Err("Could not create Type[] array".to_string());
297 }
298
299 let array_ref = &mut *type_array;
300 for (i, &type_arg) in type_args.iter().enumerate() {
301 array_ref.set(i, type_arg);
302 }
303
304 let make_generic_method = method_obj
305 .method(("MakeGenericMethod", ["System.Type[]"]))
306 .ok_or_else(|| "Could not find MakeGenericMethod(Type[]) method".to_string())?;
307
308 let inflated_method_obj =
309 make_generic_method.call::<*mut c_void>(&[type_array as *mut c_void])?;
310
311 if inflated_method_obj.is_null() {
312 return Err("MakeGenericMethod returned null".to_string());
313 }
314
315 let inflated_obj = Object::from_ptr(inflated_method_obj);
316
317 let mhandle_field = inflated_obj
318 .field("mhandle")
319 .unwrap()
320 .get_value::<*mut c_void>()
321 .map_err(|e| format!("Could not read mhandle field: {}", e))?;
322
323 if mhandle_field.is_null() {
324 return Err("mhandle field is null".to_string());
325 }
326
327 cache::method_from_ptr(mhandle_field).ok_or_else(|| {
328 "Could not convert inflated method pointer to Method struct".to_string()
329 })
330 }
331 }
332}