use anyhow::Context; use itertools::Itertools; use std::{hash::Hash, ptr::NonNull}; use crate::core_types::{self, FName}; use crate::tarray::TArray; use self::traits::{AsUObject, AsUStruct, FromRaw}; #[repr(transparent)] #[derive(Debug, Clone)] pub struct UObject { inner: NonNull, } unsafe impl Send for UObject {} unsafe impl Sync for UObject {} impl Eq for UObject {} impl PartialEq for UObject { fn eq(&self, other: &Self) -> bool { unsafe { self.inner.as_ref().internal_index == other.inner.as_ref().internal_index && self.inner.as_ref().name == other.inner.as_ref().name } } } impl Hash for UObject { fn hash(&self, state: &mut H) { self.internal_index().hash(state); self.name().hash(state); } } #[repr(transparent)] #[derive(Debug, Clone)] pub struct UClass { pub(crate) inner: NonNull, } impl Eq for UClass {} impl PartialEq for UClass { fn eq(&self, other: &Self) -> bool { self.as_uobject().eq(&other.as_uobject()) } } impl Hash for UClass { fn hash(&self, state: &mut H) { self.as_uobject().hash(state); } } #[repr(transparent)] #[derive(Debug, Clone)] pub struct UField { inner: NonNull, } impl Eq for UField {} impl PartialEq for UField { fn eq(&self, other: &Self) -> bool { self.as_uobject().eq(&other.as_uobject()) } } impl Hash for UField { fn hash(&self, state: &mut H) { self.as_uobject().hash(state); } } impl UObject { pub fn new(inner: NonNull) -> Self { Self { inner } } pub fn maybe_new(inner: Option>) -> Option { inner.map(|inner| Self { inner }) } pub fn to_inner(self) -> NonNull { self.inner } pub const unsafe fn cast(self) -> NonNull { self.inner.cast::() } /// returns a `Self` with the provided raw pointer, if the pointer is not null pub fn maybe_with_raw(raw: *mut core_types::UObject) -> Option { Self::maybe_new(NonNull::new(raw)) } pub fn class(&self) -> Option { unsafe { self.inner.as_ref().class.map(|inner| UClass::new(inner)) } } pub fn internal_index(&self) -> u32 { unsafe { self.inner.as_ref().internal_index } } pub fn name(&self) -> core_types::FName { unsafe { self.inner.as_ref().name } } pub fn get_name(&self) -> anyhow::Result { let name = self.name(); name.get_name() .map(|name_str| { if name.number > 0 { format!("{}_{}", name_str, name.number) } else { name_str } }) .map(|name_str| { name_str .rfind("/") .map(|pos| name_str[(pos + 1)..].to_string()) .unwrap_or_else(|| name_str) }) } pub fn get_name_or_default(&self) -> String { self.get_name() .unwrap_or_else(|_| "DefaultObjectName".to_string()) } pub fn get_full_name(&self) -> anyhow::Result { let tmp = self .iter_outer_objects() .map(|obj| obj.get_name()) .fold_ok(String::new(), |acc, obj_name| { format!("{}.{}", obj_name, acc) })?; Ok(format!( "{} {}{}", self.class() .context("invalid class pointer")? .as_uobject() .get_name_or_default(), tmp, self.get_name_or_default() )) } pub fn get_full_name_or_default(&self) -> String { self.get_full_name() .unwrap_or_else(|_| "DefaultObjectFullName".to_string()) } pub fn outer(&self) -> Option { unsafe { self.inner.as_ref().outer.map(|inner| UObject::new(inner)) } } /// returns the package object of this object pub fn outermost(&self) -> Option { self.iter_outer_objects().last() } pub fn is_package_object(&self) -> bool { unsafe { self.inner.as_ref().outer.is_none() } } pub fn iter_outer_objects(&self) -> OuterObjectIterator { OuterObjectIterator::new(self.clone()) } pub fn is_a(&self, other: &UClass) -> bool { self.class() .map(|class| class.iter_super_classes().contains(other)) .unwrap_or(false) } } pub struct OuterObjectIterator { object: UObject, } impl OuterObjectIterator { pub fn new(object: UObject) -> Self { Self { object } } } impl Iterator for OuterObjectIterator { type Item = UObject; fn next(&mut self) -> Option { if let Some(outer) = self.object.outer() { self.object = outer.clone(); Some(outer) } else { None } } } impl From for UClass { fn from(obj: UObject) -> Self { Self::new(obj.inner.cast()) } } impl AsUStruct for UClass { fn as_ustruct(&self) -> UStruct { UStruct::new(self.inner.clone().cast()) } } impl AsRef for UClass { fn as_ref(&self) -> &core_types::UClass { unsafe { self.inner.as_ref() } } } impl FromRaw for UClass { fn from_non_null(inner: NonNull) -> Self { Self::new(inner) } } impl UClass { pub fn new(inner: NonNull) -> Self { Self { inner } } pub fn iter_super_classes(&self) -> SuperClassIter { SuperClassIter::new(self.clone()) } } pub struct SuperClassIter { class: UClass, } impl SuperClassIter { pub fn new(class: UClass) -> Self { Self { class } } } impl Iterator for SuperClassIter { type Item = UClass; fn next(&mut self) -> Option { if let Some(next) = self.class.super_struct().map(|spr| spr.as_uobject().into()) { let item = core::mem::replace(&mut self.class, next); Some(item) } else { None } } } impl From for UField { fn from(obj: UObject) -> Self { Self::new(obj.inner.cast()) } } impl UField { pub fn new(inner: NonNull) -> Self { Self { inner } } pub fn maybe_new(inner: Option>) -> Option { inner.map(|inner| Self { inner }) } pub fn maybe_with_raw(raw: *mut core_types::UField) -> Option { NonNull::new(raw).map(|inner| Self::new(inner)) } pub fn as_uobject(&self) -> UObject { UObject::new(self.inner.clone().cast()) } pub unsafe fn as_ref(&self) -> &core_types::UField { self.inner.as_ref() } pub fn next(&self) -> Option { Self::maybe_new(unsafe { self.as_ref().next }) } } #[derive(Debug, Clone)] pub struct UEnum { inner: NonNull, } impl From for UEnum { fn from(obj: UObject) -> Self { Self::new(obj.inner.cast()) } } impl UEnum { pub fn new(inner: NonNull) -> Self { Self { inner } } pub fn maybe_new(inner: Option>) -> Option { inner.map(|inner| Self { inner }) } pub fn maybe_with_raw(raw: *mut core_types::UEnum) -> Option { NonNull::new(raw).map(|inner| Self::new(inner)) } pub fn as_uobject(&self) -> UObject { UObject::new(self.inner.clone().cast()) } pub unsafe fn as_ref(&self) -> &core_types::UEnum { self.inner.as_ref() } pub fn get_names(&self) -> &TArray { unsafe { &self.as_ref().names } } } #[derive(Debug, Clone)] pub struct UStruct { inner: NonNull, } impl From for UStruct { fn from(obj: UObject) -> Self { Self::new(obj.inner.cast()) } } impl FromRaw for UStruct { fn from_non_null(inner: NonNull) -> Self { Self::new(inner) } } impl UStruct { pub fn new(inner: NonNull) -> Self { Self { inner } } pub unsafe fn as_raw(&self) -> &core_types::UStruct { self.inner.as_ref() } fn super_struct(&self) -> Option { Self::from_maybe_non_null(unsafe { self.as_raw().super_field }) } fn children(&self) -> Option { UField::maybe_new(unsafe { self.as_raw().children }) } fn iter_fields(&self) -> Option { self.children() .map(|field| UStructFieldIterator::new(field)) } } impl traits::AsUStruct for UStruct { fn as_ustruct(&self) -> UStruct { self.clone() } } pub struct UStructFieldIterator { field: UField, } impl UStructFieldIterator { pub fn new(field: UField) -> Self { Self { field } } } impl Iterator for UStructFieldIterator { type Item = UField; fn next(&mut self) -> Option { if let Some(mut next) = self.field.next() { std::mem::swap(&mut self.field, &mut next); Some(next) } else { None } } } #[derive(Debug, Clone)] pub struct UScriptStruct { inner: NonNull, } impl From for UScriptStruct { fn from(obj: UObject) -> Self { Self::new(obj.inner.cast()) } } impl UScriptStruct { pub fn new(inner: NonNull) -> Self { Self { inner } } pub fn maybe_new(inner: Option>) -> Option { inner.map(|inner| Self { inner }) } pub fn maybe_with_raw(raw: *mut core_types::UStruct) -> Option { NonNull::new(raw).map(|inner| Self::new(inner)) } pub fn as_uobject(&self) -> UObject { UObject::new(self.inner.clone().cast()) } pub unsafe fn as_ref(&self) -> &core_types::UStruct { self.inner.as_ref() } pub fn iter_fields(&self) -> Option { UField::maybe_new(unsafe { self.as_ref().children }) .map(|field| UStructFieldIterator::new(field)) } } #[derive(Debug, Clone)] pub struct UProperty { inner: NonNull, } impl From for UProperty { /// WARNING: There is no type check on this conversion, so it is most certainly unsafe fn from(obj: UObject) -> Self { Self::new(obj.inner.cast()) } } impl UProperty { pub fn new(inner: NonNull) -> Self { Self { inner } } pub fn maybe_new(inner: Option>) -> Option { inner.map(|inner| Self { inner }) } pub fn maybe_with_raw(raw: *mut core_types::UProperty) -> Option { NonNull::new(raw).map(|inner| Self::new(inner)) } pub fn as_uobject(&self) -> UObject { UObject::new(self.inner.clone().cast()) } pub unsafe fn as_ref(&self) -> &core_types::UProperty { self.inner.as_ref() } } #[derive(Debug, Clone)] pub struct UFunction { inner: NonNull, } impl From for UFunction { /// WARNING: There is no type check on this conversion, so it is most certainly unsafe fn from(obj: UObject) -> Self { Self::new(obj.inner.cast()) } } impl UFunction { pub fn new(inner: NonNull) -> Self { Self { inner } } pub fn maybe_new(inner: Option>) -> Option { inner.map(|inner| Self { inner }) } pub fn maybe_with_raw(raw: *mut core_types::UFunction) -> Option { NonNull::new(raw).map(|inner| Self::new(inner)) } pub fn as_uobject(&self) -> UObject { UObject::new(self.inner.clone().cast()) } pub unsafe fn as_ref(&self) -> &core_types::UFunction { self.inner.as_ref() } } #[derive(Debug, Clone)] pub enum UAnyType { UObject(UObject), UClass(UClass), UField(UField), UScriptStruct(UScriptStruct), UProperty(UProperty), UEnum(UEnum), UStruct(UStruct), UFunction(UFunction), } impl UAnyType { pub fn as_uobject(&self) -> UObject { match self { UAnyType::UObject(rep) => rep.clone(), UAnyType::UClass(rep) => rep.as_uobject(), UAnyType::UField(rep) => rep.as_uobject(), UAnyType::UScriptStruct(rep) => rep.as_uobject(), UAnyType::UProperty(rep) => rep.as_uobject(), UAnyType::UEnum(rep) => rep.as_uobject(), UAnyType::UStruct(rep) => rep.as_uobject(), UAnyType::UFunction(rep) => rep.as_uobject(), } } } impl Hash for UAnyType { fn hash(&self, state: &mut H) { self.as_uobject().hash(state); } } pub mod traits { use crate::core_types::FName; use std::ptr::NonNull; use itertools::Itertools; use super::{OuterObjectIterator, UClass, UField, UObject, UStruct, UStructFieldIterator}; pub unsafe fn null_ptr_cast<'a, T, U>(ptr: &'a NonNull) -> &'a NonNull { std::mem::transmute(ptr) } pub trait AsUObject: Sized { fn as_uobject(&self) -> UObject; fn class(&self) -> Option { self.as_uobject().class() } fn internal_index(&self) -> u32 { self.as_uobject().internal_index() } fn name(&self) -> FName { self.as_uobject().name() } fn get_name(&self) -> anyhow::Result { self.as_uobject().get_name() } fn get_name_or_default(&self) -> String { self.as_uobject().get_name_or_default() } fn get_full_name(&self) -> anyhow::Result { self.as_uobject().get_full_name() } fn get_full_name_or_default(&self) -> String { self.as_uobject().get_full_name_or_default() } fn outer(&self) -> Option { self.as_uobject().outer() } fn outermost(&self) -> Option { self.as_uobject().outermost() } fn is_package_object(&self) -> bool { self.as_uobject().is_package_object() } fn iter_outer_objects(&self) -> OuterObjectIterator { OuterObjectIterator::new(self.as_uobject()) } fn is_a(&self, other: &UClass) -> bool { self.class() .map(|class| class.iter_super_classes().contains(other)) .unwrap_or(false) } } pub trait AsUStruct { fn as_ustruct(&self) -> UStruct; #[inline] fn children(&self) -> Option { self.as_ustruct().children() } #[inline] fn super_struct(&self) -> Option { self.as_ustruct().super_struct() } #[inline] fn iter_fields(&self) -> Option { self.children() .map(|field| UStructFieldIterator::new(field)) } } impl AsUObject for T where T: AsUStruct, { fn as_uobject(&self) -> UObject { UObject::new(self.as_ustruct().inner.cast()) } } pub trait FromRaw { fn from_non_null(inner: NonNull) -> Self; fn from_raw(raw: *mut R) -> Option where Self: Sized, { NonNull::new(raw).map(|inner| Self::from_non_null(inner)) } fn from_maybe_non_null(inner: Option>) -> Option where Self: Sized, { inner.map(|inner| Self::from_non_null(inner)) } } }