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 sdk {
|
||||
pub mod output;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{hash_map::Entry, BTreeMap, HashMap, HashSet},
|
||||
|
@ -22,11 +23,13 @@ pub mod sdk {
|
|||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||
|
||||
use crate::{
|
||||
global_tables::objects::{FindClass, GOBJECTS},
|
||||
v2_types::{
|
||||
actor_static_class,
|
||||
any_type::{self, AnyField, AnyObject, AnyProperty, AnyStruct},
|
||||
traits::{
|
||||
AsUObject, StaticClass, UArrayPropertyTrait, UBytePropertyTrait,
|
||||
|
@ -34,10 +37,12 @@ pub mod sdk {
|
|||
UObjectPropertyBaseTrait, UObjectTrait, UPropertyTrait, UStructNonConst,
|
||||
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 {
|
||||
classes: Mutex<HashMap<String, UClass>>,
|
||||
}
|
||||
|
@ -152,9 +157,14 @@ pub mod sdk {
|
|||
let name = strct
|
||||
.get_name()
|
||||
.context("failed to get struct or class name")?;
|
||||
|
||||
let full_name = strct
|
||||
.get_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 super_struct = if let Some(spr) = *strct.super_field() {
|
||||
|
@ -172,7 +182,16 @@ pub mod sdk {
|
|||
Ok(Class {
|
||||
is_class,
|
||||
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,
|
||||
fields,
|
||||
methods,
|
||||
|
@ -363,34 +382,33 @@ pub mod sdk {
|
|||
match Self::find_type(any_type::AnyProperty::from_prop(prop)) {
|
||||
Ok(ty) => {
|
||||
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()
|
||||
.contains(EPropertyFlags::ReturnParm)
|
||||
{
|
||||
Some(ParameterKind::Return)
|
||||
return_types.push(param.clone());
|
||||
Some(ParameterKind::Return(param))
|
||||
} else if prop.property_flags().contains(EPropertyFlags::OutParm) {
|
||||
if prop.property_flags().contains(EPropertyFlags::ConstParm) {
|
||||
Some(ParameterKind::Default)
|
||||
Some(ParameterKind::Default(param))
|
||||
} else {
|
||||
Some(ParameterKind::Out)
|
||||
Some(ParameterKind::Out(param))
|
||||
}
|
||||
} else if prop.property_flags().contains(EPropertyFlags::Parm) {
|
||||
Some(ParameterKind::Default)
|
||||
Some(ParameterKind::Default(param))
|
||||
} 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
params.extend(param);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("skipping field with offset {}: {e}", prop.offset());
|
||||
|
@ -507,6 +525,50 @@ pub mod sdk {
|
|||
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> {
|
||||
let gobjects = GOBJECTS.read().unwrap();
|
||||
let objects = gobjects.as_objects().unwrap();
|
||||
|
@ -634,7 +696,7 @@ pub mod sdk {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PrimitiveType {
|
||||
Bool,
|
||||
U8,
|
||||
|
@ -647,9 +709,10 @@ pub mod sdk {
|
|||
I64,
|
||||
F32,
|
||||
F64,
|
||||
Custom(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Type {
|
||||
Ptr(Box<Type>),
|
||||
Ref(Box<Type>),
|
||||
|
@ -697,10 +760,10 @@ pub mod sdk {
|
|||
pub ty: Type,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MethodParameter {
|
||||
pub name: String,
|
||||
pub ty: Type,
|
||||
pub is_out: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -709,8 +772,8 @@ pub mod sdk {
|
|||
pub full_name: String,
|
||||
pub is_native: bool,
|
||||
pub is_static: bool,
|
||||
pub parameters: Vec<MethodParameter>,
|
||||
pub return_type: Option<Type>,
|
||||
pub parameters: Vec<ParameterKind>,
|
||||
pub return_type: Option<MethodParameter>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -725,6 +788,10 @@ pub mod sdk {
|
|||
}
|
||||
|
||||
impl Class {
|
||||
pub fn rust_name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn referenced_types(&self) -> Vec<UObject> {
|
||||
let mut types = Vec::new();
|
||||
self.super_class.map(|obj| types.push(obj.as_uobject()));
|
||||
|
@ -738,13 +805,13 @@ pub mod sdk {
|
|||
method
|
||||
.return_type
|
||||
.as_ref()
|
||||
.and_then(|ty| ty.referenced_type()),
|
||||
.and_then(|param| param.ty.referenced_type()),
|
||||
);
|
||||
types.extend(
|
||||
method
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|param| ¶m.ty)
|
||||
.map(|param| ¶m.as_param().ty)
|
||||
.filter_map(|ty| ty.referenced_type()),
|
||||
);
|
||||
}
|
||||
|
@ -759,10 +826,21 @@ pub mod sdk {
|
|||
Enum(Enum),
|
||||
}
|
||||
|
||||
enum ParameterKind {
|
||||
Return,
|
||||
Default,
|
||||
Out,
|
||||
#[derive(Debug)]
|
||||
pub enum ParameterKind {
|
||||
Return(MethodParameter),
|
||||
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)]
|
||||
|
|
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 {
|
||||
($($ty:ident),+) => {
|
||||
$(
|
||||
|
|
Loading…
Reference in a new issue