546 lines
15 KiB
Rust
546 lines
15 KiB
Rust
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");
|
|
}
|