extracted fname and tarray from raw types into own modules

- tarray and fname are pretty independent from the rest of the type system
This commit is contained in:
Janis 2023-04-20 02:36:46 +02:00
parent e68aa9ec55
commit 724939e5da
7 changed files with 486 additions and 139 deletions

View file

@ -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"

View file

@ -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<NonNull<UProperty>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UByteProperty {
pub(crate) uproperty: UProperty,
uenum: Option<NonNull<UEnum>>,
}
#[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<NonNull<UClass>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UClassProperty {
pub(crate) uobjectproperty: UObjectProperty,
meta_class: Option<NonNull<UClass>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UAssetClassProperty {
pub(crate) uobjectproperty: UObjectProperty,
meta_class: Option<NonNull<UClass>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UInterfaceProperty {
pub(crate) uproperty: UProperty,
interface_class: Option<NonNull<UClass>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UStructProperty {
pub(crate) uproperty: UProperty,
ustruct: Option<NonNull<UStruct>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UArrayProperty {
pub(crate) uproperty: UProperty,
inner: Option<NonNull<UProperty>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UMapProperty {
pub(crate) uproperty: UProperty,
key_prop: Option<NonNull<UProperty>>,
value_prop: Option<NonNull<UProperty>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UDelegateProperty {
pub(crate) uproperty: UProperty,
signature_function: Option<NonNull<UFunction>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UMulticastDelegateProperty {
pub(crate) uproperty: UProperty,
signature_function: Option<NonNull<UFunction>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UEnumProperty {
pub(crate) uproperty: UProperty,
underlying_type: Option<NonNull<UProperty>>,
uenum: Option<NonNull<UEnum>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UFunction {
@ -141,110 +229,3 @@ pub struct UFunction {
first_property_to_init: Option<NonNull<UProperty>>,
function: Option<NonNull<()>>,
}
pub mod tarray {
use std::{ops::Index, ptr::NonNull, slice::SliceIndex};
#[repr(C)]
#[derive(Debug)]
pub struct TArray<T> {
data: Option<NonNull<T>>,
count: u32,
max: u32,
}
unsafe impl<T> Send for TArray<T> where T: Send {}
unsafe impl<T> Sync for TArray<T> where T: Sync {}
pub type FString = TArray<u16>;
impl ToString for FString {
fn to_string(&self) -> String {
widestring::U16CStr::from_slice(&self)
.expect("invalid utf16 string")
.to_string_lossy()
}
}
impl<T> TArray<T> {
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<T, I: SliceIndex<[T]>> Index<I> for TArray<T> {
type Output = I::Output;
fn index(&self, i: I) -> &Self::Output {
let data = self.as_slice();
&data[i]
}
}
impl<T> std::ops::Deref for TArray<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
pub struct IntoIter<T> {
//array_ref: &'a TArray<T>,
ptr: *const T,
end: *const T,
}
impl<'a, T> IntoIter<T> {
pub fn new(array: &'a TArray<T>) -> Self {
let ptr = array.data.unwrap().as_ptr();
Self {
ptr,
end: unsafe { ptr.offset(array.count as isize) },
}
}
}
impl<'a, T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
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<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}
}

19
src/fname.rs Normal file
View file

@ -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<String> {
GNAMES
.read()
.unwrap()
.as_names()
.unwrap()
.fname_to_string(self)
}
}

View file

@ -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,
};

104
src/tarray.rs Normal file
View file

@ -0,0 +1,104 @@
use std::{ops::Index, ptr::NonNull, slice::SliceIndex};
#[repr(C)]
#[derive(Debug)]
pub struct TArray<T> {
data: Option<NonNull<T>>,
count: u32,
max: u32,
}
unsafe impl<T> Send for TArray<T> where T: Send {}
unsafe impl<T> Sync for TArray<T> where T: Sync {}
pub type FString = TArray<u16>;
impl ToString for FString {
fn to_string(&self) -> String {
widestring::U16CStr::from_slice(&self)
.expect("invalid utf16 string")
.to_string_lossy()
}
}
impl<T> TArray<T> {
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<T, I: SliceIndex<[T]>> Index<I> for TArray<T> {
type Output = I::Output;
fn index(&self, i: I) -> &Self::Output {
let data = self.as_slice();
&data[i]
}
}
impl<T> std::ops::Deref for TArray<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
pub struct IntoIter<T> {
//array_ref: &'a TArray<T>,
ptr: *const T,
end: *const T,
}
impl<'a, T> IntoIter<T> {
pub fn new(array: &'a TArray<T>) -> Self {
let ptr = array.data.unwrap().as_ptr();
Self {
ptr,
end: unsafe { ptr.offset(array.count as isize) },
}
}
}
impl<'a, T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
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<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}

View file

@ -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<core_types::UClass>,
pub(crate) inner: NonNull<core_types::UClass>,
}
impl Eq for UClass {}

View file

@ -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<UnsafeCell<()>>);
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<Self> {
match NonNull::new(raw) {
Some(ptr) => Some(Self(ptr.cast())),
None => None,
}
}
pub const fn from_nonnull(raw: NonNull<UnsafeCell<()>>) -> Self {
Self(raw)
impl Eq for $ty {}
impl<T> PartialEq<T> 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<Self> {
match NonNull::new(raw) {
Some(ptr) => Some(Self(ptr.cast())),
None => None,
}
}
fn from_nonnull(raw: NonNull<UnsafeCell<()>>) -> 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<Self::Item> {
if let Some(outer) = self.object.outer() {
self.object = *outer;
Some(self.object)
} else {
None
}
}
}
pub struct SuperStructIter<'a> {
class: Option<UStruct>,
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<UStruct>) -> Self {
Self {
class,
phantom: PhantomData,
}
}
}
impl<'a> Iterator for SuperStructIter<'a> {
type Item = UStruct;
fn next(&mut self) -> Option<Self::Item> {
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<UClass>) -> 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::Item> {
self.0.next().map(|class| unsafe { class.cast() })
}
}
pub struct ChildIter<'a> {
children: Option<UField>,
phantom: PhantomData<&'a ()>,
}
impl<'a> ChildIter<'a> {
pub fn new(children: Option<UField>) -> Self {
Self {
children,
phantom: PhantomData,
}
}
}
impl<'a> Iterator for ChildIter<'a> {
type Item = UField;
fn next(&mut self) -> Option<Self::Item> {
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<super::UClass> = 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<Self>;
fn from_nonnull(raw: NonNull<UnsafeCell<()>>) -> Self;
unsafe fn cast<T>(&self) -> T
where
T: ~const AsUObject,
{
T::from_nonnull(self.as_uobject().0)
}
}
pub trait UObjectNonConst: UObjectTrait {
fn get_name(&self) -> anyhow::Result<String> {
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<String> {
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<T> 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<super::UObject> {
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<T> 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<T> 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 {