unity/il2cpp/
object.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
use std::ops::{Deref, DerefMut};

use crate::{Il2CppResult, Il2CppError};

use super::{api, class::{Il2CppClass, Il2CppClassData}};

/// A type alias for `Il2CppObject<Array<T>>`.
pub type Il2CppArray<T> = Array<T>;

/// Wrapper structure for a class instance provided by Il2Cpp.
/// 
/// It contains a pointer to the class it represents as well as its own copy of the fields in the class.  
/// Every class instance is represented by this type and passed by reference.
/// 
/// Define the fields of the class as a structure and pass them as a generic.
/// 
/// Example:
///
/// ```
/// pub fn hooked_method(proc: &Il2CppObject<ProcInst>) {
/// // ...
/// }
/// ```
#[repr(C)]
pub struct Il2CppObject<T> {
    /// The class this instance refers to.
    /// Be aware that editing it means editing every other object using the same class.  
    /// Use carefully.
    pub klass: &'static mut Il2CppClass,
    monitor: *const u8,
    pub fields: T,
}

impl<T> Deref for Il2CppObject<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.fields
    }
}

impl<T> DerefMut for Il2CppObject<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.fields
    }
}

impl<T> AsRef<T> for Il2CppObject<T> {
    fn as_ref(&self) -> &T {
        &self.fields
    }
}

impl<T> AsMut<T> for Il2CppObject<T> {
    fn as_mut(&mut self) -> &mut T {
        &mut self.fields
    }
}

impl<T> Il2CppObject<T> {
    pub fn get_class(&self) -> &Il2CppClass {
        self.klass
    }

    pub fn get_class_mut(&mut self) -> &mut Il2CppClass {
        self.klass
    }

    /// Create a unique [`Il2CppObject`] instance of the [`Il2CppClass`](crate::il2cpp::class::Il2CppClass) provided.
    pub fn from_class(class: &Il2CppClass) -> Il2CppResult<&'static mut T> {
        unsafe { api::object_new(class) }.ok_or(Il2CppError::FailedInstantiation(class.get_name()))
    }
}

#[repr(C)]
#[derive(Clone, Copy)]
struct Il2CppArrayBounds {
    length: usize,
    lower_bounds: u32,
}

#[crate::class("System", "Array")]
pub struct Array<T> {
    bounds: &'static Il2CppArrayBounds,
    pub max_length: usize,
    pub m_items: [T; 0],
}

impl<T> Deref for ArrayFields<T> {
    type Target = [T];

    fn deref(&self) -> &Self::Target {
        unsafe { std::slice::from_raw_parts(self.m_items.as_ptr(), self.max_length) }
    }
}

impl<T> DerefMut for ArrayFields<T> {
    fn deref_mut(&mut self) -> &mut [T] {
        unsafe { std::slice::from_raw_parts_mut(self.m_items.as_mut_ptr(), self.max_length) }
    }
}

/// Trait to abstract away the new methods for value types vs reference ones.
pub trait ArrayInstantiator<T> {
    /// Create an empty Il2CppArray capable of holding the provided amount of entries.
    /// 
    /// Arguments:
    ///
    /// * `class`: The class of the elements that are going to be stored.
    /// * `capacity`: The maximum amount of element that can be stored.
    /// 
    /// Example:
    /// 
    /// ```
    /// let new_array: Il2CppArray<u8> = Il2CppArray::<u8>::new(SystemByte::get_class(), 69).unwrap();
    /// ```
    fn new(capacity: usize) -> Il2CppResult<&'static mut Self>;

    /// Create a new Il2CppArray by copying the content of a slice into it.
    /// 
    /// Arguments:
    ///
    /// * `class`: The class of the elements that are going to be stored.
    /// * `slice`: The slice that'll be copied into the Il2CppArray.
    /// 
    /// Example:
    /// 
    /// ```
    /// let mut slice: &mut [u8] = &[0x1, 0x2, 0x3];
    /// let new_array: Il2CppArray<u8> = Il2CppArray::<u8>::new_from(SystemByte::get_class(), slice).unwrap();
    /// ```
    /// 
    /// Note that this method takes ownership of the slice, so you won't be able to use it afterwards.
    fn from_slice(slice: impl AsMut<[T]>) -> Il2CppResult<&'static mut Self>;
}

impl ArrayInstantiator<u8> for Array<u8> {
    /// Create an empty Il2CppArray capable of holding the provided amount of entries.
    /// 
    /// Arguments:
    ///
    /// * `class`: The class of the elements that are going to be stored.
    /// * `capacity`: The maximum amount of element that can be stored.
    /// 
    /// Example:
    /// 
    /// ```
    /// let new_array: Il2CppArray<u8> = Il2CppArray::<u8>::new(SystemByte::get_class(), 69).unwrap();
    /// ```
    fn new(capacity: usize) -> Il2CppResult<&'static mut Self> {
        array_new(u8::class(), capacity)
    }

    /// Create a new Il2CppArray by copying the content of a slice into it.
    /// 
    /// Arguments:
    ///
    /// * `class`: The class of the elements that are going to be stored.
    /// * `slice`: The slice that'll be copied into the Il2CppArray.
    /// 
    /// Example:
    /// 
    /// ```
    /// let mut slice: &mut [u8] = &[0x1, 0x2, 0x3];
    /// let new_array: Il2CppArray<u8> = Il2CppArray::<u8>::new_from(SystemByte::get_class(), slice).unwrap();
    /// ```
    /// 
    /// Note that this method takes ownership of the slice, so you won't be able to use it afterwards.
    fn from_slice(mut slice: impl AsMut<[u8]>) -> Il2CppResult<&'static mut Self> {
        let new_array = array_new(u8::class(), slice.as_mut().len())?;
        new_array.swap_with_slice(slice.as_mut());
        Ok(new_array)
    }
}

