Skip to main content

rusteal_core/
subclass_of.rs

1use std::marker::PhantomData;
2
3use rusteal_ffi::{UClassHandle, UObjectHandle};
4
5use crate::containers::ContainerElement;
6use crate::object_ref::ObjectPointer;
7use crate::traits::{HasParent, UeClass};
8
9#[repr(transparent)]
10pub struct SubclassOf<T: UeClass> {
11    handle: UClassHandle,
12    _marker: PhantomData<*const T>,
13}
14
15impl<T: UeClass> Clone for SubclassOf<T> {
16    fn clone(&self) -> Self {
17        *self
18    }
19}
20
21impl<T: UeClass> Copy for SubclassOf<T> {}
22
23impl<T: UeClass> PartialEq for SubclassOf<T> {
24    fn eq(&self, other: &Self) -> bool {
25        self.handle == other.handle
26    }
27}
28
29impl<T: UeClass> Eq for SubclassOf<T> {}
30
31impl<T: UeClass> std::hash::Hash for SubclassOf<T> {
32    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
33        self.handle.hash(state);
34    }
35}
36
37unsafe impl<T: UeClass> Send for SubclassOf<T> {}
38
39impl<T: UeClass> SubclassOf<T> {
40    pub fn null() -> Self {
41        Self {
42            handle: UClassHandle::null(),
43            _marker: PhantomData,
44        }
45    }
46
47    pub fn base() -> Self {
48        Self {
49            handle: T::static_class(),
50            _marker: PhantomData,
51        }
52    }
53
54    pub unsafe fn from_raw(handle: UClassHandle) -> Self {
55        Self {
56            handle,
57            _marker: PhantomData,
58        }
59    }
60
61    #[track_caller]
62    pub fn from_class(class: UClassHandle) -> crate::error::RustealResult<Self> {
63        use crate::error::RustealError;
64
65        if class.is_null() {
66            return Err(RustealError::null_argument());
67        }
68
69        if !unsafe { crate::ffi_dispatch::core_is_child_of(class, T::static_class()) } {
70            return Err(RustealError::invalid_cast_to(
71                UObjectHandle(class.0),
72                T::static_class(),
73            ));
74        }
75
76        Ok(unsafe { Self::from_raw(class) })
77    }
78
79    pub fn raw(&self) -> UClassHandle {
80        self.handle
81    }
82
83    pub fn is_null(&self) -> bool {
84        self.handle.is_null()
85    }
86}
87
88impl<T: HasParent> SubclassOf<T> {
89    #[inline]
90    pub fn upcast(self) -> SubclassOf<T::Parent> {
91        unsafe { SubclassOf::from_raw(self.handle) }
92    }
93}
94
95impl<T: UeClass> SubclassOf<T> {
96    #[inline]
97    pub fn upcast_to<U: UeClass>(self) -> SubclassOf<U>
98    where
99        T: crate::traits::Inherits<U>,
100    {
101        unsafe { SubclassOf::from_raw(self.handle) }
102    }
103}
104
105impl<T: UeClass> ObjectPointer for SubclassOf<T> {
106    #[inline]
107    unsafe fn from_object_handle(handle: UObjectHandle) -> Self {
108        unsafe { Self::from_raw(UClassHandle(handle.0)) }
109    }
110
111    #[inline]
112    fn object_handle(&self) -> UObjectHandle {
113        UObjectHandle(self.handle.0)
114    }
115}
116
117impl<T: UeClass> Default for SubclassOf<T> {
118    fn default() -> Self {
119        Self::null()
120    }
121}
122
123impl<T: UeClass> std::fmt::Debug for SubclassOf<T> {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        f.debug_tuple("SubclassOf").field(&self.handle.0).finish()
126    }
127}
128
129unsafe impl<T: UeClass> ContainerElement for SubclassOf<T> {
130    const BUF_SIZE: u32 = std::mem::size_of::<UObjectHandle>() as u32;
131
132    #[inline]
133    unsafe fn read_from_buf(buf: *const u8, _written: u32) -> Self {
134        unsafe {
135            let handle = (buf as *const UObjectHandle).read_unaligned();
136
137            Self::from_raw(UClassHandle(handle.0))
138        }
139    }
140
141    #[inline]
142    unsafe fn write_to_buf(&self, buf: *mut u8) -> u32 {
143        unsafe {
144            (buf as *mut UObjectHandle).write_unaligned(UObjectHandle(self.handle.0));
145
146            std::mem::size_of::<UObjectHandle>() as u32
147        }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    struct Probe;
156    impl UeClass for Probe {
157        fn static_class() -> UClassHandle {
158            UClassHandle::from_addr(0x1000)
159        }
160    }
161
162    #[test]
163    fn compares_by_class() {
164        let base = SubclassOf::<Probe>::base();
165        assert_eq!(base, SubclassOf::<Probe>::base());
166        assert_ne!(base, SubclassOf::<Probe>::null());
167        assert!(SubclassOf::<Probe>::default().is_null());
168        assert_eq!(base.raw(), Probe::static_class());
169    }
170
171    struct Child;
172    impl UeClass for Child {
173        fn static_class() -> UClassHandle {
174            UClassHandle::from_addr(0x2000)
175        }
176    }
177    impl HasParent for Child {
178        type Parent = Probe;
179    }
180
181    #[test]
182    fn travels_as_an_object_pointer_and_upcasts() {
183        let child = SubclassOf::<Child>::base();
184        let handle = child.object_handle();
185        assert_eq!(handle.to_addr(), 0x2000);
186
187        assert_eq!(
188            unsafe { SubclassOf::<Child>::from_object_handle(handle) },
189            child
190        );
191
192        let as_parent: SubclassOf<Probe> = child.upcast();
193        assert_eq!(as_parent.raw(), Child::static_class());
194    }
195
196    #[test]
197    fn round_trips_through_a_container_buffer() {
198        let base = SubclassOf::<Probe>::base();
199        let mut buf = [0u8; 8];
200
201        unsafe {
202            assert_eq!(base.write_to_buf(buf.as_mut_ptr()), 8);
203            assert_eq!(SubclassOf::<Probe>::read_from_buf(buf.as_ptr(), 8), base);
204        }
205    }
206}