split into multiple files

This commit is contained in:
Janis 2023-04-17 18:46:27 +02:00
parent b7803bd453
commit 3d3b1b20c9
6 changed files with 1346 additions and 1358 deletions

237
src/core_types.rs Normal file
View file

@ -0,0 +1,237 @@
#![allow(non_upper_case_globals)]
use bitflags::bitflags;
use std::ptr::NonNull;
#[derive(Debug)]
#[repr(transparent)]
pub struct VTbl(NonNull<()>);
impl VTbl {}
bitflags! {
#[repr(C)]
pub struct EObjectFlags: u32 {
const RF_NoFlags = 0x00000000;
const RF_Public = 0x00000001;
const RF_Standalone = 0x00000002;
const RF_MarkAsNative = 0x00000004;
const RF_Transactional = 0x00000008;
const RF_ClassDefaultObject = 0x00000010;
const RF_ArchetypeObject = 0x00000020;
const RF_Transient = 0x00000040;
const RF_MarkAsRootSet = 0x00000080;
const RF_TagGarbageTemp = 0x00000100;
const RF_NeedInitialization = 0x00000200;
const RF_NeedLoad = 0x00000400;
const RF_KeepForCooker = 0x00000800;
const RF_NeedPostLoad = 0x00001000;
const RF_NeedPostLoadSubobjects = 0x00002000;
const RF_NewerVersionExists = 0x00004000;
const RF_BeginDestroyed = 0x00008000;
const RF_FinishDestroyed = 0x00010000;
const RF_BeingRegenerated = 0x00020000;
const RF_DefaultSubObject = 0x00040000;
const RF_WasLoaded = 0x00080000;
const RF_TextExportTransient = 0x00100000;
const RF_LoadCompleted = 0x00200000;
const RF_InheritableComponentTemplate = 0x00400000;
const RF_DuplicateTransient = 0x00800000;
const RF_StrongRefOnFrame = 0x01000000;
const RF_NonPIEDuplicateTransient = 0x02000000;
const RF_Dynamic = 0x04000000;
const RF_WillBeLoaded = 0x08000000;
const RF_HasExternalPackage = 0x10000000;
}
}
pub use tarray::{FString, TArray};
use crate::global_tables::names::GNAMES;
#[repr(C)]
#[derive(Debug)]
pub struct UObject {
pub vtbl: VTbl,
pub object_flags: EObjectFlags, //EObjectFlags,
pub internal_index: u32,
pub class: Option<NonNull<UClass>>,
pub name: FName,
pub outer: Option<NonNull<UObject>>,
}
#[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)
}
}
#[repr(C)]
#[derive(Debug)]
pub struct UField {
pub uobject: UObject,
pub next: Option<NonNull<UField>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UEnum {
pub ufield: UField,
pub cpp_type: FString,
pub names: TArray<FName>,
pub cpp_form: u32,
}
#[repr(C)]
#[derive(Debug)]
pub struct UStruct {
pub(crate) ufield: UField,
pub(crate) super_field: Option<NonNull<UStruct>>,
pub(crate) children: Option<NonNull<UField>>,
pub(crate) property_size: u32,
pub(crate) min_alignment: u32,
padding1: [u8; 0x48],
}
#[repr(C)]
#[derive(Debug)]
pub struct UClass {
pub(crate) ustruct: UStruct,
padding1: [u8; 0xF8],
}
#[repr(C)]
#[derive(Debug)]
pub struct UProperty {
pub(crate) ufield: UField,
array_dim: i32,
element_size: i32,
property_flags: u64,
rep_index: i16,
rep_notify_function: FName,
offset: i32,
property_link_next: Option<NonNull<UProperty>>,
next_ref: Option<NonNull<UProperty>>,
destructor_link_next: Option<NonNull<UProperty>>,
post_construct_link_next: Option<NonNull<UProperty>>,
}
#[repr(C)]
#[derive(Debug)]
pub struct UFunction {
pub(crate) ustruct: UStruct,
function_flags: u32,
rep_offset: u16,
num_params: u8,
params_size: u16,
return_value_offset: u16,
rpc_id: u16,
rpc_response_id: u16,
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
}
}
impl<T, I: SliceIndex<[T]>> Index<I> for TArray<T> {
type Output = I::Output;
fn index(&self, i: I) -> &Self::Output {
let data =
unsafe { std::slice::from_raw_parts(self.data.unwrap().as_ptr(), self.len()) };
&data[i]
}
}
impl<T> std::ops::Deref for TArray<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { std::slice::from_raw_parts(self.data.unwrap().as_ptr(), self.len()) }
}
}
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)
}
}
}

