unreal-sdk/src/sdk/output/rust.rs
2023-04-21 17:21:07 +02:00

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");
}