commonlibsse_ng\re\b/
BSExtraData.rs1use crate::re::CxxVirtClass;
2use crate::re::ExtraDataType::ExtraDataType;
3use crate::re::offsets_rtti::RTTI_BSExtraData;
4use crate::re::offsets_vtable::VTABLE_BSExtraData;
5use crate::rel::ResolvableAddress as _;
6use crate::rel::id::VariantID;
7use core::ffi::c_void;
8use core::marker::PhantomData;
9use core::ptr::{self, NonNull};
10
11#[repr(C)]
13#[derive(Debug, PartialEq)]
14pub struct BSExtraData {
15    pub vtbl: *const BSExtraDataVtbl,
17    pub next: *mut BSExtraData,
26    marker: PhantomData<BSExtraData>,
28}
29
30const _: () = assert!(core::mem::size_of::<BSExtraData>() == 0x10);
31
32impl BSExtraData {
33    pub const RTTI: VariantID = RTTI_BSExtraData;
35
36    pub const VTABLE: [VariantID; 1] = VTABLE_BSExtraData;
38
39    pub const EXTRA_DATA_TYPE: ExtraDataType = ExtraDataType::None;
41
42    #[inline]
44    pub const fn new() -> Self {
45        Self { vtbl: &BS_EXTRA_DATA_VTBL, next: ptr::null_mut(), marker: PhantomData }
46    }
47
48    #[inline]
50    pub fn get_type(&self) -> ExtraDataType {
51        let vtable = match Self::VTABLE[0].address() {
52            Ok(addr) => addr.cast::<BSExtraDataVtbl>(),
53            Err(_err) => {
54                #[cfg(feature = "tracing")]
55                tracing::error!("Failed to get address of vtable: {_err}");
56                return (BS_EXTRA_DATA_VTBL.GetType)(self);
57            }
58        };
59        unsafe { (vtable.as_ref().GetType)(self) }
60    }
61
62    #[inline]
64    pub fn is_not_equal(&self, rhs: &Self) -> bool {
65        let vtable = match Self::VTABLE[0].address() {
66            Ok(addr) => addr.cast::<BSExtraDataVtbl>(),
67            Err(_err) => {
68                #[cfg(feature = "tracing")]
69                tracing::error!("Failed to get address of vtable: {_err}");
70                return false;
71            }
72        };
73        unsafe { (vtable.as_ref().IsNotEqual)(self, rhs) }
74    }
75
76    pub fn create<T: CxxVirtClass>() -> Option<NonNull<T>> {
80        let t = &T::vtable()[0];
81        Self::create_with(core::mem::size_of::<T>(), t.address().ok()?).map(|void| void.cast())
82    }
83
84    #[inline]
89    pub fn create_with(size: usize, vtbl: NonNull<c_void>) -> Option<NonNull<c_void>> {
90        use core::alloc::Layout;
91        use core::mem::align_of;
92        use std::alloc::alloc_zeroed;
93
94        unsafe {
95            let memory = {
96                let layout = Layout::from_size_align(size, align_of::<Self>()).ok()?;
97                NonNull::new(alloc_zeroed(layout))?
98            };
99
100            let vtable_ptr = memory.cast();
101            vtable_ptr.write(vtbl);
102
103            Some(memory.cast())
104        }
105    }
106}
107
108#[inline]
109pub fn downcast_as<T>(extra_data: *mut BSExtraData) -> Option<NonNull<T>> {
110    if extra_data.is_null() || !extra_data.is_aligned() {
111        return None;
112    }
113
114    if crate::rex::win32::is_valid_range(extra_data.cast(), core::mem::size_of::<T>()) {
115        return Some(unsafe { NonNull::new_unchecked(extra_data.cast::<T>()) });
116    };
117    None
118}
119
120pub trait DerivedBSExtraData {
124    fn get_extra_data(&self) -> &BSExtraData;
126    fn get_extra_data_type() -> ExtraDataType;
128}
129
130impl DerivedBSExtraData for BSExtraData {
131    #[inline]
132    fn get_extra_data(&self) -> &BSExtraData {
133        self
134    }
135
136    #[inline]
137    fn get_extra_data_type() -> ExtraDataType {
138        Self::EXTRA_DATA_TYPE
139    }
140}
141
142impl CxxVirtClass for BSExtraData {
143    #[inline]
144    fn rtti() -> &'static VariantID {
145        &Self::RTTI
146    }
147
148    #[inline]
149    fn vtable() -> &'static [VariantID] {
150        &Self::VTABLE
151    }
152}
153
154impl Default for BSExtraData {
155    #[inline]
156    fn default() -> Self {
157        Self::new()
158    }
159}
160
161static BS_EXTRA_DATA_VTBL: BSExtraDataVtbl = BSExtraDataVtbl::new();
163
164#[repr(C)]
166pub struct BSExtraDataVtbl {
167    pub CxxDrop: fn(this: &mut BSExtraData),
169
170    pub GetType: fn(this: &BSExtraData) -> ExtraDataType,
172
173    pub IsNotEqual: fn(this: &BSExtraData, rhs: &BSExtraData) -> bool,
175}
176
177impl BSExtraDataVtbl {
178    #[inline]
180    const fn new() -> Self {
181        const fn CxxDrop(_this: &mut BSExtraData) {}
182
183        const fn GetType(_this: &BSExtraData) -> ExtraDataType {
184            BSExtraData::EXTRA_DATA_TYPE
185        }
186
187        const fn IsNotEqual(_this: &BSExtraData, _rhs: &BSExtraData) -> bool {
188            false
189        }
190
191        Self { CxxDrop, GetType, IsNotEqual }
192    }
193}
194
195pub struct BSExtraDataIter<'a> {
197    pub(crate) current: *mut BSExtraData,
199
200    pub(crate) prev: *mut BSExtraData,
202
203    marker: PhantomData<&'a BSExtraData>,
205}
206
207impl BSExtraDataIter<'_> {
208    #[inline]
210    pub const fn new(start: *mut BSExtraData) -> Self {
211        Self { current: start, prev: std::ptr::null_mut(), marker: PhantomData }
212    }
213
214    pub fn remove_current(&mut self) -> Option<*mut BSExtraData> {
219        if self.current.is_null() {
220            return None;
221        }
222
223        let removed = self.current;
224        unsafe {
225            let next = (*removed).next;
226
227            if !self.prev.is_null() {
228                (*self.prev).next = next;
229            }
230
231            self.current = next;
232        }
233
234        Some(removed)
235    }
236}
237
238impl Iterator for BSExtraDataIter<'_> {
239    type Item = *mut BSExtraData;
240
241    fn next(&mut self) -> Option<Self::Item> {
242        if self.current.is_null() {
243            return None;
244        }
245
246        let current = self.current;
247        unsafe {
248            self.prev = current;
249            self.current = (*current).next;
250        }
251        Some(current)
252    }
253}
254
255pub struct BSExtraDataIterMut<'a> {
257    cur: *mut BSExtraData,
258    prev: *mut BSExtraData,
259    marker: PhantomData<&'a BSExtraData>,
260}
261
262impl BSExtraDataIterMut<'_> {
263    #[inline]
265    pub const fn new(start: *mut BSExtraData) -> Self {
266        Self { cur: start, prev: std::ptr::null_mut(), marker: PhantomData }
267    }
268
269    pub fn remove_current(&mut self) {
271        if self.cur.is_null() {
272            return;
273        }
274
275        let next = (unsafe { &*self.cur }).next;
276
277        if !self.prev.is_null() {
278            (unsafe { &mut *self.prev }).next = next;
279        }
280
281        let to_delete = self.cur;
282        self.cur = next;
283
284        drop(unsafe { Box::from_raw(to_delete) });
286    }
287}
288
289impl Iterator for BSExtraDataIterMut<'_> {
290    type Item = *mut BSExtraData;
291
292    fn next(&mut self) -> Option<Self::Item> {
293        if self.cur.is_null() {
294            return None;
295        }
296
297        let current = self.cur;
298        unsafe {
299            self.prev = current;
300            self.cur = (*current).next;
301        }
302        Some(current)
303    }
304}
305
306