3
src/global_tables/mod.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod names;
pub mod objects;

178
src/global_tables/names.rs Normal file
View file

@ -0,0 +1,178 @@
use std::{ops::Index, ptr::NonNull, sync::RwLock};
use crate::core_types::FName;
lazy_static::lazy_static! {
pub static ref GNAMES: RwLock<GNames> = RwLock::new(GNames::new());
}
#[derive(Debug, Default)]
pub struct GNames {
names: Option<NonNull<NameEntryArray>>,
}
impl GNames {
pub const fn new() -> Self {
Self { names: None }
}
pub fn set_names(&mut self, names: NonNull<NameEntryArray>) {
self.names = Some(names);
}
pub fn as_names(&self) -> Option<&NameEntryArray> {
self.names.map(|names| unsafe { names.as_ref() })
}
}
unsafe impl Send for GNames {}
unsafe impl Sync for GNames {}
#[repr(C)]
#[derive(Debug)]
pub struct FNameEntry {
pub index: u32,
pub hash_next: *const FNameEntry,
pub ansi_name: [u8; 1024],
}
impl FNameEntry {
/// panics if the name cant be turned into a string
pub fn as_string(&self) -> String {
self.try_into().unwrap()
}
pub fn as_str(&self) -> &str {
let end = self.ansi_name.iter().position(|&c| c == 0);
let bytes = &self.ansi_name[..end.unwrap_or(self.ansi_name.len())];
// TODO: debug assert this
unsafe { std::str::from_utf8_unchecked(bytes) }
}
}
impl<'a> TryInto<String> for &'a FNameEntry {
type Error = std::string::FromUtf8Error;
fn try_into(self) -> Result<String, Self::Error> {
let bytes = self
.ansi_name
.iter()
.take_while(|&&b| b != 0)
.map(|&b| b)
.collect::<Vec<_>>();
String::from_utf8(bytes)
}
}
const NAMES_ELEMENTS_PER_CHUNK: usize = 16 * 1024;
const NAMES_CHUNK_TABLE_SIZE: usize =
(2 * 1024 * 1024 + NAMES_ELEMENTS_PER_CHUNK - 1) / NAMES_ELEMENTS_PER_CHUNK;
#[repr(C)]
#[derive(Debug)]
pub struct NameEntryArray {
chunks: [Option<NonNull<Option<NonNull<FNameEntry>>>>; NAMES_CHUNK_TABLE_SIZE],
num_elements: u32,
num_chunks: u32,
}
unsafe impl Send for NameEntryArray {}
unsafe impl Sync for NameEntryArray {}
impl NameEntryArray {
/// this does not mean that the name at this index is actually present!, just that looking it up is sound, because all the indices point at allocated memory
pub fn is_valid_index(&self, index: usize) -> bool {
index < self.num_elements as usize
}
/// panics if chunk_index is out of bounds
fn chunk_as_slice(&self, chunk_index: usize) -> Option<&[Option<NonNull<FNameEntry>>]> {
// TODO: probably make this unchecked for release?
self.chunks
// get chunk
[chunk_index]
// cast chunk pointer into slice of FNameEntry pointers
.map(|ptr| unsafe {
core::slice::from_raw_parts(ptr.as_ptr(), NAMES_ELEMENTS_PER_CHUNK)
})
}
pub fn get_index(&self, index: usize) -> Option<&FNameEntry> {
if index < self.num_elements as usize {
let chunk_index = index / NAMES_ELEMENTS_PER_CHUNK;
let within_chunk_index = index % NAMES_ELEMENTS_PER_CHUNK;
// safety: we know chunk_index and within_chunk_index are valid (or do we?);
// TODO: make sure this is actually the case
// it is because index is < than self.num_elements
self.chunk_as_slice(chunk_index)?[within_chunk_index].map(|ptr| unsafe { ptr.as_ref() })
} else {
None
}
}
pub fn iter(&self) -> NamesIterator {
NamesIterator::new(self)
}
pub fn len(&self) -> usize {
self.num_elements as usize
}
pub fn fname_to_string(&self, name: &FName) -> anyhow::Result<String> {
use anyhow::Context;
Ok(self
.get_index(name.comparison_index as usize)
.context("invalid comparison index")?
.try_into()?)
}
}
impl Index<usize> for NameEntryArray {
type Output = FNameEntry;
fn index(&self, i: usize) -> &Self::Output {
self.get_index(i).expect("Out of bounds access")
}
}
pub struct NamesIterator<'a> {
names: &'a NameEntryArray,
index: usize,
}
impl<'a> NamesIterator<'a> {
pub fn new(names: &'a NameEntryArray) -> Self {
Self { names, index: 0 }
}
}
impl<'a> Iterator for NamesIterator<'a> {
type Item = &'a FNameEntry;
fn next(&mut self) -> Option<Self::Item> {
loop {
if !self.names.is_valid_index(self.index) {
break None;
}
let item = self.names.get_index(self.index);
self.index += 1;
// skip empty entries, we dont care about them
if item.is_some() {
break item;
}
}
}
}
impl<'a> IntoIterator for &'a NameEntryArray {
type Item = &'a FNameEntry;
type IntoIter = NamesIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
NamesIterator {
names: self,
index: 0,
}
}
}

