restructured sdk generator with Package type, parsing struct types

This commit is contained in:
Janis 2023-04-20 20:50:57 +02:00
parent 69273c7e76
commit a574191066

View file

@ -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<UObject>,
dependencies: Vec<UObject>,
}
impl Sdk {
pub fn package_objects() -> HashMap<UObject, Vec<UObject>> {
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::<UObject, Vec<UObject>>::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<UObject, Vec<UObject>>) -> 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<UObject>) -> Self {
Self {
sdk,
package,
objects,
dependencies: Vec::new(),
}
Ok(())
}
fn process_package(
cache: &mut ClassCache,
package: UObject,
objects: Vec<UObject>,
) -> 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<Type> {
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<Vec<ClassField>> {
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<Enum> {
// 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<Package> {
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::<UObject, Vec<UObject>>::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::<Vec<_>>()
}
}
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<Type>),
Ref(Box<Type>),
WeakPtr(UClass),
SoftPtr(UClass),
LazyPtr(UClass),
AssetPtr(UClass),
Array(Box<Type>),
Primitive(PrimitiveType),
Name,
String,
Text,
Enum {
underlying: Box<Type>,
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<ClassField>,
}
#[derive(Debug)]
struct Enum {
name: String,