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(&self, sdk: &Sdk, w: &mut W) -> anyhow::Result<()>; } impl RustType for T where T: AsUObject, { fn rust_type(&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(&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(&self, sdk: &Sdk, w: &mut W) -> anyhow::Result<()> { match self { Type::Ptr(ptr) => { write!(w, "Option>")?; } 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(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::::zeroed().assume_init() }} }} }} "# )?; } else { writeln!(w, "impl StaticClass for {name} {{")?; writeln!(w, "fn get_static_class() -> Option {{")?; 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(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(name: &str, w: &mut W) -> anyhow::Result<()> { write!( w, r#" {{ static OBJECT: OnceCell> = 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( 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( 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::::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( 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>")), }, 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>")), }, ], 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>")), }], 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>")), }, ClassField { offset: 56, size: 8, name: "children".to_string(), ty: Type::Primitive(PrimitiveType::Custom("Option>")), }, 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"); }