From 724939e5da0f633b834467a163e418ce743f7aa1 Mon Sep 17 00:00:00 2001 From: Janis Date: Thu, 20 Apr 2023 02:36:46 +0200 Subject: [PATCH] extracted fname and tarray from raw types into own modules - tarray and fname are pretty independent from the rest of the type system --- Cargo.toml | 1 + src/core_types.rs | 199 ++++++++++++++---------------- src/fname.rs | 19 +++ src/lib.rs | 4 +- src/tarray.rs | 104 ++++++++++++++++ src/types.rs | 5 +- src/v2_types/mod.rs | 293 ++++++++++++++++++++++++++++++++++++++++---- 7 files changed, 486 insertions(+), 139 deletions(-) create mode 100644 src/fname.rs create mode 100644 src/tarray.rs diff --git a/Cargo.toml b/Cargo.toml index 50ab91e..9a63893 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ bitflags = "1.0.0" anyhow = "1.0" widestring = "1.0" lazy_static = "1.4.0" +once_cell = "1.17.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" \ No newline at end of file diff --git a/src/core_types.rs b/src/core_types.rs index 312e3d5..9f7d6f2 100644 --- a/src/core_types.rs +++ b/src/core_types.rs @@ -1,4 +1,5 @@ #![allow(non_upper_case_globals)] +use crate::tarray::{FString, TArray}; use bitflags::bitflags; use std::ptr::NonNull; @@ -44,8 +45,6 @@ bitflags! { } } -pub use tarray::{FString, TArray}; - use crate::global_tables::names::GNAMES; #[repr(C)] @@ -127,6 +126,95 @@ pub struct UProperty { post_construct_link_next: Option>, } +#[repr(C)] +#[derive(Debug)] +pub struct UByteProperty { + pub(crate) uproperty: UProperty, + uenum: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UBoolProperty { + pub(crate) uproperty: UProperty, + field_size: u8, + byte_offset: u8, + byte_mask: u8, + field_mask: u8, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UObjectProperty { + pub(crate) uproperty: UProperty, + property_class: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UClassProperty { + pub(crate) uobjectproperty: UObjectProperty, + meta_class: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UAssetClassProperty { + pub(crate) uobjectproperty: UObjectProperty, + meta_class: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UInterfaceProperty { + pub(crate) uproperty: UProperty, + interface_class: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UStructProperty { + pub(crate) uproperty: UProperty, + ustruct: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UArrayProperty { + pub(crate) uproperty: UProperty, + inner: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UMapProperty { + pub(crate) uproperty: UProperty, + key_prop: Option>, + value_prop: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UDelegateProperty { + pub(crate) uproperty: UProperty, + signature_function: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UMulticastDelegateProperty { + pub(crate) uproperty: UProperty, + signature_function: Option>, +} + +#[repr(C)] +#[derive(Debug)] +pub struct UEnumProperty { + pub(crate) uproperty: UProperty, + underlying_type: Option>, + uenum: Option>, +} + #[repr(C)] #[derive(Debug)] pub struct UFunction { @@ -141,110 +229,3 @@ pub struct UFunction { first_property_to_init: Option>, function: Option>, } - -pub mod tarray { - use std::{ops::Index, ptr::NonNull, slice::SliceIndex}; - - #[repr(C)] - #[derive(Debug)] - pub struct TArray { - data: Option>, - count: u32, - max: u32, - } - - unsafe impl Send for TArray where T: Send {} - unsafe impl Sync for TArray where T: Sync {} - - pub type FString = TArray; - - impl ToString for FString { - fn to_string(&self) -> String { - widestring::U16CStr::from_slice(&self) - .expect("invalid utf16 string") - .to_string_lossy() - } - } - - impl TArray { - pub fn len(&self) -> usize { - self.count as usize - } - - pub fn capacity(&self) -> usize { - self.max as usize - } - - pub fn as_slice(&self) -> &[T] { - match self.data { - Some(ptr) => unsafe { core::slice::from_raw_parts(ptr.as_ptr(), self.len()) }, - None => unsafe { core::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0) }, - } - } - - pub fn as_slice_mut(&mut self) -> &mut [T] { - match self.data { - Some(ptr) => unsafe { core::slice::from_raw_parts_mut(ptr.as_ptr(), self.len()) }, - None => unsafe { core::slice::from_raw_parts_mut(NonNull::dangling().as_ptr(), 0) }, - } - } - } - - impl> Index for TArray { - type Output = I::Output; - - fn index(&self, i: I) -> &Self::Output { - let data = self.as_slice(); - - &data[i] - } - } - - impl std::ops::Deref for TArray { - type Target = [T]; - - fn deref(&self) -> &Self::Target { - self.as_slice() - } - } - - pub struct IntoIter { - //array_ref: &'a TArray, - ptr: *const T, - end: *const T, - } - - impl<'a, T> IntoIter { - pub fn new(array: &'a TArray) -> Self { - let ptr = array.data.unwrap().as_ptr(); - Self { - ptr, - end: unsafe { ptr.offset(array.count as isize) }, - } - } - } - - impl<'a, T> Iterator for IntoIter { - type Item = T; - - fn next(&mut self) -> Option { - if self.ptr == self.end { - None - } else { - let old = self.ptr; - self.ptr = unsafe { self.ptr.offset(1) }; - - Some(unsafe { std::ptr::read(old) }) - } - } - } - - impl<'a, T> IntoIterator for &'a TArray { - type Item = T; - type IntoIter = IntoIter; - - fn into_iter(self) -> Self::IntoIter { - IntoIter::new(self) - } - } -} diff --git a/src/fname.rs b/src/fname.rs new file mode 100644 index 0000000..ae357d2 --- /dev/null +++ b/src/fname.rs @@ -0,0 +1,19 @@ +use crate::global_tables::names::GNAMES; + +#[repr(C)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] +pub struct FName { + pub comparison_index: u32, + pub number: u32, +} + +impl FName { + pub fn get_name(&self) -> anyhow::Result { + GNAMES + .read() + .unwrap() + .as_names() + .unwrap() + .fname_to_string(self) + } +} diff --git a/src/lib.rs b/src/lib.rs index d939dd8..e9d4033 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,16 @@ #![feature(const_trait_impl, const_ptr_as_ref, const_nonnull_new)] mod core_types; +pub mod fname; pub mod global_tables; +pub mod tarray; pub mod types; pub mod v2_types; pub mod sdk { use std::{ borrow::Cow, - collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, + collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, fmt::Display, sync::Mutex, }; diff --git a/src/tarray.rs b/src/tarray.rs new file mode 100644 index 0000000..5a7b3ae --- /dev/null +++ b/src/tarray.rs @@ -0,0 +1,104 @@ +use std::{ops::Index, ptr::NonNull, slice::SliceIndex}; + +#[repr(C)] +#[derive(Debug)] +pub struct TArray { + data: Option>, + count: u32, + max: u32, +} + +unsafe impl Send for TArray where T: Send {} +unsafe impl Sync for TArray where T: Sync {} + +pub type FString = TArray; + +impl ToString for FString { + fn to_string(&self) -> String { + widestring::U16CStr::from_slice(&self) + .expect("invalid utf16 string") + .to_string_lossy() + } +} + +impl TArray { + pub fn len(&self) -> usize { + self.count as usize + } + + pub fn capacity(&self) -> usize { + self.max as usize + } + + pub fn as_slice(&self) -> &[T] { + match self.data { + Some(ptr) => unsafe { core::slice::from_raw_parts(ptr.as_ptr(), self.len()) }, + None => unsafe { core::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0) }, + } + } + + pub fn as_slice_mut(&mut self) -> &mut [T] { + match self.data { + Some(ptr) => unsafe { core::slice::from_raw_parts_mut(ptr.as_ptr(), self.len()) }, + None => unsafe { core::slice::from_raw_parts_mut(NonNull::dangling().as_ptr(), 0) }, + } + } +} + +impl> Index for TArray { + type Output = I::Output; + + fn index(&self, i: I) -> &Self::Output { + let data = self.as_slice(); + + &data[i] + } +} + +impl std::ops::Deref for TArray { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +pub struct IntoIter { + //array_ref: &'a TArray, + ptr: *const T, + end: *const T, +} + +impl<'a, T> IntoIter { + pub fn new(array: &'a TArray) -> Self { + let ptr = array.data.unwrap().as_ptr(); + Self { + ptr, + end: unsafe { ptr.offset(array.count as isize) }, + } + } +} + +impl<'a, T> Iterator for IntoIter { + type Item = T; + + fn next(&mut self) -> Option { + if self.ptr == self.end { + None + } else { + let old = self.ptr; + self.ptr = unsafe { self.ptr.offset(1) }; + + Some(unsafe { std::ptr::read(old) }) + } + } +} + +impl<'a, T> IntoIterator for &'a TArray { + type Item = T; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self) + } +} diff --git a/src/types.rs b/src/types.rs index 24092d9..31112c2 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,7 +2,8 @@ use anyhow::Context; use itertools::Itertools; use std::{hash::Hash, ptr::NonNull}; -use crate::core_types::{self, FName, TArray}; +use crate::core_types::{self, FName}; +use crate::tarray::TArray; use self::traits::{AsUObject, AsUStruct, FromRaw}; @@ -36,7 +37,7 @@ impl Hash for UObject { #[repr(transparent)] #[derive(Debug, Clone)] pub struct UClass { - inner: NonNull, + pub(crate) inner: NonNull, } impl Eq for UClass {} diff --git a/src/v2_types/mod.rs b/src/v2_types/mod.rs index f584b6f..fc2a119 100644 --- a/src/v2_types/mod.rs +++ b/src/v2_types/mod.rs @@ -6,7 +6,7 @@ //! that the contents might very well change under its feet (which they might). #![allow(non_upper_case_globals)] use bitflags::bitflags; -use std::{cell::UnsafeCell, ptr::NonNull}; +use std::{cell::UnsafeCell, marker::PhantomData, ptr::NonNull}; #[derive(Debug)] #[repr(transparent)] @@ -86,8 +86,8 @@ macro_rules! define_utypes { #[derive(Debug, Clone, Copy)] pub struct $ty(NonNull>); - impl $ty { - pub const fn static_class_name() -> &'static str { + impl traits::StaticClass for $ty { + fn static_class_name() -> &'static str { concat!("Class CoreUObject.", $name) } } @@ -102,35 +102,34 @@ macro_rules! define_utypes { }; } -macro_rules! impl_const_trait_for { - - ($trt:ty: $($ty:ty),+) => { - $( - impl const $trt for $ty {} - )+ - }; -} - macro_rules! impl_asuobject { ($($ty:ty),+) => { $( - impl $ty { - pub const fn from_raw(raw: *mut ()) -> Option { - match NonNull::new(raw) { - Some(ptr) => Some(Self(ptr.cast())), - None => None, - } - } - - pub const fn from_nonnull(raw: NonNull>) -> Self { - Self(raw) + impl Eq for $ty {} + impl PartialEq for $ty where T: AsUObject{ + fn eq(&self, other: &T) -> bool { + self.as_uobject().partial_eq(&other.as_uobject()) } } + unsafe impl Send for $ty {} + unsafe impl Sync for $ty {} + impl const traits::AsUObject for $ty { fn as_uobject(&self) -> self::UObject { self::UObject(self.0) } + + fn from_raw(raw: *mut ()) -> Option { + match NonNull::new(raw) { + Some(ptr) => Some(Self(ptr.cast())), + None => None, + } + } + + fn from_nonnull(raw: NonNull>) -> Self { + Self(raw) + } } )+ }; @@ -189,6 +188,10 @@ impl UObject { const fn raw_mut(&self) -> *mut () { unsafe { self.0.as_ref().get() as _ } } + + fn partial_eq(&self, other: &Self) -> bool { + self.internal_index() == other.internal_index() && self.name() == other.name() + } } #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] @@ -209,19 +212,229 @@ impl UEnum { } } +pub struct OuterObjectIter<'a> { + object: UObject, + phantom: PhantomData<&'a ()>, +} + +impl<'a> OuterObjectIter<'a> { + pub fn new(object: UObject) -> Self { + Self { + object, + phantom: PhantomData, + } + } +} + +impl<'a> Iterator for OuterObjectIter<'a> { + type Item = UObject; + + fn next(&mut self) -> Option { + if let Some(outer) = self.object.outer() { + self.object = *outer; + + Some(self.object) + } else { + None + } + } +} + +pub struct SuperStructIter<'a> { + class: Option, + phantom: PhantomData<&'a ()>, +} + +impl<'a> SuperStructIter<'a> { + pub fn new(class: UStruct) -> Self { + Self { + class: Some(class), + phantom: PhantomData, + } + } + pub fn from_option(class: Option) -> Self { + Self { + class, + phantom: PhantomData, + } + } +} + +impl<'a> Iterator for SuperStructIter<'a> { + type Item = UStruct; + + fn next(&mut self) -> Option { + let next = *self.class?.super_field(); + + core::mem::replace(&mut self.class, next) + } +} + +pub struct SuperClassIter<'a>(SuperStructIter<'a>); + +impl<'a> SuperClassIter<'a> { + pub fn new(class: UClass) -> Self { + Self(SuperStructIter::new(unsafe { class.cast() })) + } + + pub fn from_option(class: Option) -> Self { + Self(SuperStructIter::from_option( + class.map(|c| unsafe { c.cast() }), + )) + } +} + +impl<'a> Iterator for SuperClassIter<'a> { + type Item = UClass; + + fn next(&mut self) -> Option { + self.0.next().map(|class| unsafe { class.cast() }) + } +} + +pub struct ChildIter<'a> { + children: Option, + phantom: PhantomData<&'a ()>, +} + +impl<'a> ChildIter<'a> { + pub fn new(children: Option) -> Self { + Self { + children, + phantom: PhantomData, + } + } +} + +impl<'a> Iterator for ChildIter<'a> { + type Item = UField; + + fn next(&mut self) -> Option { + let next = *self.children?.next(); + + core::mem::replace(&mut self.children, next) + } +} + use traits::*; mod traits { + use std::cell::UnsafeCell; use std::ptr::NonNull; - use crate::core_types::{FName, FString, TArray}; + use anyhow::Context; + use itertools::Itertools; + use once_cell::sync::OnceCell; + + use crate::fname::FName; + use crate::global_tables::objects::{FindClass, GOBJECTS}; + use crate::tarray::{FString, TArray}; use super::{EObjectFlags, VTbl}; - #[const_trait] - pub trait AsUObject { - fn as_uobject(&self) -> super::UObject; + pub trait StaticClass { + fn static_class_name() -> &'static str; + fn static_class() -> super::UClass { + // this is something that we always do in C/C++ but unfortinately requires a blanket impl Sync + Send on all UObject types.. + static CLASS: OnceCell = OnceCell::new(); + + *CLASS.get_or_init(|| { + super::UClass( + GOBJECTS + .read() + .unwrap() + .as_objects() + .unwrap() + .find_class(Self::static_class_name()) + .expect("static class not found!") + .inner + .cast(), + ) + }) + } } + #[const_trait] + pub trait AsUObject: Sized { + fn as_uobject(&self) -> super::UObject; + + fn from_raw(raw: *mut ()) -> Option; + + fn from_nonnull(raw: NonNull>) -> Self; + + unsafe fn cast(&self) -> T + where + T: ~const AsUObject, + { + T::from_nonnull(self.as_uobject().0) + } + } + + pub trait UObjectNonConst: UObjectTrait { + 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) + }) + } + + fn get_name_or_default(&self) -> String { + self.get_name() + .unwrap_or_else(|_| "DefaultObjectName".to_string()) + } + + 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")? + .get_name_or_default(), + tmp, + self.get_name_or_default() + )) + } + + fn get_full_name_or_default(&self) -> String { + self.get_full_name() + .unwrap_or_else(|_| "DefaultObjectFullName".to_string()) + } + + fn iter_outer_objects(&self) -> super::OuterObjectIter { + super::OuterObjectIter::new(self.as_uobject()) + } + + fn package_object(&self) -> super::UObject { + self.iter_outer_objects() + .last() + .unwrap_or(self.as_uobject()) + } + + fn is_a(&self, other: &super::UClass) -> bool { + self.class() + .map(|class| class.iter_super_classes().contains(other)) + .unwrap_or(false) + } + } + + impl UObjectNonConst for T where T: UObjectTrait {} + #[const_trait] pub trait UObjectTrait: ~const AsUObject { fn vtbl(&self) -> &VTbl { @@ -242,6 +455,12 @@ mod traits { fn outer(&self) -> &Option { unsafe { &*self.as_uobject().raw_ptr().offset(32).cast() } } + + // utility + + fn is_package_object(&self) -> bool { + self.outer().is_none() + } } #[const_trait] @@ -280,11 +499,31 @@ mod traits { } } + pub trait UStructNonConst: UStructTrait { + fn iter_super_structs(&self) -> super::SuperStructIter { + super::SuperStructIter::from_option(*self.super_field()) + } + + fn iter_children(&self) -> super::ChildIter { + super::ChildIter::new(*self.children()) + } + } + + impl UStructNonConst for T where T: UStructTrait {} + #[const_trait] pub trait UScriptStructTrait: ~const AsUObject {} + pub trait UClassNonConst: UClassTrait { + fn iter_super_classes(&self) -> super::SuperClassIter { + super::SuperClassIter::from_option(self.super_field().map(|c| unsafe { c.cast() })) + } + } + + impl UClassNonConst for T where T: UClassTrait {} + #[const_trait] - pub trait UClassTrait: ~const AsUObject {} + pub trait UClassTrait: ~const UStructTrait {} #[const_trait] pub trait UFunctionTrait: ~const AsUObject {