commonlibsse_ng\rel\relocation/phantom_member.rs
1use crate::skse::version::RUNTIME_SSE_1_6_629;
2
3use super::{RelocationError, relocate_member_if_newer, relocate_member_if_newer_mut};
4use core::{fmt, marker::PhantomData};
5
6/// A zero-sized marker used to access dynamically relocated members.
7///
8/// In the C++ class, it is not possible to simultaneously obtain mutable references
9/// to multiple fields due to Rust's borrowing rules. To work around this, `PhantomMember` provides
10/// an API for safely accessing individual fields while respecting the newer runtime offsets.
11///
12/// This struct is a placeholder that allows access to relocated members by providing
13/// runtime-based offset calculations.
14///
15/// # Generics
16/// - `T`: Member type
17/// - `OLD`: Offset when < ver.1_6_629
18/// - `NEW`: Offset when >= ver.1_6_629
19#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct PhantomMember<T, const OLD: isize, const NEW: isize> {
21 marker: PhantomData<T>,
22}
23
24impl<T, const OLD: isize, const NEW: isize> fmt::Debug for PhantomMember<T, OLD, NEW>
25where
26 T: fmt::Debug,
27{
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 f.debug_struct("PhantomMember")
30 .field("type", &self.get())
31 .field("OLD", &OLD)
32 .field("NEW", &NEW)
33 .finish()
34 }
35}
36
37impl<T, const OLD: isize, const NEW: isize> Clone for PhantomMember<T, OLD, NEW> {
38 #[inline]
39 fn clone(&self) -> Self {
40 Self { marker: self.marker }
41 }
42}
43
44impl<T, const OLD: isize, const NEW: isize> PhantomMember<T, OLD, NEW> {
45 /// Retrieves a reference to the member as immutable.
46 ///
47 /// # Safety
48 /// This performs an unsafe relocation based on runtime version checks.
49 /// Returns `None` if the relocation fails.
50 ///
51 /// # Errors
52 /// - This function may return an error if the module's state cannot be accessed, or if the `map_active` call fails when fetching the current version.
53 /// - If the pointer is null
54 /// - If the pointer is unaligned
55 #[inline]
56 pub fn get(&self) -> Result<&T, RelocationError> {
57 unsafe { relocate_member_if_newer(RUNTIME_SSE_1_6_629, self, OLD, NEW) }
58 }
59
60 /// Retrieves a mutable reference to the member.
61 ///
62 /// # Safety
63 /// This performs an unsafe relocation based on runtime version checks.
64 /// Returns `None` if the relocation fails.
65 ///
66 /// # Errors
67 /// - This function may return an error if the module's state cannot be accessed, or if the `map_active` call fails when fetching the current version.
68 /// - If the pointer is null
69 /// - If the pointer is unaligned
70 #[inline]
71 pub fn get_mut(&mut self) -> Result<&mut T, RelocationError> {
72 unsafe { relocate_member_if_newer_mut(RUNTIME_SSE_1_6_629, self, OLD, NEW) }
73 }
74}