impl<'a, T: Il2CppClassData> ArrayInstantiator<&'a mut T> for Array<&'a mut T> {
    /// Create an empty Il2CppArray capable of holding the provided amount of entries.
    /// 
    /// Arguments:
    ///
    /// * `class`: The class of the elements that are going to be stored.
    /// * `capacity`: The maximum amount of element that can be stored.
    /// 
    /// Example:
    /// 
    /// ```
    /// let new_array: Il2CppArray<u8> = Il2CppArray::<u8>::new(SystemByte::get_class(), 69).unwrap();
    /// ```
    fn new(capacity: usize) -> Il2CppResult<&'static mut Self> {
        array_new(T::class(), capacity)
    }

    /// Create a new Il2CppArray by copying the content of a slice into it.
    /// 
    /// Arguments:
    ///
    /// * `class`: The class of the elements that are going to be stored.
    /// * `slice`: The slice that'll be copied into the Il2CppArray.
    /// 
    /// Example:
    /// 
    /// ```
    /// let mut slice: &mut [u8] = &[0x1, 0x2, 0x3];
    /// let new_array: Il2CppArray<u8> = Il2CppArray::<u8>::new_from(SystemByte::get_class(), slice).unwrap();
    /// ```
    /// 
    /// Note that this method takes ownership of the slice, so you won't be able to use it afterwards.
    fn from_slice(mut slice: impl AsMut<[&'a mut T]>) -> Il2CppResult<&'static mut Self> {
        let new_array = array_new(T::class(), slice.as_mut().len())?;
        new_array.swap_with_slice(slice.as_mut());
        Ok(new_array)
    }
}

impl<'a, T: Il2CppClassData> ArrayInstantiator<Option<&'a mut T>> for Array<Option<&'a mut T>> {
    /// Create an empty Il2CppArray capable of holding the provided amount of entries.
    /// 
    /// Arguments:
    ///
    /// * `class`: The class of the elements that are going to be stored.
    /// * `capacity`: The maximum amount of element that can be stored.
    /// 
    /// Example:
    /// 
    /// ```
    /// let new_array: Il2CppArray<u8> = Il2CppArray::<u8>::new(SystemByte::get_class(), 69).unwrap();
    /// ```
    fn new(capacity: usize) -> Il2CppResult<&'static mut Self> {
        array_new(T::class(), capacity)
    }

    /// Create a new Il2CppArray by copying the content of a slice into it.
    /// 
    /// Arguments:
    ///
    /// * `class`: The class of the elements that are going to be stored.
    /// * `slice`: The slice that'll be copied into the Il2CppArray.
    /// 
    /// Example:
    /// 
    /// ```
    /// let mut slice: &mut [u8] = &[0x1, 0x2, 0x3];
    /// let new_array: Il2CppArray<u8> = Il2CppArray::<u8>::new_from(SystemByte::get_class(), slice).unwrap();
    /// ```
    /// 
    /// Note that this method takes ownership of the slice, so you won't be able to use it afterwards.
    fn from_slice(mut slice: impl AsMut<[Option<&'a mut T>]>) -> Il2CppResult<&'static mut Self> {
        let new_array = array_new(T::class(), slice.as_mut().len())?;
        new_array.swap_with_slice(slice.as_mut());
        Ok(new_array)
    }
}

impl<T> Array<T> {
    pub fn new_specific(class: &Il2CppClass, capacity: usize) -> Il2CppResult<&'static mut Self> {
        array_new_specific(class, capacity)
    }

    pub fn new_from_token(token: u64, capacity: usize) -> Il2CppResult<&'static mut Self> {
        let temp = token;
        let fake_class = unsafe { &*(temp as *mut Il2CppClass) };
        array_new_specific(fake_class, capacity)
    }

    /// Takes a mutable slice and allocates a new Il2CppArray filled with its content.
    ///
    /// This is partially needed because we do not implement Clone on Il2CppObject.
    pub fn new_specific_from(class: &Il2CppClass, mut slice: impl AsMut<[T]>) -> Il2CppResult<&'static mut Self> {
        let new_array = array_new_specific(class, slice.as_mut().len())?;
        new_array.swap_with_slice(slice.as_mut());
        Ok(new_array)
    }

    /// Create a new Vec filled with the content from the Il2CppArray.
    ///
    /// This is partially needed because we do not implement Clone on Il2CppObject.
    pub fn to_vec(&mut self) -> Vec<T> {
        let mut vec = Vec::with_capacity(self.len());
        unsafe { vec.set_len(self.len()) }
        // TODO: Try to improve this with a memcpy, since we have the same size
        self.swap_with_slice(vec.as_mut());
        vec
    }
}

fn array_new_specific<T>(array_typeinfo: &Il2CppClass, length: usize) -> Il2CppResult<&'static mut Il2CppArray<T>> {
    unsafe { api::array_new_specific(array_typeinfo, length) }.ok_or(Il2CppError::FailedArrayInstantiation)
}

fn array_new<T>(element_typeinfo: &Il2CppClass, length: usize) -> Il2CppResult<&'static mut Il2CppArray<T>> {
    unsafe { api::array_new(element_typeinfo, length) }.ok_or(Il2CppError::FailedArrayInstantiation)
}