use std::marker::PhantomData;
use rusteal_ffi::{UClassHandle, UObjectHandle};
use crate::containers::ContainerElement;
use crate::object_ref::ObjectPointer;
use crate::traits::{HasParent, UeClass};
#[repr(transparent)]
pub struct SubclassOf<T: UeClass> {
handle: UClassHandle,
_marker: PhantomData<*const T>,
}
impl<T: UeClass> Clone for SubclassOf<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: UeClass> Copy for SubclassOf<T> {}
impl<T: UeClass> PartialEq for SubclassOf<T> {
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle
}
}
impl<T: UeClass> Eq for SubclassOf<T> {}
impl<T: UeClass> std::hash::Hash for SubclassOf<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.handle.hash(state);
}
}
unsafe impl<T: UeClass> Send for SubclassOf<T> {}
impl<T: UeClass> SubclassOf<T> {
pub fn null() -> Self {
Self {
handle: UClassHandle::null(),
_marker: PhantomData,
}
}
pub fn base() -> Self {
Self {
handle: T::static_class(),
_marker: PhantomData,
}
}
pub unsafe fn from_raw(handle: UClassHandle) -> Self {
Self {
handle,
_marker: PhantomData,
}
}
pub fn raw(&self) -> UClassHandle {
self.handle
}
pub fn is_null(&self) -> bool {
self.handle.is_null()
}
}
impl<T: HasParent> SubclassOf<T> {
#[inline]
pub fn upcast(self) -> SubclassOf<T::Parent> {
unsafe { SubclassOf::from_raw(self.handle) }
}
}
impl<T: UeClass> SubclassOf<T> {
#[inline]
pub fn upcast_to<U: UeClass>(self) -> SubclassOf<U>
where
T: crate::traits::Inherits<U>,
{
unsafe { SubclassOf::from_raw(self.handle) }
}
}
impl<T: UeClass> ObjectPointer for SubclassOf<T> {
#[inline]
unsafe fn from_object_handle(handle: UObjectHandle) -> Self {
unsafe { Self::from_raw(UClassHandle(handle.0)) }
}
#[inline]
fn object_handle(&self) -> UObjectHandle {
UObjectHandle(self.handle.0)
}
}
impl<T: UeClass> Default for SubclassOf<T> {
fn default() -> Self {
Self::null()
}
}
impl<T: UeClass> std::fmt::Debug for SubclassOf<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SubclassOf").field(&self.handle.0).finish()
}
}
unsafe impl<T: UeClass> ContainerElement for SubclassOf<T> {
const BUF_SIZE: u32 = std::mem::size_of::<UObjectHandle>() as u32;
#[inline]
unsafe fn read_from_buf(buf: *const u8, _written: u32) -> Self {
unsafe {
let handle = (buf as *const UObjectHandle).read_unaligned();
Self::from_raw(UClassHandle(handle.0))
}
}
#[inline]
unsafe fn write_to_buf(&self, buf: *mut u8) -> u32 {
unsafe {
(buf as *mut UObjectHandle).write_unaligned(UObjectHandle(self.handle.0));
std::mem::size_of::<UObjectHandle>() as u32
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Probe;
impl UeClass for Probe {
fn static_class() -> UClassHandle {
UClassHandle::from_addr(0x1000)
}
}
#[test]
fn compares_by_class() {
let base = SubclassOf::<Probe>::base();
assert_eq!(base, SubclassOf::<Probe>::base());
assert_ne!(base, SubclassOf::<Probe>::null());
assert!(SubclassOf::<Probe>::default().is_null());
assert_eq!(base.raw(), Probe::static_class());
}
struct Child;
impl UeClass for Child {
fn static_class() -> UClassHandle {
UClassHandle::from_addr(0x2000)
}
}
impl HasParent for Child {
type Parent = Probe;
}
#[test]
fn travels_as_an_object_pointer_and_upcasts() {
let child = SubclassOf::<Child>::base();
let handle = child.object_handle();
assert_eq!(handle.to_addr(), 0x2000);
assert_eq!(unsafe { SubclassOf::<Child>::from_object_handle(handle) }, child);
let as_parent: SubclassOf<Probe> = child.upcast();
assert_eq!(as_parent.raw(), Child::static_class());
}
#[test]
fn round_trips_through_a_container_buffer() {
let base = SubclassOf::<Probe>::base();
let mut buf = [0u8; 8];
unsafe {
assert_eq!(base.write_to_buf(buf.as_mut_ptr()), 8);
assert_eq!(SubclassOf::<Probe>::read_from_buf(buf.as_ptr(), 8), base);
}
}
}