restructured sdk generator with Package type, parsing struct types
This commit is contained in:
parent
69273c7e76
commit
a574191066
382
src/lib.rs
382
src/lib.rs
|
@ -22,12 +22,18 @@ pub mod sdk {
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use itertools::any;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
global_tables::objects::{FindClass, GOBJECTS},
|
global_tables::objects::{FindClass, GOBJECTS},
|
||||||
v2_types::{
|
v2_types::{
|
||||||
traits::{AsUObject, UEnumTrait, UObjectNonConst, UObjectTrait},
|
any_type::{self, AnyProperty},
|
||||||
UAnyType, UClass, UEnum, UObject,
|
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()?;
|
let name = class.get_full_name()?;
|
||||||
self.classes.lock().unwrap().insert(name, class);
|
self.classes.lock().unwrap().insert(name, class);
|
||||||
Ok(())
|
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 {
|
impl<'a> Package<'a> {
|
||||||
pub fn package_objects() -> HashMap<UObject, Vec<UObject>> {
|
pub fn new(sdk: &'a Sdk, package: UObject, objects: Vec<UObject>) -> Self {
|
||||||
let gobjects = GOBJECTS.read().unwrap();
|
Self {
|
||||||
let objects = gobjects.as_objects().unwrap();
|
sdk,
|
||||||
|
package,
|
||||||
let sorted_objects = objects
|
objects,
|
||||||
.iter()
|
dependencies: Vec::new(),
|
||||||
// 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)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_package(
|
pub fn process(&self) -> anyhow::Result<()> {
|
||||||
cache: &mut ClassCache,
|
for object in self.objects.iter() {
|
||||||
package: UObject,
|
self.sdk
|
||||||
objects: Vec<UObject>,
|
.cache
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
cache
|
|
||||||
.cache_class(package.class().context("package had no class")?)
|
|
||||||
.context("package not of any base type")?;
|
|
||||||
|
|
||||||
for object in objects {
|
|
||||||
cache
|
|
||||||
.cache_class(object.class().expect("object had no class?"))
|
.cache_class(object.class().expect("object had no class?"))
|
||||||
.context("object not of any base type?")?;
|
.context("object not of any base type?")?;
|
||||||
|
|
||||||
let ty = UAnyType::from_uobject(object.as_uobject());
|
let ty = UAnyType::from_uobject(object.as_uobject());
|
||||||
log::debug!("ty: {ty:?}");
|
log::trace!("ty: {ty:?}");
|
||||||
|
|
||||||
match ty {
|
match ty {
|
||||||
UAnyType::UObject(_) => {}
|
UAnyType::UClass(class) => {
|
||||||
UAnyType::UClass(_) => {}
|
let _ = self.process_struct(unsafe { class.cast() });
|
||||||
UAnyType::UField(_) => {}
|
}
|
||||||
UAnyType::UScriptStruct(_) => {}
|
UAnyType::UScriptStruct(class) => {
|
||||||
|
let _ = self.process_struct(unsafe { class.cast() });
|
||||||
|
}
|
||||||
UAnyType::UEnum(obj) => {
|
UAnyType::UEnum(obj) => {
|
||||||
let enm = Self::process_enum(obj)?;
|
let enm = Self::process_enum(obj)?;
|
||||||
log::info!("enum: {enm}");
|
log::info!("enum: {enm}");
|
||||||
}
|
}
|
||||||
UAnyType::UStruct(_) => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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> {
|
fn process_enum(enm: UEnum) -> anyhow::Result<Enum> {
|
||||||
// get all the variants
|
// get all the variants
|
||||||
let values = enm
|
let values = enm
|
||||||
|
@ -206,6 +367,54 @@ pub mod sdk {
|
||||||
values: variants.into_iter().map(|(_, name)| name).collect(),
|
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> {
|
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)]
|
#[derive(Debug)]
|
||||||
struct Enum {
|
struct Enum {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
Loading…
Reference in a new issue