rust generation
This commit is contained in:
parent
d1c0520316
commit
32bcc30b96
142
src/lib.rs
142
src/lib.rs
|
@ -14,6 +14,7 @@ pub mod tarray;
|
||||||
pub mod v2_types;
|
pub mod v2_types;
|
||||||
|
|
||||||
pub mod sdk {
|
pub mod sdk {
|
||||||
|
pub mod output;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::{hash_map::Entry, BTreeMap, HashMap, HashSet},
|
collections::{hash_map::Entry, BTreeMap, HashMap, HashSet},
|
||||||
|
@ -22,11 +23,13 @@ pub mod sdk {
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use itertools::Itertools;
|
||||||
use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
global_tables::objects::{FindClass, GOBJECTS},
|
global_tables::objects::{FindClass, GOBJECTS},
|
||||||
v2_types::{
|
v2_types::{
|
||||||
|
actor_static_class,
|
||||||
any_type::{self, AnyField, AnyObject, AnyProperty, AnyStruct},
|
any_type::{self, AnyField, AnyObject, AnyProperty, AnyStruct},
|
||||||
traits::{
|
traits::{
|
||||||
AsUObject, StaticClass, UArrayPropertyTrait, UBytePropertyTrait,
|
AsUObject, StaticClass, UArrayPropertyTrait, UBytePropertyTrait,
|
||||||
|
@ -34,10 +37,12 @@ pub mod sdk {
|
||||||
UObjectPropertyBaseTrait, UObjectTrait, UPropertyTrait, UStructNonConst,
|
UObjectPropertyBaseTrait, UObjectTrait, UPropertyTrait, UStructNonConst,
|
||||||
UStructPropertyTrait, UStructTrait,
|
UStructPropertyTrait, UStructTrait,
|
||||||
},
|
},
|
||||||
EFunctionFlags, EPropertyFlags, UAnyType, UClass, UEnum, UFunction, UObject, UStruct,
|
EFunctionFlags, EPropertyFlags, UClass, UEnum, UFunction, UObject, UStruct,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use self::output::rust::inject_coreuobject_types;
|
||||||
|
|
||||||
struct ClassCache {
|
struct ClassCache {
|
||||||
classes: Mutex<HashMap<String, UClass>>,
|
classes: Mutex<HashMap<String, UClass>>,
|
||||||
}
|
}
|
||||||
|
@ -152,9 +157,14 @@ pub mod sdk {
|
||||||
let name = strct
|
let name = strct
|
||||||
.get_name()
|
.get_name()
|
||||||
.context("failed to get struct or class name")?;
|
.context("failed to get struct or class name")?;
|
||||||
|
|
||||||
let full_name = strct
|
let full_name = strct
|
||||||
.get_full_name()
|
.get_full_name()
|
||||||
.context("failed to get struct or class full name")?;
|
.context("failed to get struct or class full name")?;
|
||||||
|
|
||||||
|
let is_actor = strct
|
||||||
|
.iter_super_structs()
|
||||||
|
.contains(&unsafe { actor_static_class().unwrap().cast() });
|
||||||
let is_class = strct.is_a(&UClass::static_class().unwrap());
|
let is_class = strct.is_a(&UClass::static_class().unwrap());
|
||||||
|
|
||||||
let super_struct = if let Some(spr) = *strct.super_field() {
|
let super_struct = if let Some(spr) = *strct.super_field() {
|
||||||
|
@ -172,7 +182,16 @@ pub mod sdk {
|
||||||
Ok(Class {
|
Ok(Class {
|
||||||
is_class,
|
is_class,
|
||||||
size: *strct.property_size() as u32,
|
size: *strct.property_size() as u32,
|
||||||
name,
|
name: format!(
|
||||||
|
"{}{name}",
|
||||||
|
if is_actor {
|
||||||
|
"A"
|
||||||
|
} else if is_class {
|
||||||
|
"U"
|
||||||
|
} else {
|
||||||
|
"F"
|
||||||
|
}
|
||||||
|
),
|
||||||
super_class: super_struct,
|
super_class: super_struct,
|
||||||
fields,
|
fields,
|
||||||
methods,
|
methods,
|
||||||
|
@ -363,34 +382,33 @@ pub mod sdk {
|
||||||
match Self::find_type(any_type::AnyProperty::from_prop(prop)) {
|
match Self::find_type(any_type::AnyProperty::from_prop(prop)) {
|
||||||
Ok(ty) => {
|
Ok(ty) => {
|
||||||
log::debug!("field: {ty:?}: {prop}");
|
log::debug!("field: {ty:?}: {prop}");
|
||||||
if let Some(kind) = if prop
|
|
||||||
|
let param = MethodParameter {
|
||||||
|
name: prop
|
||||||
|
.get_name()
|
||||||
|
.context("failed to get parameter name")?,
|
||||||
|
ty,
|
||||||
|
};
|
||||||
|
|
||||||
|
let param = if prop
|
||||||
.property_flags()
|
.property_flags()
|
||||||
.contains(EPropertyFlags::ReturnParm)
|
.contains(EPropertyFlags::ReturnParm)
|
||||||
{
|
{
|
||||||
Some(ParameterKind::Return)
|
return_types.push(param.clone());
|
||||||
|
Some(ParameterKind::Return(param))
|
||||||
} else if prop.property_flags().contains(EPropertyFlags::OutParm) {
|
} else if prop.property_flags().contains(EPropertyFlags::OutParm) {
|
||||||
if prop.property_flags().contains(EPropertyFlags::ConstParm) {
|
if prop.property_flags().contains(EPropertyFlags::ConstParm) {
|
||||||
Some(ParameterKind::Default)
|
Some(ParameterKind::Default(param))
|
||||||
} else {
|
} else {
|
||||||
Some(ParameterKind::Out)
|
Some(ParameterKind::Out(param))
|
||||||
}
|
}
|
||||||
} else if prop.property_flags().contains(EPropertyFlags::Parm) {
|
} else if prop.property_flags().contains(EPropertyFlags::Parm) {
|
||||||
Some(ParameterKind::Default)
|
Some(ParameterKind::Default(param))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
} {
|
};
|
||||||
match kind {
|
|
||||||
ParameterKind::Return => {
|
params.extend(param);
|
||||||
return_types.push(ty);
|
|
||||||
}
|
|
||||||
ParameterKind::Default => {
|
|
||||||
params.push(MethodParameter { ty, is_out: false });
|
|
||||||
}
|
|
||||||
ParameterKind::Out => {
|
|
||||||
params.push(MethodParameter { ty, is_out: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("skipping field with offset {}: {e}", prop.offset());
|
log::warn!("skipping field with offset {}: {e}", prop.offset());
|
||||||
|
@ -507,6 +525,50 @@ pub mod sdk {
|
||||||
Self { packages }
|
Self { packages }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn patch(mut self) -> Self {
|
||||||
|
inject_coreuobject_types(&mut self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_type(&self, obj: UObject) -> Option<&Types> {
|
||||||
|
self.packages
|
||||||
|
.get(&obj.package_object())
|
||||||
|
.and_then(|pkg| pkg.types.get(&obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inject_type(&mut self, class: Class) -> Option<UClass> {
|
||||||
|
if let Some(class_obj) = GOBJECTS
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.as_objects()
|
||||||
|
.unwrap()
|
||||||
|
.find_class(&class.full_name)
|
||||||
|
{
|
||||||
|
let package = class_obj.package_object();
|
||||||
|
match self.packages.entry(package.clone()) {
|
||||||
|
Entry::Occupied(mut entry) => {
|
||||||
|
entry
|
||||||
|
.get_mut()
|
||||||
|
.types
|
||||||
|
.insert(class_obj.as_uobject(), Types::Class(class));
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
let mut types = HashMap::new();
|
||||||
|
types.insert(class_obj.as_uobject(), Types::Class(class));
|
||||||
|
entry.insert(ProcessedPackage {
|
||||||
|
package,
|
||||||
|
types,
|
||||||
|
package_dependencies: HashMap::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(class_obj)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn package_objects() -> Vec<Package> {
|
pub fn package_objects() -> Vec<Package> {
|
||||||
let gobjects = GOBJECTS.read().unwrap();
|
let gobjects = GOBJECTS.read().unwrap();
|
||||||
let objects = gobjects.as_objects().unwrap();
|
let objects = gobjects.as_objects().unwrap();
|
||||||
|
@ -634,7 +696,7 @@ pub mod sdk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum PrimitiveType {
|
pub enum PrimitiveType {
|
||||||
Bool,
|
Bool,
|
||||||
U8,
|
U8,
|
||||||
|
@ -647,9 +709,10 @@ pub mod sdk {
|
||||||
I64,
|
I64,
|
||||||
F32,
|
F32,
|
||||||
F64,
|
F64,
|
||||||
|
Custom(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Ptr(Box<Type>),
|
Ptr(Box<Type>),
|
||||||
Ref(Box<Type>),
|
Ref(Box<Type>),
|
||||||
|
@ -697,10 +760,10 @@ pub mod sdk {
|
||||||
pub ty: Type,
|
pub ty: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MethodParameter {
|
pub struct MethodParameter {
|
||||||
|
pub name: String,
|
||||||
pub ty: Type,
|
pub ty: Type,
|
||||||
pub is_out: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -709,8 +772,8 @@ pub mod sdk {
|
||||||
pub full_name: String,
|
pub full_name: String,
|
||||||
pub is_native: bool,
|
pub is_native: bool,
|
||||||
pub is_static: bool,
|
pub is_static: bool,
|
||||||
pub parameters: Vec<MethodParameter>,
|
pub parameters: Vec<ParameterKind>,
|
||||||
pub return_type: Option<Type>,
|
pub return_type: Option<MethodParameter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -725,6 +788,10 @@ pub mod sdk {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Class {
|
impl Class {
|
||||||
|
pub fn rust_name(&self) -> &String {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
pub fn referenced_types(&self) -> Vec<UObject> {
|
pub fn referenced_types(&self) -> Vec<UObject> {
|
||||||
let mut types = Vec::new();
|
let mut types = Vec::new();
|
||||||
self.super_class.map(|obj| types.push(obj.as_uobject()));
|
self.super_class.map(|obj| types.push(obj.as_uobject()));
|
||||||
|
@ -738,13 +805,13 @@ pub mod sdk {
|
||||||
method
|
method
|
||||||
.return_type
|
.return_type
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|ty| ty.referenced_type()),
|
.and_then(|param| param.ty.referenced_type()),
|
||||||
);
|
);
|
||||||
types.extend(
|
types.extend(
|
||||||
method
|
method
|
||||||
.parameters
|
.parameters
|
||||||
.iter()
|
.iter()
|
||||||
.map(|param| ¶m.ty)
|
.map(|param| ¶m.as_param().ty)
|
||||||
.filter_map(|ty| ty.referenced_type()),
|
.filter_map(|ty| ty.referenced_type()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -759,10 +826,21 @@ pub mod sdk {
|
||||||
Enum(Enum),
|
Enum(Enum),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ParameterKind {
|
#[derive(Debug)]
|
||||||
Return,
|
pub enum ParameterKind {
|
||||||
Default,
|
Return(MethodParameter),
|
||||||
Out,
|
Default(MethodParameter),
|
||||||
|
Out(MethodParameter),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParameterKind {
|
||||||
|
pub fn as_param(&self) -> &MethodParameter {
|
||||||
|
match self {
|
||||||
|
ParameterKind::Return(param) => param,
|
||||||
|
ParameterKind::Default(param) => param,
|
||||||
|
ParameterKind::Out(param) => param,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
1
src/sdk/output/mod.rs
Normal file
1
src/sdk/output/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod rust;
|
545
src/sdk/output/rust.rs
Normal file
545
src/sdk/output/rust.rs
Normal file
|
@ -0,0 +1,545 @@
|
||||||
|
use std::io::{BufWriter, Write};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
sdk::{
|
||||||
|
canonicalize_name, Class, ClassField, ClassMethod, PrimitiveType, ProcessedPackage, Sdk,
|
||||||
|
Type, Types,
|
||||||
|
},
|
||||||
|
v2_types::traits::{AsUObject, UObjectNonConst, UStructNonConst},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait RustType {
|
||||||
|
fn rust_type<W: Write>(&self, sdk: &Sdk, w: &mut W) -> anyhow::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RustType for T
|
||||||
|
where
|
||||||
|
T: AsUObject,
|
||||||
|
{
|
||||||
|
fn rust_type<W: Write>(&self, sdk: &Sdk, w: &mut W) -> anyhow::Result<()> {
|
||||||
|
sdk.find_type(self.as_uobject())
|
||||||
|
.context("could not find type")?
|
||||||
|
.rust_type(sdk, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RustType for Types {
|
||||||
|
fn rust_type<W: Write>(&self, _sdk: &Sdk, w: &mut W) -> anyhow::Result<()> {
|
||||||
|
match self {
|
||||||
|
Types::Class(class) => {
|
||||||
|
write!(w, "{}", class.rust_name())?;
|
||||||
|
}
|
||||||
|
Types::Enum(enm) => {
|
||||||
|
// FIXME: this?
|
||||||
|
write!(w, "{}", enm.name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RustType for Type {
|
||||||
|
fn rust_type<W: Write>(&self, sdk: &Sdk, w: &mut W) -> anyhow::Result<()> {
|
||||||
|
match self {
|
||||||
|
Type::Ptr(ptr) => {
|
||||||
|
write!(w, "Option<NonNull<")?;
|
||||||
|
ptr.rust_type(sdk, w)?;
|
||||||
|
write!(w, ">>")?;
|
||||||
|
}
|
||||||
|
Type::Ref(_) => todo!(),
|
||||||
|
Type::WeakPtr(ptr) => {
|
||||||
|
write!(w, "TWeakObjectPtr<")?;
|
||||||
|
ptr.rust_type(sdk, w)?;
|
||||||
|
write!(w, ">")?;
|
||||||
|
}
|
||||||
|
Type::SoftPtr(ptr) => {
|
||||||
|
write!(w, "TSoftObjectPtr<")?;
|
||||||
|
ptr.rust_type(sdk, w)?;
|
||||||
|
write!(w, ">")?;
|
||||||
|
}
|
||||||
|
Type::LazyPtr(ptr) => {
|
||||||
|
write!(w, "TLazyObjectPtr<")?;
|
||||||
|
ptr.rust_type(sdk, w)?;
|
||||||
|
write!(w, ">")?;
|
||||||
|
}
|
||||||
|
Type::AssetPtr(ptr) => {
|
||||||
|
write!(w, "TAssetPtr<")?;
|
||||||
|
ptr.rust_type(sdk, w)?;
|
||||||
|
write!(w, ">")?;
|
||||||
|
}
|
||||||
|
Type::Array(array) => {
|
||||||
|
write!(w, "TArray<")?;
|
||||||
|
array.rust_type(sdk, w)?;
|
||||||
|
write!(w, ">")?;
|
||||||
|
}
|
||||||
|
Type::Primitive(ty) => {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
"{}",
|
||||||
|
match ty {
|
||||||
|
PrimitiveType::Bool => "bool",
|
||||||
|
PrimitiveType::U8 => "u8",
|
||||||
|
PrimitiveType::U16 => "u16",
|
||||||
|
PrimitiveType::U32 => "u32",
|
||||||
|
PrimitiveType::U64 => "u64",
|
||||||
|
PrimitiveType::I8 => "i8",
|
||||||
|
PrimitiveType::I16 => "i16",
|
||||||
|
PrimitiveType::I32 => "i32",
|
||||||
|
PrimitiveType::I64 => "i64",
|
||||||
|
PrimitiveType::F32 => "f32",
|
||||||
|
PrimitiveType::F64 => "f64",
|
||||||
|
PrimitiveType::Custom(custom) => custom,
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Type::RawArray { ty, len } => {
|
||||||
|
write!(w, "[")?;
|
||||||
|
ty.rust_type(sdk, w)?;
|
||||||
|
write!(w, "; {}]", len)?;
|
||||||
|
}
|
||||||
|
Type::Name => {
|
||||||
|
write!(w, "FName")?;
|
||||||
|
}
|
||||||
|
Type::String => {
|
||||||
|
write!(w, "FString")?;
|
||||||
|
}
|
||||||
|
Type::Text => {
|
||||||
|
write!(w, "FText")?;
|
||||||
|
}
|
||||||
|
Type::Enum { enum_type, .. } => {
|
||||||
|
write!(w, "TEnumAsByte<")?;
|
||||||
|
enum_type.rust_type(sdk, w)?;
|
||||||
|
write!(w, ">")?;
|
||||||
|
}
|
||||||
|
Type::Class(class) => {
|
||||||
|
class.rust_type(sdk, w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_class<W: Write>(class: &Class, _sdk: &Sdk, w: &mut W) -> anyhow::Result<()> {
|
||||||
|
let Class {
|
||||||
|
is_class,
|
||||||
|
size,
|
||||||
|
full_name,
|
||||||
|
..
|
||||||
|
} = class;
|
||||||
|
|
||||||
|
let name = class.rust_name();
|
||||||
|
|
||||||
|
writeln!(w, "#[repr(transparent)]")?;
|
||||||
|
writeln!(w, "#[derive(Debug)]")?;
|
||||||
|
if !is_class {
|
||||||
|
writeln!(w, "#[derive(Debug, Eq, PartialEq, Copy, Clone)]")?;
|
||||||
|
}
|
||||||
|
writeln!(w, "pub struct {name}(UnsafeCell<[u8; {size}]>);")?;
|
||||||
|
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
r#"
|
||||||
|
impl AsPtr for {name} {{
|
||||||
|
fn as_ptr(&self) -> *const u8 {{
|
||||||
|
self.0.get() as _
|
||||||
|
}}
|
||||||
|
fn as_mut_ptr(&self) -> *mut u8 {{
|
||||||
|
self.0.get()
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if !is_class {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
r#"
|
||||||
|
impl {name} {{
|
||||||
|
fn zeroed() -> Self {{
|
||||||
|
unsafe {{ core::mem::MaybeUninit::<Self>::zeroed().assume_init() }}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
writeln!(w, "impl StaticClass for {name} {{")?;
|
||||||
|
writeln!(w, "fn get_static_class() -> Option<UClass> {{")?;
|
||||||
|
write!(w, "let class: UClass = ")?;
|
||||||
|
generate_find_object(&full_name, w)?;
|
||||||
|
writeln!(w, ";")?;
|
||||||
|
writeln!(w, "class")?;
|
||||||
|
writeln!(w, "}}")?;
|
||||||
|
writeln!(w, "}}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_class_impl<W: Write>(class: &Class, sdk: &Sdk, w: &mut W) -> anyhow::Result<()> {
|
||||||
|
let Class {
|
||||||
|
super_class,
|
||||||
|
fields,
|
||||||
|
methods,
|
||||||
|
..
|
||||||
|
} = class;
|
||||||
|
|
||||||
|
let name = class.rust_name();
|
||||||
|
|
||||||
|
writeln!(w, "pub trait {}_Fields {{", name)?;
|
||||||
|
for field in fields {
|
||||||
|
write!(w, "fn get_{}(&self) -> &", field.name)?;
|
||||||
|
field.ty.rust_type(sdk, w)?;
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
" {{unsafe {{ &*self.as_ptr().offset({}) }} }}",
|
||||||
|
field.offset
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write!(w, "fn get_{}_mut(&mut self) -> &mut ", field.name)?;
|
||||||
|
field.ty.rust_type(sdk, w)?;
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
" {{unsafe {{ &mut *self.as_mut_ptr().offset({}) }} }}",
|
||||||
|
field.offset
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
writeln!(w, "}}")?;
|
||||||
|
writeln!(w, "impl {name}_Fields for {name} {{}}")?;
|
||||||
|
|
||||||
|
for method in methods {
|
||||||
|
generate_method_params(class, method, sdk, w)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(w, "pub trait {}_Methods {{", name)?;
|
||||||
|
|
||||||
|
for method in methods {
|
||||||
|
generate_method(class, method, sdk, w)?;
|
||||||
|
}
|
||||||
|
writeln!(w, "}}")?;
|
||||||
|
writeln!(w, "impl {name}_Methods for {name} {{}}")?;
|
||||||
|
|
||||||
|
if let Some(supr) = super_class {
|
||||||
|
let iter = core::iter::once(*supr).chain(supr.iter_super_structs());
|
||||||
|
for parent in iter {
|
||||||
|
if let Some(parent) = sdk.find_type(parent.as_uobject()) {
|
||||||
|
match parent {
|
||||||
|
Types::Class(class) => {
|
||||||
|
writeln!(w, "impl {}_Methods for {name} {{}}", class.rust_name())?;
|
||||||
|
writeln!(w, "impl {}_Fields for {name} {{}}", class.rust_name())?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: functions
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_find_object<W: Write>(name: &str, w: &mut W) -> anyhow::Result<()> {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
r#"
|
||||||
|
{{
|
||||||
|
static OBJECT: OnceCell<Option<UObject>> = OnceCell::new();
|
||||||
|
*OBJECT.get_or_init(|| {{
|
||||||
|
match GOBJECTS
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.as_objects()
|
||||||
|
.unwrap()
|
||||||
|
.find_class(&"{name}") {{
|
||||||
|
Some(class) => {{Some(class)}},
|
||||||
|
None => {{
|
||||||
|
log::error!("static object {name} not found!");
|
||||||
|
None
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}).cast()
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_method<W: Write>(
|
||||||
|
class: &Class,
|
||||||
|
method: &ClassMethod,
|
||||||
|
sdk: &Sdk,
|
||||||
|
w: &mut W,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
write!(w, "fn {}(&self", method.name)?;
|
||||||
|
|
||||||
|
for param in &method.parameters {
|
||||||
|
match param {
|
||||||
|
crate::sdk::ParameterKind::Default(parm) => {
|
||||||
|
write!(w, ",{}: &", parm.name)?;
|
||||||
|
parm.ty.rust_type(sdk, w)?;
|
||||||
|
}
|
||||||
|
crate::sdk::ParameterKind::Out(parm) => {
|
||||||
|
write!(w, ",{}: &mut ", parm.name)?;
|
||||||
|
parm.ty.rust_type(sdk, w)?;
|
||||||
|
write!(w, "/*(OutParm)*/")?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, ") -> ")?;
|
||||||
|
match &method.return_type {
|
||||||
|
Some(param) => {
|
||||||
|
param.ty.rust_type(sdk, w)?;
|
||||||
|
}
|
||||||
|
None => write!(w, "()")?,
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(w, " {{")?;
|
||||||
|
|
||||||
|
write!(w, "let func: UFunction = ")?;
|
||||||
|
generate_find_object(&method.full_name, w)?;
|
||||||
|
writeln!(w, ";")?;
|
||||||
|
|
||||||
|
let params_name = format!("{}_{}_Params", class.rust_name(), method.name);
|
||||||
|
writeln!(w, "let mut params = {params_name}::zeroed();")?;
|
||||||
|
|
||||||
|
for param in &method.parameters {
|
||||||
|
let param = param.as_param();
|
||||||
|
writeln!(w, "params.{0} = *{0};", param.name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(w, "let flags = *func.function_flags();")?;
|
||||||
|
writeln!(w, "process_event(&self as *const _, func, &mut params);")?;
|
||||||
|
writeln!(w, "*func.function_flags_mut() = flags;")?;
|
||||||
|
|
||||||
|
for param in &method.parameters {
|
||||||
|
match param {
|
||||||
|
crate::sdk::ParameterKind::Out(param) => {
|
||||||
|
writeln!(w, "*{0} = params.{0};", param.name)?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match &method.return_type {
|
||||||
|
Some(param) => {
|
||||||
|
writeln!(w, "params.{}", param.name)?;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(w, "}}")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_method_params<W: Write>(
|
||||||
|
class: &Class,
|
||||||
|
method: &ClassMethod,
|
||||||
|
sdk: &Sdk,
|
||||||
|
w: &mut W,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let name = format!("{}_{}_Params", class.rust_name(), method.name);
|
||||||
|
write!(w, "#[repr(C)]\n#[derive(Debug)]\nstruct {name} {{\n")?;
|
||||||
|
|
||||||
|
for param in &method.parameters {
|
||||||
|
write!(w, "\t{}: ", param.as_param().name)?;
|
||||||
|
param.as_param().ty.rust_type(sdk, w)?;
|
||||||
|
write!(w, ",\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "}}\n")?;
|
||||||
|
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
r#"
|
||||||
|
impl {name} {{
|
||||||
|
fn zeroed() -> Self {{
|
||||||
|
unsafe {{ core::mem::MaybeUninit::<Self>::zeroed().assume_init() }}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_sdk_to_tmp(sdk: &Sdk) -> anyhow::Result<()> {
|
||||||
|
let file = std::fs::File::create("z:/tmp/ark_sdk/mod.rs")?;
|
||||||
|
let mut writer = BufWriter::new(file);
|
||||||
|
|
||||||
|
for (_, pkg) in &sdk.packages {
|
||||||
|
let pkg_name = canonicalize_name(
|
||||||
|
&pkg.package
|
||||||
|
.get_full_name()
|
||||||
|
.context("could not get package name")?,
|
||||||
|
)
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
writeln!(writer, "pub mod {pkg_name};")?;
|
||||||
|
|
||||||
|
let file = std::fs::File::create(format!("z:/tmp/ark_sdk/{pkg_name}.rs"))?;
|
||||||
|
let mut writer = BufWriter::new(file);
|
||||||
|
generate_package_rust_module(pkg, sdk, &mut writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_package_rust_module<W: Write>(
|
||||||
|
pkg: &ProcessedPackage,
|
||||||
|
sdk: &Sdk,
|
||||||
|
w: &mut W,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"pub mod {} {{",
|
||||||
|
canonicalize_name(
|
||||||
|
&pkg.package
|
||||||
|
.get_full_name()
|
||||||
|
.context("could not get package name")?
|
||||||
|
)
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(w, "use core::ptr::NonNull;\nuse core::cell::UnsafeCell;\n")?;
|
||||||
|
for (pkg, _) in &pkg.package_dependencies {
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"use super::{};",
|
||||||
|
canonicalize_name(&pkg.get_full_name().context("could not get package name")?)
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, ty) in &pkg.types {
|
||||||
|
match ty {
|
||||||
|
Types::Class(class) => {
|
||||||
|
generate_class(&class, sdk, w)?;
|
||||||
|
generate_class_impl(&class, sdk, w)?;
|
||||||
|
}
|
||||||
|
Types::Enum(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(w, "}}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn inject_coreuobject_types(sdk: &mut Sdk) {
|
||||||
|
let uobject = Class {
|
||||||
|
is_class: true,
|
||||||
|
size: 40,
|
||||||
|
name: "Object".to_string(),
|
||||||
|
full_name: "Class CoreUObject.Object".to_string(),
|
||||||
|
super_class: None,
|
||||||
|
fields: vec![
|
||||||
|
ClassField {
|
||||||
|
offset: 0,
|
||||||
|
size: 8,
|
||||||
|
name: "vtbl".to_string(),
|
||||||
|
ty: Type::Ptr(Box::new(Type::Primitive(PrimitiveType::U8))),
|
||||||
|
},
|
||||||
|
ClassField {
|
||||||
|
offset: 8,
|
||||||
|
size: 4,
|
||||||
|
name: "object_flags".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::Custom("EObjectFlags")),
|
||||||
|
},
|
||||||
|
ClassField {
|
||||||
|
offset: 12,
|
||||||
|
size: 4,
|
||||||
|
name: "internal_index".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::U32),
|
||||||
|
},
|
||||||
|
ClassField {
|
||||||
|
offset: 16,
|
||||||
|
size: 8,
|
||||||
|
name: "class".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::Custom("Option<NonNull<UClass>>")),
|
||||||
|
},
|
||||||
|
ClassField {
|
||||||
|
offset: 24,
|
||||||
|
size: 8,
|
||||||
|
name: "name".to_string(),
|
||||||
|
ty: Type::Name,
|
||||||
|
},
|
||||||
|
ClassField {
|
||||||
|
offset: 32,
|
||||||
|
size: 8,
|
||||||
|
name: "outer".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::Custom("Option<NonNull<UObject>>")),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
methods: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let uobject = sdk.inject_type(uobject).expect("uobject");
|
||||||
|
let ufield = Class {
|
||||||
|
is_class: true,
|
||||||
|
size: 48,
|
||||||
|
name: "Field".to_string(),
|
||||||
|
full_name: "Class CoreUObject.Field".to_string(),
|
||||||
|
super_class: Some(unsafe { uobject.cast() }),
|
||||||
|
fields: vec![ClassField {
|
||||||
|
offset: 40,
|
||||||
|
size: 8,
|
||||||
|
name: "next".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::Custom("Option<NonNull<UField>>")),
|
||||||
|
}],
|
||||||
|
methods: vec![],
|
||||||
|
};
|
||||||
|
let ufield = sdk.inject_type(ufield).expect("ufield");
|
||||||
|
let ustruct = Class {
|
||||||
|
is_class: true,
|
||||||
|
size: 144,
|
||||||
|
name: "Struct".to_string(),
|
||||||
|
full_name: "Class CoreUObject.Struct".to_string(),
|
||||||
|
super_class: Some(unsafe { ufield.cast() }),
|
||||||
|
fields: vec![
|
||||||
|
ClassField {
|
||||||
|
offset: 48,
|
||||||
|
size: 8,
|
||||||
|
name: "super_struct".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::Custom("Option<NonNull<UStruct>>")),
|
||||||
|
},
|
||||||
|
ClassField {
|
||||||
|
offset: 56,
|
||||||
|
size: 8,
|
||||||
|
name: "children".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::Custom("Option<NonNull<UField>>")),
|
||||||
|
},
|
||||||
|
ClassField {
|
||||||
|
offset: 64,
|
||||||
|
size: 4,
|
||||||
|
name: "property_size".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::U32),
|
||||||
|
},
|
||||||
|
ClassField {
|
||||||
|
offset: 68,
|
||||||
|
size: 4,
|
||||||
|
name: "min_alignment".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::U32),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
methods: vec![],
|
||||||
|
};
|
||||||
|
let ustruct = sdk.inject_type(ustruct).expect("ustruct");
|
||||||
|
let ufunction = Class {
|
||||||
|
is_class: true,
|
||||||
|
size: 176,
|
||||||
|
name: "Function".to_string(),
|
||||||
|
full_name: "Class CoreUObject.Function".to_string(),
|
||||||
|
super_class: Some(unsafe { ustruct.cast() }),
|
||||||
|
fields: vec![ClassField {
|
||||||
|
offset: 144,
|
||||||
|
size: 4,
|
||||||
|
name: "function_flags".to_string(),
|
||||||
|
ty: Type::Primitive(PrimitiveType::U32),
|
||||||
|
}],
|
||||||
|
methods: vec![],
|
||||||
|
};
|
||||||
|
let _ufunction = sdk.inject_type(ufunction).expect("ufunction");
|
||||||
|
}
|
|
@ -152,6 +152,31 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn actor_static_class() -> Option<UClass> {
|
||||||
|
use crate::global_tables::objects::{FindClass, GOBJECTS};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
// this is something that we always do in C/C++ but unfortinately requires a blanket impl Sync + Send on all UObject types..
|
||||||
|
static CLASS: OnceCell<Option<UClass>> = OnceCell::new();
|
||||||
|
|
||||||
|
*CLASS.get_or_init(|| {
|
||||||
|
let class = match GOBJECTS
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.as_objects()
|
||||||
|
.unwrap()
|
||||||
|
.find_class("Class Engine.Actor")
|
||||||
|
{
|
||||||
|
Some(class) => Some(class),
|
||||||
|
None => {
|
||||||
|
log::error!("static class \"Class Engine.Actor\" not found!");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
log::info!("class for {} is {:?}", "Class Engine.Actor", class);
|
||||||
|
class
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! define_utypes {
|
macro_rules! define_utypes {
|
||||||
($($ty:ident),+) => {
|
($($ty:ident),+) => {
|
||||||
$(
|
$(
|
||||||
|
|
Loading…
Reference in a new issue