Skip to main content

il2cpp_bridge_rs/structs/collections/
array.rs

1//! IL2CPP Array definition and operations
2use crate::api;
3use crate::structs::core::Class;
4use std::ffi::c_void;
5use std::marker::PhantomData;
6
7#[repr(C)]
8pub struct ArrayBounds {
9    /// Length of the dimension
10    pub length: usize,
11    /// Lower bound of the dimension
12    pub lower_bound: i32,
13}
14
15#[repr(C)]
16pub struct Il2cppArray<T> {
17    /// Pointer to the array class
18    pub klass: *mut c_void,
19    /// Monitor for synchronization
20    pub monitor: *mut c_void,
21    /// Pointer to array bounds
22    pub bounds: *mut ArrayBounds,
23    /// Maximum length of the array
24    pub max_length: usize,
25    phantom: PhantomData<T>,
26}
27
28impl<T: Copy> Il2cppArray<T> {
29    /// Gets the address of the data array
30    ///
31    /// # Returns
32    /// * `usize` - The memory address where the array data begins
33    pub fn get_data(&self) -> usize {
34        let header_size = std::mem::size_of::<Self>();
35        (self as *const Self as usize) + header_size
36    }
37
38    /// Gets the element at the specified index
39    ///
40    /// # Arguments
41    /// * `index` - The index of the element to retrieve
42    ///
43    /// # Returns
44    /// * `T` - The element at the specified index
45    ///
46    /// # Panics
47    /// Panics if the index is out of bounds.
48    pub fn get(&self, index: usize) -> T {
49        if index >= self.max_length {
50            panic!("Index out of bounds: {} >= {}", index, self.max_length);
51        }
52
53        unsafe {
54            let element_ptr = (self.get_data() + index * std::mem::size_of::<T>()) as *const T;
55            *element_ptr
56        }
57    }
58
59    /// Alias for `get`
60    ///
61    /// # Arguments
62    /// * `index` - The index of the element to retrieve
63    ///
64    /// # Returns
65    /// * `T` - The element at the specified index
66    pub fn at(&self, index: usize) -> T {
67        self.get(index)
68    }
69
70    /// Sets the element at the specified index
71    ///
72    /// # Arguments
73    /// * `index` - The index where the value should be set
74    /// * `value` - The value to set
75    ///
76    /// # Panics
77    /// Panics if the index is out of bounds.
78    pub fn set(&mut self, index: usize, value: T) {
79        if index >= self.max_length {
80            panic!("Index out of bounds: {} >= {}", index, self.max_length);
81        }
82
83        unsafe {
84            let element_ptr = (self.get_data() + index * std::mem::size_of::<T>()) as *mut T;
85            *element_ptr = value;
86        }
87    }
88
89    /// Gets a raw pointer to the data
90    ///
91    /// # Returns
92    /// * `*const T` - Pointer to the first element of the array
93    pub fn get_pointer(&self) -> *const T {
94        let header_size = std::mem::size_of::<Self>();
95        ((self as *const Self as usize) + header_size) as *const T
96    }
97
98    /// Inserts elements from a slice into the array
99    ///
100    /// # Arguments
101    /// * `arr` - The slice of elements to insert
102    /// * `size` - The number of elements to insert
103    /// * `index` - The starting index in the array
104    pub fn insert(&mut self, arr: &[T], size: usize, index: usize) {
105        if (size + index) >= self.max_length {
106            if index >= self.max_length {
107                return;
108            }
109
110            let new_size = self.max_length - index;
111            for (i, &item) in arr.iter().enumerate().take(new_size) {
112                self.set(i + index, item);
113            }
114        } else {
115            for (i, &item) in arr.iter().enumerate().take(size) {
116                self.set(i + index, item);
117            }
118        }
119    }
120
121    /// Fills the array with a value
122    ///
123    /// # Arguments
124    /// * `value` - The value to fill the array with
125    pub fn fill(&mut self, value: T) {
126        for i in 0..self.max_length {
127            self.set(i, value);
128        }
129    }
130
131    /// Removes an element at the specified index
132    ///
133    /// Shifts subsequent elements to the left in place.
134    ///
135    /// IL2CPP arrays are fixed-size, so this does not change `max_length`.
136    /// The final slot is left as a duplicate of the former tail element.
137    ///
138    /// # Arguments
139    /// * `index` - The index of the element to remove
140    pub fn remove_at(&mut self, index: usize) {
141        if index >= self.max_length {
142            return;
143        }
144
145        if index + 1 < self.max_length {
146            unsafe {
147                let data = self.get_pointer() as *mut T;
148                std::ptr::copy(
149                    data.add(index + 1),
150                    data.add(index),
151                    self.max_length - index - 1,
152                );
153            }
154        }
155    }
156
157    /// Removes a range of elements
158    ///
159    /// Shifts subsequent elements to the left in place.
160    ///
161    /// IL2CPP arrays are fixed-size, so this does not change `max_length`.
162    /// Vacated tail slots are left duplicated from the previous tail values.
163    ///
164    /// # Arguments
165    /// * `index` - The starting index
166    /// * `count` - The number of elements to remove
167    pub fn remove_range(&mut self, index: usize, mut count: usize) {
168        if index >= self.max_length {
169            return;
170        }
171
172        if count == 0 {
173            count = 1;
174        }
175
176        let count = count.min(self.max_length - index);
177        let tail_len = self.max_length - index - count;
178
179        if tail_len > 0 {
180            unsafe {
181                let data = self.get_pointer() as *mut T;
182                std::ptr::copy(data.add(index + count), data.add(index), tail_len);
183            }
184        }
185    }
186
187    /// Removes all elements from the array
188    ///
189    /// IL2CPP arrays are fixed-size managed objects, so clearing them by
190    /// shrinking the header is unsupported. Overwrite elements explicitly if
191    /// you need sentinel values.
192    pub fn remove_all(&mut self) {}
193
194    /// Converts the array to a Rust Vec
195    ///
196    /// # Returns
197    /// * `Vec<T>` - A new vector containing the array elements
198    pub fn to_vector(&self) -> Vec<T> {
199        let mut result = Vec::with_capacity(self.max_length);
200        for i in 0..self.max_length {
201            result.push(self.at(i));
202        }
203        result
204    }
205
206    /// Creates a new array instance
207    ///
208    /// # Arguments
209    /// * `class` - The class of the elements
210    /// * `size` - The size of the array
211    ///
212    /// # Returns
213    /// * `*mut Self` - A pointer to the new array
214    pub fn new(class: &Class, size: usize) -> *mut Self {
215        unsafe { api::array_new(class.address, size as u32) as *mut Self }
216    }
217}