From 08149329e18fb664dd67d63e815a82390c47429f Mon Sep 17 00:00:00 2001 From: Janis Date: Thu, 20 Apr 2023 22:24:26 +0200 Subject: [PATCH] parsing classes, structs and enums --- src/lib.rs | 301 +++++++++++++++++++++++++++++++------------- src/v2_types/mod.rs | 145 +++++++++++++++++++++ 2 files changed, 359 insertions(+), 87 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index db1ebbf..c94a55e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ pub mod sdk { use anyhow::Context; use itertools::any; + use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use crate::{ global_tables::objects::{FindClass, GOBJECTS}, @@ -30,10 +31,12 @@ pub mod sdk { any_type::{self, AnyProperty}, traits::{ AsUObject, StaticClass, UArrayPropertyTrait, UBytePropertyTrait, - UEnumPropertyTrait, UEnumTrait, UObjectNonConst, UObjectPropertyBaseTrait, - UObjectTrait, UPropertyTrait, UStructNonConst, UStructPropertyTrait, UStructTrait, + UEnumPropertyTrait, UEnumTrait, UFunctionTrait, UObjectNonConst, + 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, } + #[derive(Debug)] + pub struct ProcessedPackage { + pub package: UObject, + pub types: HashMap, + pub package_dependencies: Vec, + } + impl<'a> Package<'a> { pub fn new(sdk: &'a Sdk, package: UObject, objects: Vec) -> Self { Self { @@ -103,57 +113,60 @@ pub mod sdk { } } - pub fn process(&self) -> anyhow::Result<()> { - for object in self.objects.iter() { - self.sdk - .cache - .cache_class(object.class().expect("object had no class?")) - .context("object not of any base type?")?; + pub fn process(self) -> ProcessedPackage { + let types = self + .objects + .par_iter() + .filter_map(|&object| { + let ty = UAnyType::from_uobject(object.as_uobject()); + log::trace!("ty: {ty:?}"); - let ty = UAnyType::from_uobject(object.as_uobject()); - log::trace!("ty: {ty:?}"); + match 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::>(); - match ty { - UAnyType::UClass(class) => { - let _ = self.process_struct(unsafe { class.cast() }); - } - UAnyType::UScriptStruct(class) => { - let _ = self.process_struct(unsafe { class.cast() }); - } - UAnyType::UEnum(obj) => { - let enm = Self::process_enum(obj)?; - log::info!("enum: {enm}"); - } - _ => {} - } + ProcessedPackage { + package: self.package, + types, + package_dependencies: self.dependencies, } - - Ok(()) } - fn process_struct(&self, strct: UStruct) -> anyhow::Result<()> { + fn process_struct(&self, strct: UStruct) -> anyhow::Result { + 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_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 { - log::info!("encountered external dependency"); - // TODO: return dependency on strct.package_object() - } else { - // process outers for classes - if !is_struct { - if let Some(outer) = strct.outer() { - if outer.is_a(&UStruct::static_class().unwrap()) { - self.process_struct(unsafe { outer.cast() })?; - } + // process outers for classes + if !is_struct { + if let Some(outer) = strct.outer() { + if outer.is_a(&UStruct::static_class().unwrap()) { + // depend on this } } + } - 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 { log::info!("encountered external dependency"); // TODO: return dependency on strct.package_object() @@ -161,38 +174,41 @@ pub mod sdk { Some(spr) } 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 { - let ty = match prop { + let ty = match prop.clone() { 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() { - Some(enm) => { - return Ok(Type::Enum { - underlying: Box::new(Type::Primitive(PrimitiveType::U8)), - enum_type: *enm, - }) - } - None => PrimitiveType::U8, + Some(enm) => Type::Enum { + underlying: Box::new(Type::Primitive(PrimitiveType::U8)), + enum_type: *enm, + }, + None => Type::Primitive(PrimitiveType::U8), }, - any_type::AnyNumericProperty::U16(_) => PrimitiveType::U16, - any_type::AnyNumericProperty::U32(_) => PrimitiveType::U32, - any_type::AnyNumericProperty::U64(_) => PrimitiveType::U64, - any_type::AnyNumericProperty::I8(_) => PrimitiveType::I8, - any_type::AnyNumericProperty::I16(_) => PrimitiveType::I16, - any_type::AnyNumericProperty::I32(_) => PrimitiveType::I32, - any_type::AnyNumericProperty::I64(_) => PrimitiveType::I64, - any_type::AnyNumericProperty::F32(_) => PrimitiveType::F32, - any_type::AnyNumericProperty::F64(_) => PrimitiveType::F64, + any_type::AnyNumericProperty::U16(_) => Type::Primitive(PrimitiveType::U16), + any_type::AnyNumericProperty::U32(_) => Type::Primitive(PrimitiveType::U32), + any_type::AnyNumericProperty::U64(_) => Type::Primitive(PrimitiveType::U64), + any_type::AnyNumericProperty::I8(_) => Type::Primitive(PrimitiveType::I8), + any_type::AnyNumericProperty::I16(_) => Type::Primitive(PrimitiveType::I16), + any_type::AnyNumericProperty::I32(_) => Type::Primitive(PrimitiveType::I32), + any_type::AnyNumericProperty::I64(_) => Type::Primitive(PrimitiveType::I64), + any_type::AnyNumericProperty::F32(_) => Type::Primitive(PrimitiveType::F32), + any_type::AnyNumericProperty::F64(_) => Type::Primitive(PrimitiveType::F64), any_type::AnyNumericProperty::Other(_) => { return Err(anyhow::anyhow!("unhandled numeric property")); } - }) + } } AnyProperty::Bool(_) => Type::Primitive(PrimitiveType::Bool), 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> { + fn process_children( + &self, + strct: UStruct, + ) -> anyhow::Result<(Vec, Vec)> { log::info!("{} children:", strct.get_full_name_or_default()); let mut fields = Vec::new(); + let mut methods = Vec::new(); for child in strct .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() => { 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 { + 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 { @@ -527,7 +627,7 @@ pub mod sdk { } #[derive(Debug)] - enum PrimitiveType { + pub enum PrimitiveType { Bool, U8, U16, @@ -542,7 +642,7 @@ pub mod sdk { } #[derive(Debug)] - enum Type { + pub enum Type { Ptr(Box), Ref(Box), WeakPtr(UClass), @@ -551,6 +651,10 @@ pub mod sdk { AssetPtr(UClass), Array(Box), Primitive(PrimitiveType), + RawArray { + ty: Box, + len: u32, + }, Name, String, Text, @@ -562,31 +666,54 @@ pub mod sdk { } #[derive(Debug)] - struct ClassField { - offset: u32, - size: u32, - name: String, - ty: Type, + pub struct ClassField { + pub offset: u32, + pub size: u32, + pub name: String, + pub ty: Type, } #[derive(Debug)] - struct ClassMethod { - offset: u32, - size: u32, - name: String, + pub struct MethodParameter { + pub ty: Type, + pub is_out: bool, } #[derive(Debug)] - struct Class { - is_class: bool, - name: String, - fields: Vec, + pub struct ClassMethod { + pub name: String, + pub full_name: String, + pub is_native: bool, + pub is_static: bool, + pub parameters: Vec, + pub return_type: Option, } #[derive(Debug)] - struct Enum { - name: String, - values: Vec, + pub struct Class { + pub is_class: bool, + pub name: String, + pub super_class: Option, + pub fields: Vec, + pub methods: Vec, + } + + #[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, } impl Display for Enum { diff --git a/src/v2_types/mod.rs b/src/v2_types/mod.rs index e7db020..a8d889e 100644 --- a/src/v2_types/mod.rs +++ b/src/v2_types/mod.rs @@ -516,6 +516,8 @@ impl<'a> Iterator for ChildIter<'a> { pub enum UAnyType { UObject(UObject), UClass(UClass), + UProperty(UProperty), + UFunction(UFunction), UField(UField), UScriptStruct(UScriptStruct), 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 { fn as_uobject(&self) -> self::UObject { match self { @@ -556,6 +699,8 @@ impl AsUObject for UAnyType { UAnyType::UScriptStruct(obj) => obj.as_uobject(), UAnyType::UEnum(obj) => obj.as_uobject(), UAnyType::UStruct(obj) => obj.as_uobject(), + UAnyType::UProperty(obj) => obj.as_uobject(), + UAnyType::UFunction(obj) => obj.as_uobject(), } }