From a5741910662466c9e79e38c671a8fffd549ded7d Mon Sep 17 00:00:00 2001 From: Janis Date: Thu, 20 Apr 2023 20:50:57 +0200 Subject: [PATCH] restructured sdk generator with Package type, parsing struct types --- src/lib.rs | 382 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 324 insertions(+), 58 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1b41345..db1ebbf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,12 +22,18 @@ pub mod sdk { }; use anyhow::Context; + use itertools::any; use crate::{ global_tables::objects::{FindClass, GOBJECTS}, v2_types::{ - traits::{AsUObject, UEnumTrait, UObjectNonConst, UObjectTrait}, - UAnyType, UClass, UEnum, UObject, + any_type::{self, AnyProperty}, + traits::{ + AsUObject, StaticClass, UArrayPropertyTrait, UBytePropertyTrait, + UEnumPropertyTrait, UEnumTrait, UObjectNonConst, UObjectPropertyBaseTrait, + UObjectTrait, UPropertyTrait, UStructNonConst, UStructPropertyTrait, UStructTrait, + }, + AnyProp, UAnyType, UClass, UEnum, UObject, UProperty, UScriptStruct, UStruct, }, }; @@ -42,7 +48,7 @@ pub mod sdk { } } - pub fn cache_class(&mut self, class: UClass) -> anyhow::Result<()> { + pub fn cache_class(&self, class: UClass) -> anyhow::Result<()> { let name = class.get_full_name()?; self.classes.lock().unwrap().insert(name, class); Ok(()) @@ -80,81 +86,236 @@ pub mod sdk { } } - pub struct Sdk {} + pub struct Package<'a> { + sdk: &'a Sdk, + package: UObject, + objects: Vec, + dependencies: Vec, + } - impl Sdk { - pub fn package_objects() -> HashMap> { - let gobjects = GOBJECTS.read().unwrap(); - let objects = gobjects.as_objects().unwrap(); - - let sorted_objects = objects - .iter() - // ensure item contains object - .filter_map(|item| item.object()) - // get package object - .map(|obj| (obj.package_object(), obj)) - .fold( - HashMap::>::new(), - |mut acc, (pkg, obj)| { - match acc.entry(pkg) { - Entry::Occupied(mut entry) => { - entry.get_mut().push(obj); - } - Entry::Vacant(entry) => { - entry.insert(vec![obj]); - } - } - - acc - }, - ); - - sorted_objects - } - - pub fn process_packages(packages: HashMap>) -> anyhow::Result<()> { - let mut cache = ClassCache::new(); - - for (package, objects) in packages { - Self::process_package(&mut cache, package, objects)?; + impl<'a> Package<'a> { + pub fn new(sdk: &'a Sdk, package: UObject, objects: Vec) -> Self { + Self { + sdk, + package, + objects, + dependencies: Vec::new(), } - - Ok(()) } - fn process_package( - cache: &mut ClassCache, - package: UObject, - objects: Vec, - ) -> anyhow::Result<()> { - cache - .cache_class(package.class().context("package had no class")?) - .context("package not of any base type")?; - - for object in objects { - cache + 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?")?; let ty = UAnyType::from_uobject(object.as_uobject()); - log::debug!("ty: {ty:?}"); + log::trace!("ty: {ty:?}"); match ty { - UAnyType::UObject(_) => {} - UAnyType::UClass(_) => {} - UAnyType::UField(_) => {} - UAnyType::UScriptStruct(_) => {} + 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}"); } - UAnyType::UStruct(_) => {} + _ => {} } } Ok(()) } + fn process_struct(&self, strct: UStruct) -> anyhow::Result<()> { + 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() })?; + } + } + } + + 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() + } + Some(spr) + } else {None}; + + self.process_children(strct)?; + } + + Ok(()) + } + + fn find_type(prop: AnyProperty) -> anyhow::Result { + let ty = match prop { + numeric @ AnyProperty::Numeric(_) => { + Type::Primitive(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, + }, + 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::Other(_) => { + return Err(anyhow::anyhow!("unhandled numeric property")); + } + }) + } + AnyProperty::Bool(_) => Type::Primitive(PrimitiveType::Bool), + AnyProperty::Interface(_) => { + return Err(anyhow::anyhow!("skipping interfaces for now")) + } + object @ AnyProperty::ObjectBase(_) => { + match object.as_any_object_base_property().unwrap() { + any_type::AnyObjectBaseProperty::Object(obj) => { + Type::Ptr(Box::new(Type::Class(unsafe { + // safety: any uclass is also a ustruct + obj.property_class() + .context("object property missing properry class")? + .cast() + }))) + } + any_type::AnyObjectBaseProperty::WeakObject(obj) => Type::WeakPtr( + obj.property_class() + .context("weak ptr property missing property class.")?, + ), + any_type::AnyObjectBaseProperty::LazyObject(obj) => Type::LazyPtr( + obj.property_class() + .context("lazy ptr property missing property class.")?, + ), + any_type::AnyObjectBaseProperty::SoftObject(obj) => Type::SoftPtr( + obj.property_class() + .context("soft ptr property missing property class.")?, + ), + asset @ any_type::AnyObjectBaseProperty::AssetObject(_) => { + match asset.as_any_asset_object_property().unwrap() { + any_type::AnyAssetObjectProperty::Class(class) => Type::AssetPtr( + class + .property_class() + .context("asset object property missing properry class")?, + ), + any_type::AnyAssetObjectProperty::Object(_) => { + return Err(anyhow::anyhow!( + "unhandled asset object property (NOT AN ERROR)" + )); + } + } + } + any_type::AnyObjectBaseProperty::Other(_) => { + return Err(anyhow::anyhow!("unhandled object base property")); + } + } + } + AnyProperty::Array(array) => { + Type::Array(Box::new(Self::find_type(AnyProperty::from_prop( + array + .inner() + .context("array property inner type missing.")?, + ))?)) + } + AnyProperty::Map(_) => unreachable!("not used in ARK"), + AnyProperty::Str(_) => Type::String, + AnyProperty::Text(_) => Type::Text, + AnyProperty::Name(_) => Type::Name, + AnyProperty::Delegate(_) => { + return Err(anyhow::anyhow!("skipping delegates for now")); + } + AnyProperty::MulticastDelegate(_) => { + return Err(anyhow::anyhow!("skipping multicast delegates for now")); + } + AnyProperty::Enum(enm) => Type::Enum { + underlying: Box::new(Self::find_type(AnyProperty::from_prop( + enm.underlying_type() + .context("enum property was missing underlying type")?, + ))?), + enum_type: enm.uenum().context("enum property missing enum type")?, + }, + AnyProperty::Struct(class) => Type::Class( + class + .ustruct() + .context("struct property had no inner struct")?, + ), + AnyProperty::Other(_) => { + return Err(anyhow::anyhow!("unhandled property.")); + } + }; + + Ok(ty) + } + + fn process_children(&self, strct: UStruct) -> anyhow::Result> { + log::info!("{} children:", strct.get_full_name_or_default()); + + let mut fields = Vec::new(); + + for child in strct + .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}"); + fields.push( + ClassField { + offset: *prop.offset() as u32, + size: *prop.element_size() as u32, + name: canonicalize_name(&prop.name().get_name().context("failed to retrieve field name")?).to_string(), + ty, + }); + }, + Err(e) => { + log::warn!("skipping field with offset {}: {e}", prop.offset()); + }, + } + }, + strt @ any_type::AnyField::Struct(_) if let Some(any_type::AnyStruct::Function(func)) = strt.as_any_struct() => { + log::info!("function: {func}"); + + // TODO: methods + }, + _ => { + } + } + } + + Ok(fields) + } + fn process_enum(enm: UEnum) -> anyhow::Result { // get all the variants let values = enm @@ -206,6 +367,54 @@ pub mod sdk { values: variants.into_iter().map(|(_, name)| name).collect(), }) } + + pub fn package_object(&self) -> &UObject { + &self.package + } + } + + pub struct Sdk { + cache: ClassCache, + } + + impl Sdk { + pub fn new() -> Self { + Self { + cache: ClassCache::new(), + } + } + + pub fn package_objects(&self) -> Vec { + let gobjects = GOBJECTS.read().unwrap(); + let objects = gobjects.as_objects().unwrap(); + + let sorted_objects = objects + .iter() + // ensure item contains object + .filter_map(|item| item.object()) + // get package object + .map(|obj| (obj.package_object(), obj)) + .fold( + HashMap::>::new(), + |mut acc, (pkg, obj)| { + match acc.entry(pkg) { + Entry::Occupied(mut entry) => { + entry.get_mut().push(obj); + } + Entry::Vacant(entry) => { + entry.insert(vec![obj]); + } + } + + acc + }, + ); + + sorted_objects + .into_iter() + .map(|(package, objects)| Package::new(self, package, objects)) + .collect::>() + } } fn keywords() -> HashSet<&'static str> { @@ -317,6 +526,63 @@ pub mod sdk { } } + #[derive(Debug)] + enum PrimitiveType { + Bool, + U8, + U16, + U32, + U64, + I8, + I16, + I32, + I64, + F32, + F64, + } + + #[derive(Debug)] + enum Type { + Ptr(Box), + Ref(Box), + WeakPtr(UClass), + SoftPtr(UClass), + LazyPtr(UClass), + AssetPtr(UClass), + Array(Box), + Primitive(PrimitiveType), + Name, + String, + Text, + Enum { + underlying: Box, + enum_type: UEnum, + }, + Class(UStruct), + } + + #[derive(Debug)] + struct ClassField { + offset: u32, + size: u32, + name: String, + ty: Type, + } + + #[derive(Debug)] + struct ClassMethod { + offset: u32, + size: u32, + name: String, + } + + #[derive(Debug)] + struct Class { + is_class: bool, + name: String, + fields: Vec, + } + #[derive(Debug)] struct Enum { name: String,