split into multiple files
This commit is contained in:
parent
b7803bd453
commit
3d3b1b20c9
237
src/core_types.rs
Normal file
237
src/core_types.rs
Normal 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
3
src/global_tables/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod names;
|
||||||
|
|
||||||
|
pub mod objects;
|
178
src/global_tables/names.rs
Normal file
178
src/global_tables/names.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
254
src/global_tables/objects.rs
Normal file
254
src/global_tables/objects.rs
Normal 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
1361
src/lib.rs
File diff suppressed because it is too large
Load diff
671
src/types.rs
Normal file
671
src/types.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue