commonlibsse_ng\re\b/
BSPointerHandle.rs

1use core::marker::PhantomData;
2
3use crate::re::Actor::Actor;
4use crate::re::NiSmartPointer::{NiPointer, RefCountable};
5use crate::re::Projectile;
6use crate::re::TESObjectREFR::TESObjectREFR;
7
8/// A raw 32-bit untyped handle used internally for object references.
9#[repr(transparent)]
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct BSUntypedPointerHandle(u32);
12
13impl BSUntypedPointerHandle {
14    /// Creates a new handle with a value of 0 (null).
15    #[inline]
16    pub const fn new() -> Self {
17        Self(0)
18    }
19
20    /// Creates a handle from a raw [`u32`] value.
21    /// - Equivalent C++ method: `from_value`
22    #[inline]
23    pub const fn from_raw(value: u32) -> Self {
24        Self(value)
25    }
26
27    /// Returns `true` if this handle is non-zero.
28    #[inline]
29    pub const fn has_value(&self) -> bool {
30        self.0 != 0
31    }
32
33    /// Returns the raw [`u32`] value of the handle.
34    /// - Equivalent C++ method: `value`
35    #[inline]
36    pub const fn as_raw(&self) -> u32 {
37        self.0
38    }
39
40    /// Resets this handle to 0.
41    #[inline]
42    pub const fn reset(&mut self) {
43        self.0 = 0;
44    }
45}
46
47/// A typed handle referencing an object of type `T`.
48///
49/// This provides type safety for handles, and can be used to retrieve a smart pointer or reference.
50#[repr(C)]
51#[derive(Debug)]
52pub struct BSPointerHandle<T> {
53    handle: BSUntypedPointerHandle,
54    _phantom: core::marker::PhantomData<T>,
55}
56const _: () = assert!(core::mem::size_of::<BSPointerHandle<()>>() == 0x4);
57
58impl<T> Clone for BSPointerHandle<T> {
59    #[inline]
60    fn clone(&self) -> Self {
61        Self { handle: self.handle, _phantom: self._phantom }
62    }
63}
64
65impl<T> Default for BSPointerHandle<T> {
66    #[inline]
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72impl<T> PartialEq<Self> for BSPointerHandle<T> {
73    #[inline]
74    fn eq(&self, other: &Self) -> bool {
75        self.handle == other.handle
76    }
77}
78
79impl<T> Eq for BSPointerHandle<T> {}
80
81impl<T> BSPointerHandle<T> {
82    /// Creates a new, null handle.
83    #[inline]
84    pub const fn new() -> Self {
85        Self { handle: BSUntypedPointerHandle::new(), _phantom: PhantomData }
86    }
87
88    /// Creates a handle from an untyped handle.
89    #[inline]
90    pub const fn from_handle(handle: BSUntypedPointerHandle) -> Self {
91        Self { handle, _phantom: PhantomData }
92    }
93
94    /// Creates a handle from a raw [`u32`] value.
95    #[inline]
96    const fn from_raw(value: u32) -> Self {
97        Self { handle: BSUntypedPointerHandle::from_raw(value), _phantom: PhantomData }
98    }
99
100    /// Returns the raw native handle value.
101    pub const fn native_handle(&self) -> u32 {
102        self.handle.as_raw()
103    }
104
105    /// Returns true if the handle has a non-zero value.
106    pub const fn has_value(&self) -> bool {
107        self.handle.has_value()
108    }
109
110    /// Resets the handle to 0.
111    pub const fn reset(&mut self) {
112        self.handle.reset();
113    }
114}
115
116impl<T: RefCountable> BSPointerHandle<T> {
117    /// Retrieves a smart pointer to the underlying object.
118    #[inline]
119    pub fn get(&self) -> NiPointer<T> {
120        let mut smart_ptr = NiPointer::<T>::new();
121        let _ = BSPointerHandleManagerInterface::<T>::GetSmartPointer(self, &mut smart_ptr);
122        smart_ptr
123    }
124
125    /// Attempts to get a shared reference to the underlying object.
126    #[inline]
127    pub fn as_ref(&self) -> Option<&T> {
128        self.get().as_ptr().map(|ptr| unsafe { ptr.as_ref() })
129    }
130}
131
132/// Interface for converting between raw pointers and handles.
133#[repr(C)]
134pub struct BSPointerHandleManagerInterface<T> {
135    _phantom: core::marker::PhantomData<T>,
136}
137
138impl<T> BSPointerHandleManagerInterface<T> {
139    /// Gets a typed handle from a raw pointer.
140    pub fn GetHandle(ptr: *mut T) -> BSPointerHandle<T> {
141        BSPointerHandle::from_raw(unsafe { Self::GetHandleRaw(ptr.cast()) })
142    }
143
144    /// FFI to retrieve a raw handle from a pointer.
145    ///
146    /// # Safety
147    /// This uses relocation and works with raw void pointers.
148    #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 15967, ae_id = 16212)]
149    #[inline]
150    pub unsafe extern "C" fn GetHandleRaw(ptr: *mut ()) -> u32 {}
151}
152
153impl<T: RefCountable> BSPointerHandleManagerInterface<T> {
154    /// Gets a smart pointer from the handle and stores it in `smart_ptr`.
155    pub fn GetSmartPointer(handle: &BSPointerHandle<T>, smart_ptr: &mut NiPointer<T>) -> bool {
156        let handle = (handle as *const BSPointerHandle<T>).cast();
157        let smart_ptr = (smart_ptr as *mut NiPointer<T>).cast();
158        // Safety: handle & smart_ptr are correct type.
159        unsafe { Self::GetSmartPointerRaw(handle, smart_ptr) }
160    }
161
162    /// FFI to retrieve a smart pointer from a handle.
163    ///
164    /// # Safety
165    /// This uses relocation and raw pointers for compatibility with native layout.
166    #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 12204, ae_id = 12332)]
167    #[inline]
168    pub unsafe extern "C" fn GetSmartPointerRaw(handle: *const (), smart_ptr: *mut ()) -> bool {}
169}
170
171/// Macro to generate strongly-typed handle wrappers over [`BSPointerHandle<T>`].
172///
173/// Provides new type-style wrappers and utility methods for specific handle types.
174macro_rules! impl_handle_wrapper {
175    ($(
176        $(#[$meta:meta])*
177        $vis:vis struct $name:ident : $inner:ty;
178    )*) => {
179        $(
180            $(#[$meta])*
181            /// Strongly-typed handle to an object.
182            ///
183            /// Use `get` or `as_ref` to access the underlying object.
184            #[repr(transparent)]
185            #[derive(Debug, Clone, Default, PartialEq)]
186            $vis struct $name(BSPointerHandle<$inner>);
187
188            // Safety: The handle can be reset to 0.
189            unsafe impl std_fork::zeroable::Zeroable for $name {}
190
191            impl $name {
192                /// Creates a new, null handle.
193                #[inline]
194                pub const fn new() -> Self {
195                    Self(BSPointerHandle::new())
196                }
197
198                /// Creates a typed handle from an untyped handle.
199                #[inline]
200                pub const fn from_handle(handle: BSUntypedPointerHandle) -> Self {
201                    Self(BSPointerHandle::from_handle(handle))
202                }
203
204                /// Creates a typed handle from a raw [`u32`] value.
205                #[inline]
206                pub const fn from_raw(value: u32) -> Self {
207                    Self(BSPointerHandle::from_raw(value))
208                }
209
210                /// Gets the raw [`u32`] value of the handle.
211                #[inline]
212                pub const fn as_raw(&self) -> u32 {
213                    self.0.native_handle()
214                }
215
216                /// Returns `true` if the handle is non-zero.
217                #[inline]
218                pub const fn has_value(&self) -> bool {
219                    self.0.has_value()
220                }
221
222                /// Resets the handle to 0.
223                #[inline]
224                pub const fn reset(&mut self) {
225                    self.0.reset()
226                }
227            }
228
229            impl From<BSPointerHandle<$inner>> for $name {
230                #[inline]
231                fn from(inner: BSPointerHandle<$inner>) -> Self {
232                    Self(inner)
233                }
234            }
235
236            impl From<$name> for BSPointerHandle<$inner> {
237                #[inline]
238                fn from(wrapper: $name) -> Self {
239                    wrapper.0
240                }
241            }
242
243            impl $name {
244                /// Retrieves a smart pointer to the underlying object.
245                #[inline]
246                pub fn get(&self) -> NiPointer<$inner> {
247                    self.0.get()
248                }
249
250                /// Attempts to get a shared reference to the object.
251                #[inline]
252                pub fn as_ref(&self) -> Option<&$inner> {
253                    self.0.as_ref()
254                }
255            }
256        )*
257    };
258}
259
260impl_handle_wrapper! {
261    pub struct ActorHandle : Actor;
262    pub struct ProjectileHandle : Projectile;
263    pub struct ObjectRefHandle : TESObjectREFR;
264}