View file

@ -0,0 +1,254 @@
use std::{ops::Index, ptr::NonNull, sync::RwLock};
use rayon::prelude::ParallelIterator;
use crate::types;
lazy_static::lazy_static! {
pub static ref GOBJECTS: RwLock<GObjects> = RwLock::new(GObjects::new());
}
#[derive(Debug, Default)]
pub struct GObjects {
objects: Option<NonNull<ObjectArray>>,
}
impl GObjects {
pub const fn new() -> Self {
Self { objects: None }
}
pub fn set_objects(&mut self, objects: NonNull<ObjectArray>) {
self.objects = Some(objects);
}
pub fn as_objects(&self) -> Option<&ObjectArray> {
self.objects.map(|objects| unsafe { objects.as_ref() })
}
}
unsafe impl Send for GObjects {}
unsafe impl Sync for GObjects {}
#[repr(C)]
#[derive(Debug)]
pub struct ObjectArrayItem {
object: Option<NonNull<crate::core_types::UObject>>,
sn: u32,
}
unsafe impl Send for ObjectArrayItem {}
unsafe impl Sync for ObjectArrayItem {}
impl ObjectArrayItem {
pub fn object(&self) -> Option<types::UObject> {
types::UObject::maybe_new(self.object)
}
}
#[repr(C)]
#[derive(Debug)]
pub struct ObjectArrayInner {
objects: *const *const ObjectArrayItem,
pre_allocated_objects: Option<NonNull<ObjectArrayItem>>,
max_elements: u32,
num_elements: u32,
max_chunks: u32,
num_chunks: u32,
}
impl ObjectArrayInner {
const ELEMENTS_PER_CHUNK: usize = 64 * 1024;
fn chunks_as_slice(&self) -> &[*const ObjectArrayItem] {
unsafe { std::slice::from_raw_parts(self.objects, self.num_chunks as usize) }
}
pub fn get_chunks(&self) -> Vec<&[ObjectArrayItem]> {
(0..self.num_chunks as usize)
.map(|i| self.chunk_as_slice(i))
.collect()
}
fn get_chunk_size(&self, chunk_index: usize) -> Option<usize> {
if chunk_index < self.num_chunks as usize {
if chunk_index * Self::ELEMENTS_PER_CHUNK <= self.num_elements as usize {
Some(Self::ELEMENTS_PER_CHUNK)
} else {
Some(self.num_elements as usize % Self::ELEMENTS_PER_CHUNK)
}
} else {
None
}
}
fn chunk_as_slice(&self, chunk_index: usize) -> &[ObjectArrayItem] {
let chunks = self.chunks_as_slice();
let chunk = unsafe {
std::slice::from_raw_parts(
chunks[chunk_index],
self.get_chunk_size(chunk_index).unwrap(),
)
};
chunk
}
/// returns the chunk index and the index inside that chunk, or none if the index is out of range
fn get_chunked_index(&self, i: usize) -> Option<(usize, usize)> {
if i < self.num_elements as usize {
let chunk_index = i / Self::ELEMENTS_PER_CHUNK;
let within_chunk_index = i % Self::ELEMENTS_PER_CHUNK;
Some((chunk_index, within_chunk_index))
} else {
None
}
}
pub fn get_index(&self, i: usize) -> Option<&ObjectArrayItem> {
self.get_chunked_index(i)
.map(|(chunk_index, within_chunk_index)| {
&self.chunk_as_slice(chunk_index)[within_chunk_index]
})
}
}
#[repr(C)]
#[derive(Debug)]
pub struct ObjectArray {
first_gc_index: u32,
last_non_gc_index: u32,
max_objects_not_considered_by_gc: u32,
open_for_disregard_for_gc: bool,
inner: ObjectArrayInner,
}
unsafe impl Send for ObjectArray {}
unsafe impl Sync for ObjectArray {}
impl ObjectArray {
pub fn len(&self) -> usize {
self.inner.num_elements as usize
}
pub fn iter(&self) -> iter::ObjectArrayIterator<'_> {
iter::ObjectArrayIterator::new(self)
}
pub fn iter_package_objects(&self) -> impl Iterator<Item = &'_ ObjectArrayItem> {
self.iter()
.filter(|entry| matches!(entry.object(), Some(object) if object.is_package_object()))
}
pub fn par_iter(&self) -> par_iter::ObjectArrayChunkedParallelIterator<'_> {
par_iter::ObjectArrayChunkedParallelIterator::new(self.inner.get_chunks())
}
pub fn par_iter_package_objects(&self) -> impl ParallelIterator<Item = &'_ ObjectArrayItem> {
self.par_iter()
.filter(|entry| matches!(entry.object(), Some(object) if object.is_package_object()))
}
}
impl Index<usize> for ObjectArray {
type Output = ObjectArrayItem;
fn index(&self, i: usize) -> &Self::Output {
self.inner.get_index(i).expect("Out of bounds access")
}
}
pub mod iter {
use super::{ObjectArray, ObjectArrayItem};
pub struct ObjectArrayIterator<'a> {
objects: &'a ObjectArray,
index: usize,
}
impl<'a> ObjectArrayIterator<'a> {
pub(super) fn new(objects: &'a ObjectArray) -> Self {
Self { objects, index: 0 }
}
}
impl<'a> Iterator for ObjectArrayIterator<'a> {
type Item = &'a ObjectArrayItem;
fn next(&mut self) -> Option<Self::Item> {
let item = if self.index < self.objects.len() {
Some(&self.objects[self.index])
} else {
None
};
self.index += 1;
item
}
}
impl<'a> IntoIterator for &'a ObjectArray {
type Item = &'a ObjectArrayItem;
type IntoIter = ObjectArrayIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
ObjectArrayIterator {
objects: self,
index: 0,
}
}
}
}
pub mod par_iter {
pub use rayon::iter::{IntoParallelIterator, ParallelIterator};
use super::ObjectArrayItem;
pub struct ObjectArrayChunkedParallelIterator<'a> {
chunks: Vec<&'a [ObjectArrayItem]>,
}
impl<'a> ObjectArrayChunkedParallelIterator<'a> {
pub fn new(chunks: Vec<&'a [ObjectArrayItem]>) -> Self {
Self { chunks }
}
}
impl<'a> ParallelIterator for ObjectArrayChunkedParallelIterator<'a> {
type Item = &'a ObjectArrayItem;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: rayon::iter::plumbing::UnindexedConsumer<Self::Item>,
{
self.chunks
.into_par_iter()
.flatten_iter()
.drive_unindexed(consumer)
}
}
}
pub trait FindClass {
fn find_class<S>(&self, class_name: S) -> Option<types::UClass>
where
S: Into<String>;
}
impl FindClass for ObjectArray {
fn find_class<S>(&self, class_name: S) -> Option<types::UClass>
where
S: Into<String>,
{
let class_name = class_name.into();
self.iter()
.filter_map(|item| item.object())
.filter(|object| object.get_full_name().unwrap() == class_name)
.map(|object| types::UClass::new(unsafe { object.cast() }))
.next()
}
}

1361
src/lib.rs

File diff suppressed because it is too large Load diff

671
src/types.rs Normal file
View file

@ -0,0 +1,671 @@
use anyhow::Context;
use itertools::Itertools;
use std::{hash::Hash, ops::Deref, ptr::NonNull};
use crate::core_types::{self, FName, TArray};
use self::traits::{AsUObject, AsUStruct, FromRaw};
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct UObject {
inner: NonNull<core_types::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<H: std::hash::Hasher>(&self, state: &mut H) {
self.internal_index().hash(state);
self.name().hash(state);
}
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct UClass {
inner: NonNull<core_types::UClass>,
}
impl Deref for UClass {
type Target = UObject;
fn deref(&self) -> &Self::Target {
unsafe { std::mem::transmute(&self) }
}
}
impl Eq for UClass {}
impl PartialEq for UClass {
fn eq(&self, other: &Self) -> bool {
self.deref().eq(other)
}
}
impl Hash for UClass {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.deref().hash(state);
}
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct UField {
inner: NonNull<core_types::UField>,
}
impl Deref for UField {
type Target = UObject;
fn deref(&self) -> &Self::Target {
unsafe { std::mem::transmute(&self) }
}
}
impl Eq for UField {}
impl PartialEq for UField {
fn eq(&self, other: &Self) -> bool {
self.deref().eq(other)
}
}
impl Hash for UField {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.deref().hash(state);
}
}
impl UObject {
pub fn new(inner: NonNull<core_types::UObject>) -> Self {
Self { inner }
}
pub fn maybe_new(inner: Option<NonNull<core_types::UObject>>) -> Option<Self> {
inner.map(|inner| Self { inner })
}
pub fn to_inner(self) -> NonNull<core_types::UObject> {
self.inner
}
pub const unsafe fn cast<T>(self) -> NonNull<T> {
self.inner.cast::<T>()
}
/// 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> {
Self::maybe_new(NonNull::new(raw))
}
pub fn class(&self) -> Option<UClass> {
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<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)
})
}
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<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")?
.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<UObject> {
unsafe { self.inner.as_ref().outer.map(|inner| UObject::new(inner)) }
}
/// returns the package object of this object
pub fn outermost(&self) -> Option<UObject> {
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<Self::Item> {
if let Some(outer) = self.object.outer() {
self.object = outer.clone();
Some(outer)
} else {
None
}
}
}
impl From<UObject> 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<core_types::UClass> for UClass {
fn as_ref(&self) -> &core_types::UClass {
unsafe { self.inner.as_ref() }
}
}
impl FromRaw<core_types::UClass> for UClass {
fn from_non_null(inner: NonNull<core_types::UClass>) -> Self {
Self::new(inner)
}
}
impl UClass {
pub fn new(inner: NonNull<core_types::UClass>) -> 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<Self::Item> {
if let Some(next) = self
.class
.as_ref()
.ustruct
.super_field
.map(|inner| inner.cast::<core_types::UClass>())
.map(|inner| UClass::new(inner))
{
self.class = next.clone();
Some(next)
} else {
None
}
}
}
impl From<UObject> for UField {
fn from(obj: UObject) -> Self {
Self::new(obj.inner.cast())
}
}
impl UField {
pub fn new(inner: NonNull<core_types::UField>) -> Self {
Self { inner }
}
pub fn maybe_new(inner: Option<NonNull<core_types::UField>>) -> Option<Self> {
inner.map(|inner| Self { inner })
}
pub fn maybe_with_raw(raw: *mut core_types::UField) -> Option<Self> {
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> {
Self::maybe_new(unsafe { self.as_ref().next })
}
}
#[derive(Debug, Clone)]
pub struct UEnum {
inner: NonNull<core_types::UEnum>,
}
impl From<UObject> for UEnum {
fn from(obj: UObject) -> Self {
Self::new(obj.inner.cast())
}
}
impl UEnum {
pub fn new(inner: NonNull<core_types::UEnum>) -> Self {
Self { inner }
}
pub fn maybe_new(inner: Option<NonNull<core_types::UEnum>>) -> Option<Self> {
inner.map(|inner| Self { inner })
}
pub fn maybe_with_raw(raw: *mut core_types::UEnum) -> Option<Self> {
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<FName> {
unsafe { &self.as_ref().names }
}
}
#[derive(Debug, Clone)]
pub struct UStruct {
inner: NonNull<core_types::UStruct>,
}
impl From<UObject> for UStruct {
fn from(obj: UObject) -> Self {
Self::new(obj.inner.cast())
}
}
impl FromRaw<core_types::UStruct> for UStruct {
fn from_non_null(inner: NonNull<core_types::UStruct>) -> Self {
Self::new(inner)
}
}
impl UStruct {
pub fn new(inner: NonNull<core_types::UStruct>) -> Self {
Self { inner }
}
pub unsafe fn as_raw(&self) -> &core_types::UStruct {
self.inner.as_ref()
}
fn super_struct(&self) -> Option<Self> {
Self::from_maybe_non_null(unsafe { self.as_raw().super_field })
}
fn children(&self) -> Option<UField> {
UField::maybe_new(unsafe { self.as_raw().children })
}
fn iter_fields(&self) -> Option<UStructFieldIterator> {
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<Self::Item> {
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<core_types::UStruct>,
}
impl From<UObject> for UScriptStruct {
fn from(obj: UObject) -> Self {
Self::new(obj.inner.cast())
}
}
impl UScriptStruct {
pub fn new(inner: NonNull<core_types::UStruct>) -> Self {
Self { inner }
}
pub fn maybe_new(inner: Option<NonNull<core_types::UStruct>>) -> Option<Self> {
inner.map(|inner| Self { inner })
}
pub fn maybe_with_raw(raw: *mut core_types::UStruct) -> Option<Self> {
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<UStructFieldIterator> {
UField::maybe_new(unsafe { self.as_ref().children })
.map(|field| UStructFieldIterator::new(field))
}
}
#[derive(Debug, Clone)]
pub struct UProperty {
inner: NonNull<core_types::UProperty>,
}
impl From<UObject> 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<core_types::UProperty>) -> Self {
Self { inner }
}
pub fn maybe_new(inner: Option<NonNull<core_types::UProperty>>) -> Option<Self> {
inner.map(|inner| Self { inner })
}
pub fn maybe_with_raw(raw: *mut core_types::UProperty) -> Option<Self> {
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<core_types::UFunction>,
}
impl From<UObject> 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<core_types::UFunction>) -> Self {
Self { inner }
}
pub fn maybe_new(inner: Option<NonNull<core_types::UFunction>>) -> Option<Self> {
inner.map(|inner| Self { inner })
}
pub fn maybe_with_raw(raw: *mut core_types::UFunction) -> Option<Self> {
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<H: std::hash::Hasher>(&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<T>) -> &'a NonNull<U> {
std::mem::transmute(ptr)
}
pub trait AsUObject: Sized {
fn as_uobject(&self) -> UObject;
fn class(&self) -> Option<UClass> {
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<String> {
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<String> {
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<UObject> {
self.as_uobject().outer()
}
fn outermost(&self) -> Option<UObject> {
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<UField> {
self.as_ustruct().children()
}
#[inline]
fn super_struct(&self) -> Option<UStruct> {
self.as_ustruct().super_struct()
}
#[inline]
fn iter_fields(&self) -> Option<UStructFieldIterator> {
self.children()
.map(|field| UStructFieldIterator::new(field))
}
}
impl<T> AsUObject for T
where
T: AsUStruct,
{
fn as_uobject(&self) -> UObject {
UObject::new(self.as_ustruct().inner.cast())
}
}
pub trait FromRaw<R> {
fn from_non_null(inner: NonNull<R>) -> Self;
fn from_raw(raw: *mut R) -> Option<Self>
where
Self: Sized,
{
NonNull::new(raw).map(|inner| Self::from_non_null(inner))
}
fn from_maybe_non_null(inner: Option<NonNull<R>>) -> Option<Self>
where
Self: Sized,
{
inner.map(|inner| Self::from_non_null(inner))
}
}
}