parsing classes, structs and enums

This commit is contained in:
Janis 2023-04-20 22:24:26 +02:00
parent a574191066
commit 08149329e1
2 changed files with 359 additions and 87 deletions

View file

@ -23,6 +23,7 @@ pub mod sdk {
use anyhow::Context; use anyhow::Context;
use itertools::any; use itertools::any;
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use crate::{ use crate::{
global_tables::objects::{FindClass, GOBJECTS}, global_tables::objects::{FindClass, GOBJECTS},
@ -30,10 +31,12 @@ pub mod sdk {
any_type::{self, AnyProperty}, any_type::{self, AnyProperty},
traits::{ traits::{
AsUObject, StaticClass, UArrayPropertyTrait, UBytePropertyTrait, AsUObject, StaticClass, UArrayPropertyTrait, UBytePropertyTrait,
UEnumPropertyTrait, UEnumTrait, UObjectNonConst, UObjectPropertyBaseTrait, UEnumPropertyTrait, UEnumTrait, UFunctionTrait, UObjectNonConst,
UObjectTrait, UPropertyTrait, UStructNonConst, UStructPropertyTrait, UStructTrait, UObjectPropertyBaseTrait, UObjectTrait, UPropertyTrait, UStructNonConst,
UStructPropertyTrait, UStructTrait,
}, },
AnyProp, UAnyType, UClass, UEnum, UObject, UProperty, UScriptStruct, UStruct, AnyProp, EFunctionFlags, EPropertyFlags, UAnyType, UClass, UEnum, UFunction, UObject,
UProperty, UScriptStruct, UStruct,
}, },
}; };
@ -93,6 +96,13 @@ pub mod sdk {
dependencies: Vec<UObject>, dependencies: Vec<UObject>,
} }
#[derive(Debug)]
pub struct ProcessedPackage {
pub package: UObject,
pub types: HashMap<UObject, Types>,
pub package_dependencies: Vec<UObject>,
}
impl<'a> Package<'a> { impl<'a> Package<'a> {
pub fn new(sdk: &'a Sdk, package: UObject, objects: Vec<UObject>) -> Self { pub fn new(sdk: &'a Sdk, package: UObject, objects: Vec<UObject>) -> Self {
Self { Self {
@ -103,57 +113,60 @@ pub mod sdk {
} }
} }
pub fn process(&self) -> anyhow::Result<()> { pub fn process(self) -> ProcessedPackage {
for object in self.objects.iter() { let types = self
self.sdk .objects
.cache .par_iter()
.cache_class(object.class().expect("object had no class?")) .filter_map(|&object| {
.context("object not of any base type?")?; let ty = UAnyType::from_uobject(object.as_uobject());
log::trace!("ty: {ty:?}");
let ty = UAnyType::from_uobject(object.as_uobject()); match ty {
log::trace!("ty: {ty:?}"); UAnyType::UClass(class) => {
if let Ok(class) = self.process_struct(unsafe { class.cast() }) {
return Some((object, Types::Class(class)));
}
}
UAnyType::UScriptStruct(class) => {
if let Ok(class) = self.process_struct(unsafe { class.cast() }) {
return Some((object, Types::Class(class)));
}
}
UAnyType::UEnum(obj) => {
if let Ok(enm) = Self::process_enum(obj) {
return Some((object, Types::Enum(enm)));
}
}
_ => {}
}
None
})
.collect::<HashMap<_, _>>();
match ty { ProcessedPackage {
UAnyType::UClass(class) => { package: self.package,
let _ = self.process_struct(unsafe { class.cast() }); types,
} package_dependencies: self.dependencies,
UAnyType::UScriptStruct(class) => {
let _ = self.process_struct(unsafe { class.cast() });
}
UAnyType::UEnum(obj) => {
let enm = Self::process_enum(obj)?;
log::info!("enum: {enm}");
}
_ => {}
}
} }
Ok(())
} }
fn process_struct(&self, strct: UStruct) -> anyhow::Result<()> { fn process_struct(&self, strct: UStruct) -> anyhow::Result<Class> {
let name = strct
.get_name()
.context("failed to get struct or class name")?;
let is_struct = strct.is_a(&UScriptStruct::static_class().unwrap()); let is_struct = strct.is_a(&UScriptStruct::static_class().unwrap());
let is_class = strct.is_a(&UClass::static_class().unwrap()); let is_class = strct.is_a(&UClass::static_class().unwrap());
if !(is_struct || is_class) {
// TODO: skip anything that isnt a struct or class
log::info!("skipping {strct:?} as its not a struct or class");
return Ok(());
}
if strct.package_object() != self.package { // process outers for classes
log::info!("encountered external dependency"); if !is_struct {
// TODO: return dependency on strct.package_object() if let Some(outer) = strct.outer() {
} else { if outer.is_a(&UStruct::static_class().unwrap()) {
// process outers for classes // depend on this
if !is_struct {
if let Some(outer) = strct.outer() {
if outer.is_a(&UStruct::static_class().unwrap()) {
self.process_struct(unsafe { outer.cast() })?;
}
} }
} }
}
let super_struct = if let Some(spr) = *strct.super_field() && spr != strct { let super_struct = if let Some(spr) = *strct.super_field() && spr != strct {
if spr.package_object() != self.package { if spr.package_object() != self.package {
log::info!("encountered external dependency"); log::info!("encountered external dependency");
// TODO: return dependency on strct.package_object() // TODO: return dependency on strct.package_object()
@ -161,38 +174,41 @@ pub mod sdk {
Some(spr) Some(spr)
} else {None}; } else {None};
self.process_children(strct)?; let (fields, methods) = self.process_children(strct)?;
}
Ok(()) Ok(Class {
is_class,
name,
super_class: super_struct,
fields,
methods,
})
} }
fn find_type(prop: AnyProperty) -> anyhow::Result<Type> { fn find_type(prop: AnyProperty) -> anyhow::Result<Type> {
let ty = match prop { let ty = match prop.clone() {
numeric @ AnyProperty::Numeric(_) => { numeric @ AnyProperty::Numeric(_) => {
Type::Primitive(match numeric.as_any_numeric_property().unwrap() { match numeric.as_any_numeric_property().unwrap() {
any_type::AnyNumericProperty::U8(prop) => match prop.uenum() { any_type::AnyNumericProperty::U8(prop) => match prop.uenum() {
Some(enm) => { Some(enm) => Type::Enum {
return Ok(Type::Enum { underlying: Box::new(Type::Primitive(PrimitiveType::U8)),
underlying: Box::new(Type::Primitive(PrimitiveType::U8)), enum_type: *enm,
enum_type: *enm, },
}) None => Type::Primitive(PrimitiveType::U8),
}
None => PrimitiveType::U8,
}, },
any_type::AnyNumericProperty::U16(_) => PrimitiveType::U16, any_type::AnyNumericProperty::U16(_) => Type::Primitive(PrimitiveType::U16),
any_type::AnyNumericProperty::U32(_) => PrimitiveType::U32, any_type::AnyNumericProperty::U32(_) => Type::Primitive(PrimitiveType::U32),
any_type::AnyNumericProperty::U64(_) => PrimitiveType::U64, any_type::AnyNumericProperty::U64(_) => Type::Primitive(PrimitiveType::U64),
any_type::AnyNumericProperty::I8(_) => PrimitiveType::I8, any_type::AnyNumericProperty::I8(_) => Type::Primitive(PrimitiveType::I8),
any_type::AnyNumericProperty::I16(_) => PrimitiveType::I16, any_type::AnyNumericProperty::I16(_) => Type::Primitive(PrimitiveType::I16),
any_type::AnyNumericProperty::I32(_) => PrimitiveType::I32, any_type::AnyNumericProperty::I32(_) => Type::Primitive(PrimitiveType::I32),
any_type::AnyNumericProperty::I64(_) => PrimitiveType::I64, any_type::AnyNumericProperty::I64(_) => Type::Primitive(PrimitiveType::I64),
any_type::AnyNumericProperty::F32(_) => PrimitiveType::F32, any_type::AnyNumericProperty::F32(_) => Type::Primitive(PrimitiveType::F32),
any_type::AnyNumericProperty::F64(_) => PrimitiveType::F64, any_type::AnyNumericProperty::F64(_) => Type::Primitive(PrimitiveType::F64),
any_type::AnyNumericProperty::Other(_) => { any_type::AnyNumericProperty::Other(_) => {
return Err(anyhow::anyhow!("unhandled numeric property")); return Err(anyhow::anyhow!("unhandled numeric property"));
} }
}) }
} }
AnyProperty::Bool(_) => Type::Primitive(PrimitiveType::Bool), AnyProperty::Bool(_) => Type::Primitive(PrimitiveType::Bool),
AnyProperty::Interface(_) => { AnyProperty::Interface(_) => {
@ -273,13 +289,24 @@ pub mod sdk {
} }
}; };
Ok(ty) if *prop.array_dim() > 1 {
Ok(Type::RawArray {
ty: Box::new(ty),
len: *prop.array_dim() as u32,
})
} else {
Ok(ty)
}
} }
fn process_children(&self, strct: UStruct) -> anyhow::Result<Vec<ClassField>> { fn process_children(
&self,
strct: UStruct,
) -> anyhow::Result<(Vec<ClassField>, Vec<ClassMethod>)> {
log::info!("{} children:", strct.get_full_name_or_default()); log::info!("{} children:", strct.get_full_name_or_default());
let mut fields = Vec::new(); let mut fields = Vec::new();
let mut methods = Vec::new();
for child in strct for child in strct
.iter_children() .iter_children()
@ -306,14 +333,87 @@ pub mod sdk {
strt @ any_type::AnyField::Struct(_) if let Some(any_type::AnyStruct::Function(func)) = strt.as_any_struct() => { strt @ any_type::AnyField::Struct(_) if let Some(any_type::AnyStruct::Function(func)) = strt.as_any_struct() => {
log::info!("function: {func}"); log::info!("function: {func}");
// TODO: methods if let Ok(method) = Self::process_function(func) {
methods.push(method);
}
}, },
_ => { _ => {
} }
} }
} }
Ok(fields) Ok((fields, methods))
}
fn process_function(func: UFunction) -> anyhow::Result<ClassMethod> {
let full_name = func
.get_full_name()
.context("could not get full function name")?;
let rust_name =
canonicalize_name(&func.get_name().context("could not get function name")?)
.to_string();
let is_static = func.function_flags().contains(EFunctionFlags::Static);
let is_native = func.function_flags().contains(EFunctionFlags::Native);
let mut params = Vec::new();
let mut return_types = Vec::new();
for child in func
.iter_children()
.map(|field| any_type::AnyField::from_field(field))
{
match child {
any_type::AnyField::Property(prop) => {
match Self::find_type(any_type::AnyProperty::from_prop(prop)) {
Ok(ty) => {
log::info!("field: {ty:?}: {prop}");
if let Some(kind) = if prop
.property_flags()
.contains(EPropertyFlags::ReturnParm)
{
Some(ParameterKind::Return)
} else if prop.property_flags().contains(EPropertyFlags::OutParm) {
if prop.property_flags().contains(EPropertyFlags::ConstParm) {
Some(ParameterKind::Default)
} else {
Some(ParameterKind::Out)
}
} else if prop.property_flags().contains(EPropertyFlags::Parm) {
Some(ParameterKind::Default)
} else {
None
} {
match kind {
ParameterKind::Return => {
return_types.push(ty);
}
ParameterKind::Default => {
params.push(MethodParameter { ty, is_out: false });
}
ParameterKind::Out => {
params.push(MethodParameter { ty, is_out: true });
}
}
}
}
Err(e) => {
log::warn!("skipping field with offset {}: {e}", prop.offset());
}
}
}
_ => {}
}
}
Ok(ClassMethod {
name: rust_name,
parameters: params,
return_type: return_types.into_iter().next(),
full_name,
is_native,
is_static,
})
} }
fn process_enum(enm: UEnum) -> anyhow::Result<Enum> { fn process_enum(enm: UEnum) -> anyhow::Result<Enum> {
@ -527,7 +627,7 @@ pub mod sdk {
} }
#[derive(Debug)] #[derive(Debug)]
enum PrimitiveType { pub enum PrimitiveType {
Bool, Bool,
U8, U8,
U16, U16,
@ -542,7 +642,7 @@ pub mod sdk {
} }
#[derive(Debug)] #[derive(Debug)]
enum Type { pub enum Type {
Ptr(Box<Type>), Ptr(Box<Type>),
Ref(Box<Type>), Ref(Box<Type>),
WeakPtr(UClass), WeakPtr(UClass),
@ -551,6 +651,10 @@ pub mod sdk {
AssetPtr(UClass), AssetPtr(UClass),
Array(Box<Type>), Array(Box<Type>),
Primitive(PrimitiveType), Primitive(PrimitiveType),
RawArray {
ty: Box<Type>,
len: u32,
},
Name, Name,
String, String,
Text, Text,
@ -562,31 +666,54 @@ pub mod sdk {
} }
#[derive(Debug)] #[derive(Debug)]
struct ClassField { pub struct ClassField {
offset: u32, pub offset: u32,
size: u32, pub size: u32,
name: String, pub name: String,
ty: Type, pub ty: Type,
} }
#[derive(Debug)] #[derive(Debug)]
struct ClassMethod { pub struct MethodParameter {
offset: u32, pub ty: Type,
size: u32, pub is_out: bool,
name: String,
} }
#[derive(Debug)] #[derive(Debug)]
struct Class { pub struct ClassMethod {
is_class: bool, pub name: String,
name: String, pub full_name: String,
fields: Vec<ClassField>, pub is_native: bool,
pub is_static: bool,
pub parameters: Vec<MethodParameter>,
pub return_type: Option<Type>,
} }
#[derive(Debug)] #[derive(Debug)]
struct Enum { pub struct Class {
name: String, pub is_class: bool,
values: Vec<String>, pub name: String,
pub super_class: Option<UStruct>,
pub fields: Vec<ClassField>,
pub methods: Vec<ClassMethod>,
}
#[derive(Debug)]
pub enum Types {
Class(Class),
Enum(Enum),
}
enum ParameterKind {
Return,
Default,
Out,
}
#[derive(Debug)]
pub struct Enum {
pub name: String,
pub values: Vec<String>,
} }
impl Display for Enum { impl Display for Enum {

View file

@ -516,6 +516,8 @@ impl<'a> Iterator for ChildIter<'a> {
pub enum UAnyType { pub enum UAnyType {
UObject(UObject), UObject(UObject),
UClass(UClass), UClass(UClass),
UProperty(UProperty),
UFunction(UFunction),
UField(UField), UField(UField),
UScriptStruct(UScriptStruct), UScriptStruct(UScriptStruct),
UEnum(UEnum), UEnum(UEnum),
@ -547,6 +549,147 @@ impl UAnyType {
} }
} }
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AnyNumericProp {
U8(UByteProperty),
U16(UUInt16Property),
U32(UUInt32Property),
U64(UUInt64Property),
I8(UInt8Property),
I16(UInt16Property),
I32(UIntProperty),
I64(UInt64Property),
F32(UFloatProperty),
F64(UDoubleProperty),
Other(UNumericProperty),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AnyObjectProp {
Object(UObjectProperty),
Class(UClassProperty),
Interface(UInterfaceProperty),
WeakObject(UWeakObjectProperty),
LazyObject(UWeakObjectProperty),
SoftObject(UWeakObjectProperty),
AssetObject(UAssetObjectProperty),
AssetClass(UAssetClassProperty),
Other(UObjectPropertyBase),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AnyDelegateProp {
Delegate(UDelegateProperty),
MulticastDelegate(UMulticastDelegateProperty),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AnyContainerProp {
Array(UArrayProperty),
Map(UMapProperty),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AnyProp {
Numeric(AnyNumericProp),
Bool(UBoolProperty),
ObjectBase(AnyObjectProp),
Container(AnyContainerProp),
Str(UStrProperty),
Text(UTextProperty),
Name(UNameProperty),
Delegate(AnyDelegateProp),
Enum(UEnumProperty),
Struct(UStructProperty),
Function(UFunction),
Other(UProperty),
}
impl AnyNumericProp {
fn from_uprop(prop: UNumericProperty) -> Self {
if prop.is_a_maybe(&UByteProperty::static_class()) {
Self::U8(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UUInt16Property::static_class()) {
Self::U16(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UUInt32Property::static_class()) {
Self::U32(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UUInt64Property::static_class()) {
Self::U64(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UInt8Property::static_class()) {
Self::I8(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UInt16Property::static_class()) {
Self::I16(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UIntProperty::static_class()) {
Self::I32(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UInt64Property::static_class()) {
Self::I64(unsafe { prop.cast() })
} else {
Self::Other(prop)
}
}
}
impl AnyObjectProp {
fn from_uprop(prop: UObjectPropertyBase) -> Self {
if prop.is_a_maybe(&UInterfaceProperty::static_class()) {
Self::Interface(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UAssetClassProperty::static_class()) {
Self::AssetClass(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UAssetObjectProperty::static_class()) {
Self::AssetObject(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UWeakObjectProperty::static_class()) {
Self::WeakObject(unsafe { prop.cast() })
} else if prop.is_a_maybe(&ULazyObjectProperty::static_class()) {
Self::LazyObject(unsafe { prop.cast() })
} else if prop.is_a_maybe(&USoftObjectProperty::static_class()) {
Self::SoftObject(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UClassProperty::static_class()) {
Self::Class(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UObjectProperty::static_class()) {
Self::Object(unsafe { prop.cast() })
} else {
Self::Other(prop)
}
}
}
impl AnyProp {
pub fn from_uprop(prop: UProperty) -> Self {
if prop.is_a_maybe(&UNumericProperty::static_class()) {
Self::Numeric(AnyNumericProp::from_uprop(unsafe { prop.cast() }))
} else if prop.is_a_maybe(&UBoolProperty::static_class()) {
Self::Bool(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UObjectPropertyBase::static_class()) {
Self::ObjectBase(AnyObjectProp::from_uprop(unsafe { prop.cast() }))
} else if prop.is_a_maybe(&UArrayProperty::static_class()) {
Self::Container(AnyContainerProp::Array(unsafe { prop.cast() }))
} else if prop.is_a_maybe(&UMapProperty::static_class()) {
Self::Container(AnyContainerProp::Map(unsafe { prop.cast() }))
} else if prop.is_a_maybe(&UDelegateProperty::static_class()) {
Self::Delegate(AnyDelegateProp::Delegate(unsafe { prop.cast() }))
} else if prop.is_a_maybe(&UMulticastDelegateProperty::static_class()) {
Self::Delegate(AnyDelegateProp::MulticastDelegate(unsafe { prop.cast() }))
} else if prop.is_a_maybe(&UStrProperty::static_class()) {
Self::Str(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UNameProperty::static_class()) {
Self::Name(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UTextProperty::static_class()) {
Self::Text(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UStructProperty::static_class()) {
Self::Struct(unsafe { prop.cast() })
} else if prop.is_a_maybe(&UEnumProperty::static_class()) {
Self::Enum(unsafe { prop.cast() })
} else {
Self::Other(prop)
}
}
}
impl AsUObject for UAnyType { impl AsUObject for UAnyType {
fn as_uobject(&self) -> self::UObject { fn as_uobject(&self) -> self::UObject {
match self { match self {
@ -556,6 +699,8 @@ impl AsUObject for UAnyType {
UAnyType::UScriptStruct(obj) => obj.as_uobject(), UAnyType::UScriptStruct(obj) => obj.as_uobject(),
UAnyType::UEnum(obj) => obj.as_uobject(), UAnyType::UEnum(obj) => obj.as_uobject(),
UAnyType::UStruct(obj) => obj.as_uobject(), UAnyType::UStruct(obj) => obj.as_uobject(),
UAnyType::UProperty(obj) => obj.as_uobject(),
UAnyType::UFunction(obj) => obj.as_uobject(),
} }
} }