diff --git a/Cargo.lock b/Cargo.lock index 89e43e8a3..e9c96ff28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,7 +150,7 @@ version = "0.59.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -191,6 +191,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "brocolib" version = "0.1.0" @@ -330,6 +336,7 @@ checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" name = "cordl" version = "0.1.0" dependencies = [ + "bitflags 2.6.0", "brocolib", "byteorder", "bytes", diff --git a/Cargo.toml b/Cargo.toml index ded8ffc36..9281969ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ rayon = "1.8" filesize = "0.2.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +bitflags = "2.6.0" [profiles.release] opt-level = 3 diff --git a/src/data/mod.rs b/src/data/mod.rs index 80afc099a..81d74836a 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1 +1,2 @@ pub mod name_components; +pub mod type_resolver; diff --git a/src/data/type_resolver.rs b/src/data/type_resolver.rs new file mode 100644 index 000000000..3aa8ac353 --- /dev/null +++ b/src/data/type_resolver.rs @@ -0,0 +1,328 @@ +use brocolib::{ + global_metadata::{GenericParameterIndex, MethodIndex}, + runtime_metadata::{Il2CppType, Il2CppTypeEnum, TypeData}, +}; + +use itertools::Itertools; +use log::warn; + +use crate::generate::{ + cs_context_collection::TypeContextCollection, + cs_type::CsType, + cs_type_tag::CsTypeTag, + metadata::CordlMetadata, + type_extensions::{ParameterDefinitionExtensions, TypeDefinitionIndexExtensions}, +}; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub enum TypeUsage { + // Method usage + Parameter, + ReturnType, + + // References + Field, + Property, + + // naming the CppType itself + TypeName, + GenericArg, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum ResolvedTypeData { + Array(Box), + GenericInst(Box, Vec<(ResolvedType, bool)>), + GenericArg(GenericParameterIndex, u16), // points to class generic + GenericMethodArg(MethodIndex, GenericParameterIndex, u16), // points to method generic + Ptr(Box), + Type(CsTypeTag), + Primitive(Il2CppTypeEnum), + Blacklisted(CsTypeTag), + ByRef(Box), + ByRefConst(Box), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ResolvedType { + pub data: ResolvedTypeData, + pub ty: usize, +} + +pub struct TypeResolver<'a, 'b> { + pub cordl_metadata: &'a CordlMetadata<'b>, + pub collection: &'a TypeContextCollection, +} + +impl<'a, 'b> TypeResolver<'a, 'b> { + pub fn resolve_type( + &self, + declaring_cs_type: &mut CsType, + to_resolve_idx: usize, + typ_usage: TypeUsage, + add_include: bool, + ) -> ResolvedType { + let to_resolve = &self.cordl_metadata.metadata_registration.types[to_resolve_idx]; + let data = self.resolve_type_recurse( + declaring_cs_type, + to_resolve, + to_resolve_idx, + typ_usage, + add_include, + ); + + ResolvedType { + data, + ty: to_resolve_idx, + } + } + + /// [declaring_generic_inst_types] the generic instantiation of the declaring type + fn resolve_type_recurse( + &self, + declaring_cs_type: &mut CsType, + to_resolve: &Il2CppType, + to_resolve_idx: usize, + typ_usage: TypeUsage, + add_include: bool, + ) -> ResolvedTypeData { + let typ_tag = to_resolve.data; + let metadata = self.cordl_metadata; + + let ret = match to_resolve.ty { + // https://learn.microsoft.com/en-us/nimbusml/concepts/types + // https://en.cppreference.com/w/cpp/types/floating-point + Il2CppTypeEnum::I1 + | Il2CppTypeEnum::U1 + | Il2CppTypeEnum::I2 + | Il2CppTypeEnum::U2 + | Il2CppTypeEnum::I4 + | Il2CppTypeEnum::U4 + | Il2CppTypeEnum::I8 + | Il2CppTypeEnum::U8 + | Il2CppTypeEnum::R4 + | Il2CppTypeEnum::R8 + | Il2CppTypeEnum::Void + | Il2CppTypeEnum::Boolean + | Il2CppTypeEnum::Char + | Il2CppTypeEnum::String => { + declaring_cs_type + .requirements + .add_dependency_tag(CsTypeTag::from_type_data( + to_resolve.data, + self.cordl_metadata.metadata, + )); + ResolvedTypeData::Primitive(to_resolve.ty) + } + + Il2CppTypeEnum::Object + | Il2CppTypeEnum::Valuetype + | Il2CppTypeEnum::Class + | Il2CppTypeEnum::Typedbyref + // ptr types + | Il2CppTypeEnum::I + | Il2CppTypeEnum::U => self.resolve_ptr(typ_tag, declaring_cs_type, to_resolve, add_include), + + // Single dimension array + Il2CppTypeEnum::Szarray => { + let generic = match to_resolve.data { + TypeData::TypeIndex(e) => { + + self.resolve_type( + declaring_cs_type, + e, + typ_usage, + add_include + ) + } + + _ => panic!("Unknown type data for array {to_resolve:?}!"), + }; + + ResolvedTypeData::Array(Box::new(generic)) + } + // multi dimensional array + Il2CppTypeEnum::Array => { + // FIXME: when stack further implements the TypeData::ArrayType we can actually implement this fully to be a multidimensional array, whatever that might mean + warn!("Multidimensional array was requested but this is not implemented, typ: {to_resolve:?}, instead returning Il2CppObject!"); + ResolvedTypeData::Primitive(Il2CppTypeEnum::Object) + } + // + Il2CppTypeEnum::Mvar => match to_resolve.data { + TypeData::GenericParameterIndex(index) => { + let generic_param: &brocolib::global_metadata::Il2CppGenericParameter = + &metadata.metadata.global_metadata.generic_parameters[index]; + + let owner = generic_param.owner(metadata.metadata); + assert!(owner.is_method != u32::MAX); + + let (_gen_idx, gen_param) = owner + .generic_parameters(metadata.metadata) + .iter() + .find_position(|&p| p.name_index == generic_param.name_index) + .unwrap(); + + let method_index = MethodIndex::new(owner.owner_index); + + ResolvedTypeData::GenericMethodArg(method_index, index, gen_param.num) + } + _ => todo!(), + }, + Il2CppTypeEnum::Var => match to_resolve.data { + // Il2CppMetadataGenericParameterHandle + TypeData::GenericParameterIndex(index) => { + let generic_param: &brocolib::global_metadata::Il2CppGenericParameter = + &metadata.metadata.global_metadata.generic_parameters[index]; + + let _owner = generic_param.owner(metadata.metadata); + + ResolvedTypeData::GenericArg(index, generic_param.num) + } + _ => todo!(), + }, + Il2CppTypeEnum::Genericinst => match to_resolve.data { + TypeData::GenericClassIndex(e) => { + let mr = &metadata.metadata_registration; + let generic_class = mr.generic_classes.get(e).unwrap(); + let generic_inst = mr + .generic_insts + .get(generic_class.context.class_inst_idx.unwrap()) + .unwrap(); + + let new_generic_inst_types = &generic_inst.types; + + let generic_type_def = &mr.types[generic_class.type_index]; + let TypeData::TypeDefinitionIndex(tdi) = generic_type_def.data else { + panic!() + }; + + if add_include { + let generic_tag = CsTypeTag::from_type_data(to_resolve.data, metadata.metadata); + + // depend on both tdi and generic instantiation + declaring_cs_type.requirements.add_dependency_tag(tdi.into()); + declaring_cs_type.requirements.add_dependency_tag(generic_tag); + } + + let generic_resolved_args = new_generic_inst_types + // let generic_types_formatted = new_generic_inst_types + .iter() + .map(|gen_arg_t_idx| { + let gen_arg_ty = mr.types.get(*gen_arg_t_idx).unwrap(); + // we must include if the type is a value type + let should_include = gen_arg_ty.valuetype && add_include; + + let t =self.resolve_type( + declaring_cs_type, + *gen_arg_t_idx, + TypeUsage::GenericArg, + should_include + ); + (t, should_include) + }) + .collect_vec(); + + + let generic_resolved_type = self.resolve_type( + declaring_cs_type, + generic_class.type_index, + typ_usage, + add_include + ); + + // add generics to type def + ResolvedTypeData::GenericInst(Box::new(generic_resolved_type), generic_resolved_args) + } + + _ => panic!("Unknown type data for generic inst {to_resolve:?}!"), + }, + + + Il2CppTypeEnum::Ptr => { + let ptr_type = match to_resolve.data { + TypeData::TypeIndex(e) => { + self.resolve_type( + declaring_cs_type, + e, + typ_usage, + add_include + ) + } + + _ => panic!("Unknown type data for array {to_resolve:?}!"), + }; + + ResolvedTypeData::Ptr(Box::new(ptr_type)) + } + _ => panic!("/* UNKNOWN TYPE! {to_resolve:?} */"), + }; + + let byref_allowed = matches!( + typ_usage, + TypeUsage::Parameter + | TypeUsage::ReturnType + | TypeUsage::TypeName + | TypeUsage::GenericArg + ); + + if (to_resolve.is_param_out() || (to_resolve.byref && !to_resolve.valuetype)) + && byref_allowed + { + return ResolvedTypeData::ByRef(Box::new(ResolvedType { + ty: to_resolve_idx, + data: ret, + })); + } + + if to_resolve.is_param_in() && byref_allowed { + return ResolvedTypeData::ByRefConst(Box::new(ResolvedType { + ty: to_resolve_idx, + data: ret, + })); + } + + ret + } + + fn resolve_ptr( + &self, + typ_tag: TypeData, + declaring_cs_type: &mut CsType, + to_resolve: &Il2CppType, + add_include: bool, + ) -> ResolvedTypeData { + let metadata = self.cordl_metadata; + let ctx_collection = self.collection; + let typ_cpp_tag: CsTypeTag = typ_tag.into(); + // Self + if typ_cpp_tag == declaring_cs_type.self_tag { + return ResolvedTypeData::Type(typ_cpp_tag); + } + + if let TypeData::TypeDefinitionIndex(tdi) = to_resolve.data + && metadata.blacklisted_types.contains(&tdi) + { + // blacklist if needed + + return ResolvedTypeData::Blacklisted(typ_cpp_tag); + } + + if add_include { + declaring_cs_type + .requirements + .add_dependency_tag(typ_cpp_tag); + } + + let to_incl_cpp_ty = ctx_collection + .get_cs_type(to_resolve.data.into()) + .unwrap_or_else(|| panic!("Unable to get type to include {:?}", to_resolve.data)); + + ResolvedTypeData::Type(to_incl_cpp_ty.self_tag) + } +} + +impl ResolvedType { + pub fn get_type<'a>(&self, metadata: &CordlMetadata<'a>) -> &'a Il2CppType { + &metadata.metadata_registration.types[self.ty] + } +} diff --git a/src/generate/context.rs b/src/generate/context.rs index 7199ed7a0..c4cd3500f 100644 --- a/src/generate/context.rs +++ b/src/generate/context.rs @@ -1,151 +1,45 @@ -use std::cmp::Ordering; -use std::io::Write; -use std::{ - collections::{HashMap, HashSet}, - fs::{create_dir_all, remove_file, File}, - path::{Path, PathBuf}, -}; +use std::collections::HashMap; use brocolib::global_metadata::TypeDefinitionIndex; +use log::info; -use color_eyre::eyre::ContextCompat; - -use itertools::Itertools; -use log::{info, trace}; -use pathdiff::diff_paths; - -use crate::generate::cs_type::CORDL_NO_INCLUDE_IMPL_DEFINE; -use crate::generate::members::CppForwardDeclare; -use crate::generate::{members::CppInclude, type_extensions::TypeDefinitionExtensions}; -use crate::helpers::sorting::DependencyGraph; -use crate::STATIC_CONFIG; - -use super::cpp_type_tag::CppTypeTag; -use super::cs_type::IL2CPP_OBJECT_TYPE; use super::{ - config::GenerationConfig, - cpp_type::CppType, - cs_type::CSType, - members::CppUsingAlias, - metadata::Metadata, - writer::{CppWriter, Writable}, + cs_type::CsType, cs_type_tag::CsTypeTag, metadata::CordlMetadata, + type_extensions::TypeDefinitionExtensions, }; // Holds the contextual information for creating a C++ file // Will hold various metadata, such as includes, type definitions, and extraneous writes #[derive(Debug, Clone)] -pub struct CppContext { - pub typedef_path: PathBuf, - pub type_impl_path: PathBuf, - - // combined header - pub fundamental_path: PathBuf, - +pub struct TypeContext { // Types to write, typedef - pub typedef_types: HashMap, - - // Namespace -> alias - pub typealias_types: HashSet<(String, CppUsingAlias)>, + pub typedef_types: HashMap, } -impl CppContext { - pub fn get_cpp_type_recursive_mut( - &mut self, - root_tag: CppTypeTag, - child_tag: CppTypeTag, - ) -> Option<&mut CppType> { - let ty = self.typedef_types.get_mut(&root_tag); - if root_tag == child_tag { - return ty; - } - - ty.and_then(|ty| ty.get_nested_type_mut(child_tag)) - } - pub fn get_cpp_type_recursive( - &self, - root_tag: CppTypeTag, - child_tag: CppTypeTag, - ) -> Option<&CppType> { - let ty = self.typedef_types.get(&root_tag); - // if a root type - if root_tag == child_tag { - return ty; - } - - ty.and_then(|ty| ty.get_nested_type(child_tag)) - } - - pub fn get_include_path(&self) -> &PathBuf { - &self.typedef_path - } - - pub fn get_types(&self) -> &HashMap { +impl TypeContext { + pub fn get_types(&self) -> &HashMap { &self.typedef_types } + pub fn get_types_mut(&mut self) -> &mut HashMap { + &mut self.typedef_types + } // TODO: Move out, this is CSContext - pub fn make( - metadata: &Metadata, - config: &GenerationConfig, - tdi: TypeDefinitionIndex, - tag: CppTypeTag, - generic_inst: Option<&Vec>, - ) -> CppContext { + pub fn make(metadata: &CordlMetadata, tdi: TypeDefinitionIndex, tag: CsTypeTag) -> TypeContext { let t = &metadata.metadata.global_metadata.type_definitions[tdi]; let components = t.get_name_components(metadata.metadata); - let ns = &components.namespace.unwrap_or_default(); - let name = &components.name; - - let cpp_namespace = config.namespace_cpp(ns); - let cpp_name = config.namespace_cpp(name); - - let ns_path = config.namespace_path(ns); - let path = if ns_path.is_empty() { - "GlobalNamespace/".to_string() - } else { - ns_path + "/" - }; - let path_name = match t.declaring_type_index != u32::MAX { - true => { - let name = config.path_name(name); - let base_name = components.declaring_types.unwrap_or_default().join("_"); + let _ns = &components.namespace.unwrap_or_default(); + let _name = &components.name; - format!("{base_name}_{name}") - } - false => config.path_name(name), - }; - - let mut x = CppContext { - typedef_path: config - .header_path - .join(format!("{path}zzzz__{path_name}_def.hpp")), - type_impl_path: config - .header_path - .join(format!("{path}zzzz__{path_name}_impl.hpp")), - fundamental_path: config.header_path.join(format!("{path}{path_name}.hpp")), + let mut x = TypeContext { typedef_types: Default::default(), - typealias_types: Default::default(), }; - if metadata.blacklisted_types.contains(&tdi) { - if !t.is_value_type() { - x.typealias_types.insert(( - cpp_namespace, - CppUsingAlias { - alias: cpp_name.to_string(), - result: IL2CPP_OBJECT_TYPE.to_string(), - template: Default::default(), - }, - )); - } - return x; - } - - match CppType::make_cpp_type(metadata, config, tdi, tag, generic_inst) { + match CsType::make_cs_type(metadata, tdi, tag) { Some(cpptype) => { - x.insert_cpp_type(cpptype); + x.insert_cs_type(cpptype); } None => { info!( @@ -158,424 +52,7 @@ impl CppContext { x } - pub fn insert_cpp_type(&mut self, cpp_type: CppType) { - if cpp_type.nested { - panic!( - "Cannot have a root type as a nested type! {}", - &cpp_type.cpp_name_components.combine_all() - ); - } + pub fn insert_cs_type(&mut self, cpp_type: CsType) { self.typedef_types.insert(cpp_type.self_tag, cpp_type); } - - pub fn write(&self, config: &GenerationConfig) -> color_eyre::Result<()> { - // Write typedef file first - if Path::exists(self.typedef_path.as_path()) { - remove_file(self.typedef_path.as_path())?; - } - if !Path::is_dir( - self.typedef_path - .parent() - .context("parent is not a directory!")?, - ) { - // Assume it's never a file - create_dir_all( - self.typedef_path - .parent() - .context("Failed to create all directories!")?, - )?; - } - - let base_path = &config.header_path; - - trace!("Writing {:?}", self.typedef_path.as_path()); - let mut typedef_writer = CppWriter { - stream: File::create(self.typedef_path.as_path())?, - indent: 0, - newline: true, - }; - let mut typeimpl_writer = CppWriter { - stream: File::create(self.type_impl_path.as_path())?, - indent: 0, - newline: true, - }; - let mut fundamental_writer = CppWriter { - stream: File::create(self.fundamental_path.as_path())?, - indent: 0, - newline: true, - }; - - writeln!(typedef_writer, "#pragma once")?; - writeln!(typeimpl_writer, "#pragma once")?; - writeln!(fundamental_writer, "#pragma once")?; - - // add IWYU - let typedef_include_path = diff_paths(&self.typedef_path, base_path).unwrap(); - let typeimpl_include_path = diff_paths(&self.type_impl_path, base_path).unwrap(); - let fundamental_include_path = diff_paths(&self.fundamental_path, base_path).unwrap(); - - let fundamental_include_pragma = format!( - "// IWYU pragma private; include \"{}\"", - fundamental_include_path.display() - ); - writeln!(typedef_writer, "{fundamental_include_pragma}")?; - writeln!(typeimpl_writer, "{fundamental_include_pragma}")?; - writeln!(fundamental_writer, "// IWYU pragma: begin_exports")?; - - // Include cordl config - // this is so confusing but basically gets the relative folder - // navigation for `_config.hpp` - let dest_path = diff_paths( - &STATIC_CONFIG.dst_header_internals_file, - self.typedef_path.parent().unwrap(), - ) - .unwrap(); - - // write typedefs.h include first - this makes include order mostly happy (probably System.Object would still be weird!) - CppInclude::new_exact("beatsaber-hook/shared/utils/typedefs.h") - .write(&mut typedef_writer)?; - CppInclude::new_exact(dest_path).write(&mut typedef_writer)?; - - // after including cordl internals - // macro module init - writeln!(typedef_writer, "CORDL_MODULE_INIT")?; - - // alphabetical sorted - let typedef_types = self - .typedef_types - .values() - .flat_map(|t: &CppType| -> Vec<&CppType> { - t.nested_types_flattened().values().copied().collect_vec() - }) - .chain(self.typedef_types.values()) - .sorted_by(|a, b| a.cpp_name_components.cmp(&b.cpp_name_components)) - // Enums go after stubs - .sorted_by(|a, b| { - if a.is_enum_type == b.is_enum_type { - return Ordering::Equal; - } - - if a.is_enum_type { - Ordering::Less - } else if b.is_enum_type { - Ordering::Greater - } else { - Ordering::Equal - } - }) - // Stubs are first - .sorted_by(|a, b| { - if a.is_stub == b.is_stub { - return Ordering::Equal; - } - - if a.is_stub { - Ordering::Less - } else if b.is_stub { - Ordering::Greater - } else { - Ordering::Equal - } - }) - // Value types are last - .sorted_by(|a, b| { - let a_strictly_vt = a.is_value_type && !a.is_enum_type; - let b_strictly_vt = b.is_value_type && !b.is_enum_type; - - if a_strictly_vt == b_strictly_vt { - return Ordering::Equal; - } - - if a_strictly_vt { - Ordering::Greater - } else if b_strictly_vt { - Ordering::Less - } else { - Ordering::Equal - } - }) - .collect_vec(); - - let typedef_root_types = typedef_types - .iter() - .filter(|t| self.typedef_types.contains_key(&t.self_tag)) - .collect_vec(); - - let mut ts = DependencyGraph::::new(|a, b| a.cmp(b)); - for cpp_type in &typedef_root_types { - ts.add_root_dependency(&cpp_type.self_tag); - - for dep in cpp_type.requirements.depending_types.iter().sorted() { - ts.add_dependency(&cpp_type.self_tag, dep); - - // add dependency for generic instantiations - // for all types with the same TDI - if let CppTypeTag::TypeDefinitionIndex(tdi) = dep { - // find all generic tags that have the same TDI - let generic_tags_in_context = - typedef_root_types.iter().filter(|t| match t.self_tag { - CppTypeTag::TypeDefinitionIndex(_) => false, - CppTypeTag::GenericInstantiation(gen_inst) => gen_inst.tdi == *tdi, - }); - - generic_tags_in_context.for_each(|generic_dep| { - ts.add_dependency(&cpp_type.self_tag, &generic_dep.self_tag); - }) - } - } - } - - // types that don't depend on anyone - // we take these because they get undeterministically sorted - // and can be first anyways - let mut undepended_cpp_types = vec![]; - - // currently sorted from root to dependencies - // aka least depended to most depended - let mut typedef_root_types_sorted = ts - .topological_sort() - .into_iter() - .filter_map(|t| self.typedef_types.get(t)) - .collect_vec(); - - // add the items with no dependencies at the tail - // when reversed these will be first and can be allowed to be first - typedef_root_types_sorted.append(&mut undepended_cpp_types); - // typedef_root_types_sorted.reverse(); - - // Write includes for typedef - typedef_types - .iter() - .flat_map(|t| &t.requirements.required_def_includes) - .unique() - .sorted() - .try_for_each(|i| i.write(&mut typedef_writer))?; - - // Write includes for typeimpl - typedef_types - .iter() - .flat_map(|t| &t.requirements.required_impl_includes) - .unique() - .sorted() - .try_for_each(|i| i.write(&mut typeimpl_writer))?; - - // add module declarations - writeln!( - typedef_writer, - "CORDL_MODULE_EXPORT({})", - self.fundamental_path.file_stem().unwrap().to_string_lossy() - )?; - - // anonymous namespace - if STATIC_CONFIG.use_anonymous_namespace { - writeln!(typedef_writer, "CORDL_MODULE_EXPORT_STRUCT namespace {{")?; - writeln!(typeimpl_writer, "CORDL_MODULE_EXPORT_STRUCT namespace {{")?; - } - - // write forward declares - // and includes for impl - { - CppInclude::new_exact(&typedef_include_path).write(&mut typeimpl_writer)?; - - let forward_declare_and_includes = || { - typedef_types - .iter() - .flat_map(|t| &t.requirements.forward_declares) - }; - - forward_declare_and_includes() - .map(|(_fd, inc)| inc) - .unique() - .sorted() - // TODO: Check forward declare is not of own type - .try_for_each(|i| -> color_eyre::Result<()> { - i.write(&mut typeimpl_writer)?; - Ok(()) - })?; - - forward_declare_and_includes() - .map(|(fd, _inc)| fd) - .unique() - .sorted_by(|a, b| { - let do_format = |fd: &CppForwardDeclare| { - format!( - "{}_{}_{}_{}", - fd.cpp_namespace.clone().unwrap_or_default(), - fd.cpp_name, - fd.literals.clone().unwrap_or_default().join(","), - fd.templates - .clone() - .unwrap_or_default() - .just_names() - .join(",") - ) - }; - let a_str = do_format(a); - let b_str = do_format(b); - - a_str.cmp(&b_str) - }) - .try_for_each(|fd| fd.write(&mut typedef_writer))?; - - writeln!(typedef_writer, "// Forward declare root types")?; - //Forward declare all types - typedef_root_types - .iter() - .map(|t| CppForwardDeclare::from_cpp_type(t)) - // TODO: Check forward declare is not of own type - .try_for_each(|fd| { - // Forward declare and include - fd.write(&mut typedef_writer) - })?; - - writeln!(typedef_writer, "// Write type traits")?; - typedef_root_types - .iter() - .try_for_each(|cpp_type| -> color_eyre::Result<()> { - if cpp_type.generic_instantiations_args_types.is_none() { - cpp_type.write_type_trait(&mut typedef_writer)?; - } - Ok(()) - })?; - - // This is likely not necessary - // self.typedef_types - // .values() - // .flat_map(|t| &t.requirements.forward_declares) - // .map(|(_, i)| i) - // .unique() - // // TODO: Check forward declare is not of own type - // .try_for_each(|i| i.write(&mut typeimpl_writer))?; - } - - for t in &typedef_root_types_sorted { - if t.nested { - panic!( - "Cannot have a root type as a nested type! {}", - &t.cpp_name_components.combine_all() - ); - } - // if t.generic_instantiation_args.is_none() || true { - // t.write_def(&mut typedef_writer)?; - // t.write_impl(&mut typeimpl_writer)?; - // } else { - // t.write_def(&mut typeimpl_writer)?; - // t.write_impl(&mut typeimpl_writer)?; - // } - - t.write_def(&mut typedef_writer)?; - t.write_impl(&mut typeimpl_writer)?; - } - - // end anonymous namespace - if STATIC_CONFIG.use_anonymous_namespace { - writeln!(typedef_writer, "}} // end anonymous namespace")?; - writeln!(typeimpl_writer, "}} // end anonymous namespace")?; - } - - // write macros - typedef_types - .iter() - .try_for_each(|t| Self::write_il2cpp_arg_macros(t, &mut typedef_writer))?; - - // Fundamental - { - CppInclude::new_exact(typedef_include_path).write(&mut fundamental_writer)?; - - // if guard for intellisense - writeln!(fundamental_writer, "#ifndef {CORDL_NO_INCLUDE_IMPL_DEFINE}")?; - CppInclude::new_exact(diff_paths(&self.type_impl_path, base_path).unwrap()) - .write(&mut fundamental_writer)?; - writeln!(fundamental_writer, "#endif")?; - - // end IWYU - writeln!(fundamental_writer, "// IWYU pragma: end_exports")?; - } - - // TODO: Write type impl and fundamental files here - Ok(()) - } - - fn write_il2cpp_arg_macros( - ty: &CppType, - writer: &mut super::writer::CppWriter, - ) -> color_eyre::Result<()> { - let is_generic_instantiation = ty.generic_instantiations_args_types.is_some(); - if is_generic_instantiation { - return Ok(()); - } - - let template_container_type = ty.is_stub - || ty - .cpp_template - .as_ref() - .is_some_and(|t| !t.names.is_empty()); - - if !ty.is_value_type && !ty.is_stub && !template_container_type && !is_generic_instantiation - { - // reference types need no boxing - writeln!( - writer, - "NEED_NO_BOX({});", - ty.cpp_name_components - .clone() - .remove_generics() - .remove_pointer() - .combine_all() - )?; - } - - if ty.nested { - writeln!( - writer, - "// TODO: Nested type, check correct definition print!" - )?; - } - - let macro_arg_define = { - match //ty.generic_instantiation_args.is_some() || - template_container_type { - true => match ty.is_value_type { - true => "DEFINE_IL2CPP_ARG_TYPE_GENERIC_STRUCT", - false => "DEFINE_IL2CPP_ARG_TYPE_GENERIC_CLASS", - }, - false => "DEFINE_IL2CPP_ARG_TYPE", - } - }; - - // Essentially splits namespace.foo/nested_foo into (namespace, foo/nested_foo) - - let namespace = ty.cs_name_components.namespace.clone().unwrap_or_default(); - let combined_name = match &ty.cs_name_components.declaring_types { - None => ty.cs_name_components.name.clone(), - Some(declaring_types) => format!( - "{}/{}", - declaring_types.join("/"), - ty.cs_name_components.name.clone() - ), - }; - - // generics shouldn't emit with a pointer, while regular types should honor the pointer - let cpp_name = match template_container_type { - true => ty - .cpp_name_components - .clone() - .remove_generics() - .remove_pointer() - .combine_all(), - - false => ty - .cpp_name_components - .clone() - .remove_generics() - .combine_all(), - }; - - writeln!( - writer, - "{macro_arg_define}({cpp_name}, \"{namespace}\", \"{combined_name}\");", - )?; - - Ok(()) - } } diff --git a/src/generate/context_collection.rs b/src/generate/context_collection.rs deleted file mode 100644 index 512983d3f..000000000 --- a/src/generate/context_collection.rs +++ /dev/null @@ -1,787 +0,0 @@ -use core::panic; -use std::{ - collections::{HashMap, HashSet}, - fs::File, - io::Write, -}; - -use brocolib::{ - global_metadata::TypeDefinitionIndex, - runtime_metadata::{Il2CppMethodSpec, TypeData}, -}; -use itertools::Itertools; -use log::{info, trace, warn}; -use pathdiff::diff_paths; - -use crate::{ - generate::{cpp_type::CppType, cs_type::CSType}, - STATIC_CONFIG, -}; - -use super::{ - config::GenerationConfig, - context::CppContext, - cpp_type_tag::{CppTypeTag, GenericInstantiation}, - metadata::Metadata, - type_extensions::TypeDefinitionExtensions, -}; - -pub struct CppContextCollection { - // Should always be a TypeDefinitionIndex - all_contexts: HashMap, - pub alias_context: HashMap, - pub alias_nested_type_to_parent: HashMap, - filled_types: HashSet, - filling_types: HashSet, - borrowing_types: HashSet, -} - -impl CppContextCollection { - pub fn fill_cpp_type( - &mut self, - cpp_type: &mut CppType, - metadata: &Metadata, - config: &GenerationConfig, - ) { - let tag = cpp_type.self_tag; - - if self.filled_types.contains(&tag) { - return; - } - if self.filling_types.contains(&tag) { - panic!("Currently filling type {tag:?}, cannot fill") - } - - // Move ownership to local - self.filling_types.insert(tag); - - cpp_type.fill_from_il2cpp(metadata, config, self); - - self.filled_types.insert(tag); - self.filling_types.remove(&tag.clone()); - } - - pub fn fill(&mut self, metadata: &Metadata, config: &GenerationConfig, type_tag: CppTypeTag) { - let _tdi = CppType::get_cpp_tag_tdi(type_tag); - - let context_tag = self.get_context_root_tag(type_tag); - - if self.filled_types.contains(&type_tag) { - return; - } - - if self.borrowing_types.contains(&context_tag) { - panic!("Borrowing context {context_tag:?}"); - } - - // Move ownership to local - let cpp_type_entry = self - .all_contexts - .get_mut(&context_tag) - .expect("No cpp context") - .typedef_types - .remove_entry(&type_tag); - - // In some occasions, the CppContext can be empty - if let Some((_t, mut cpp_type)) = cpp_type_entry { - assert!(!cpp_type.nested, "Cannot fill a nested type!"); - - self.fill_cpp_type(&mut cpp_type, metadata, config); - - // Move ownership back up - self.all_contexts - .get_mut(&context_tag) - .expect("No cpp context") - .insert_cpp_type(cpp_type); - } - } - - fn alias_nested_types(&mut self, owner: &CppType, root_tag: CppTypeTag, context_check: bool) { - for (tag, nested_type) in &owner.nested_types { - // info!( - // "Aliasing {:?} to {:?}", - // nested_type.self_tag, owner.self_tag - // ); - self.alias_type_to_context(*tag, root_tag, context_check, false); - self.alias_type_to_parent(*tag, owner.self_tag, context_check); - - self.alias_nested_types(nested_type, root_tag, context_check); - } - } - - pub fn alias_type_to_context( - &mut self, - src: CppTypeTag, - dest: CppTypeTag, - context_check: bool, - overrid: bool, - ) { - if self.alias_context.contains_key(&dest) && !overrid { - panic!("Aliasing an aliased type! {src:?} to {dest:?}"); - } - if context_check && !self.all_contexts.contains_key(&dest) { - panic!("Aliased context {src:?} to {dest:?} doesn't have a context"); - } - if self.alias_context.contains_key(&src) && context_check { - panic!("Already aliased this key!"); - } - self.alias_context.insert(src, dest); - } - - pub fn alias_type_to_parent( - &mut self, - src: CppTypeTag, - dest: CppTypeTag, - _context_check: bool, - ) { - // if context_check && !self.all_contexts.contains_key(&dest) { - // panic!("Aliased nested type {src:?} to {dest:?} doesn't have a parent"); - // } - if src == dest { - panic!("Self {src:?} can't point to dest!") - } - if self.alias_nested_type_to_parent.get(&dest) == Some(&src) { - panic!("Parent {dest:?} can't be assigned to src {src:?}!") - } - self.alias_nested_type_to_parent.insert(src, dest); - } - - pub fn fill_nested_types( - &mut self, - metadata: &Metadata, - config: &GenerationConfig, - owner_ty: CppTypeTag, - ) { - let owner_type_tag = owner_ty; - let owner = self - .get_cpp_type_mut(owner_type_tag) - .unwrap_or_else(|| panic!("Owner does not exist {owner_type_tag:?}")); - - // we clone, then write later - // since we're modifying only 1 type exclusively - // and we don't rely on any other type at this time - // we can clone - - // sad inefficient memory usage but oh well - let nested_types: HashMap = owner - .nested_types - .clone() - .into_iter() - .map(|(nested_tag, mut nested_type)| { - self.fill_cpp_type(&mut nested_type, metadata, config); - - (nested_tag, nested_type) - }) - .collect(); - - self.get_cpp_type_mut(owner_type_tag).unwrap().nested_types = nested_types; - } - - pub fn get_context_root_tag(&self, ty: CppTypeTag) -> CppTypeTag { - self.alias_context - .get(&ty) - .cloned() - // .map(|t| self.get_context_root_tag(*t)) - .unwrap_or(ty) - } - pub fn get_parent_or_self_tag(&self, ty: CppTypeTag) -> CppTypeTag { - self.alias_nested_type_to_parent - .get(&ty) - .cloned() - .map(|t| self.get_parent_or_self_tag(t)) - .unwrap_or(ty) - } - - pub fn make_nested_from( - &mut self, - metadata: &Metadata<'_>, - config: &GenerationConfig, - tdi: TypeDefinitionIndex, - generic_inst: Option<&Vec>, - ) -> Option<&mut CppContext> { - let ty_data = CppTypeTag::TypeDefinitionIndex(tdi); - let ty_def = &metadata.metadata.global_metadata.type_definitions[tdi]; - let context_root_tag = self.get_context_root_tag(ty_data); - - if self.filling_types.contains(&context_root_tag) { - panic!("Currently filling type {context_root_tag:?}, cannot fill") - } - - // Why is the borrow checker so dumb? - // Using entries causes borrow checker to die :( - if self.filled_types.contains(&ty_data) { - return Some(self.all_contexts.get_mut(&context_root_tag).unwrap()); - } - - if self.get_cpp_type(ty_data).is_some() { - return self.get_context_mut(ty_data); - } - - let context_tag = self.get_context_root_tag(ty_data); - let context_type_data: TypeDefinitionIndex = context_tag.into(); - let context_td = &metadata.metadata.global_metadata.type_definitions[context_type_data]; - - if metadata.blacklisted_types.contains(&tdi) { - warn!( - "Skipping nested type because it's blacklisted! {context_tag:?} {}", - context_td.full_name(metadata.metadata, true) - ); - return None; - } - - let nested_inherits_declaring = ty_def.is_assignable_to(context_td, metadata.metadata); - if nested_inherits_declaring { - warn!( - "Nested type \"{}\" inherits declaring type \"{}\"", - ty_def.full_name(metadata.metadata, true), - context_td.full_name(metadata.metadata, true) - ); - } - - match nested_inherits_declaring { - true => { - // If a nested type inherits its declaring type, move it to its own CppContext - - let context = CppContext::make(metadata, config, tdi, ty_data, generic_inst); - - // Unnest type does not alias to another context or type - self.alias_context.remove(&ty_data); - self.alias_nested_type_to_parent.remove(&ty_data); - - self.all_contexts.insert(ty_data, context); - self.all_contexts.get_mut(&ty_data) - } - false => { - let new_cpp_type = - CppType::make_cpp_type(metadata, config, tdi, ty_data, generic_inst) - .expect("Failed to make nested type"); - - let context = self.get_context_mut(ty_data).unwrap(); - // self.alias_type_to_context(new_cpp_type.self_tag, context_root_tag, true); - - // context.insert_cpp_type(stub); - context.insert_cpp_type(new_cpp_type); - - Some(context) - } - } - } - - /// Make a generic type - /// based of an existing type definition - /// and give it the generic args - pub fn make_generic_from( - &mut self, - method_spec: &Il2CppMethodSpec, - metadata: &mut Metadata, - config: &GenerationConfig, - ) -> Option<&mut CppContext> { - // Not a generic class, no type needed - if method_spec.class_inst_index == u32::MAX { - return None; - } - // Skip generic methods? - if method_spec.method_inst_index != u32::MAX { - return None; - } - - let method = - &metadata.metadata.global_metadata.methods[method_spec.method_definition_index]; - let ty_def = &metadata.metadata.global_metadata.type_definitions[method.declaring_type]; - - if ty_def.is_interface() { - // Skip interface - info!( - "Skipping make interface for generic instantiation {}", - ty_def.full_name(metadata.metadata, true) - ); - return None; - } - - let type_data = CppTypeTag::TypeDefinitionIndex(method.declaring_type); - let tdi = method.declaring_type; - let context_root_tag = self.get_context_root_tag(type_data); - - if metadata.blacklisted_types.contains(&tdi) { - warn!( - "Skipping generic instantiation {tdi:?} {} {}", - method_spec.class_inst_index, - ty_def.full_name(metadata.metadata, true) - ); - return None; - } - - if self.filling_types.contains(&context_root_tag) { - panic!("Currently filling type {context_root_tag:?}, cannot fill") - } - - let generic_class_ty_data = CppTypeTag::GenericInstantiation(GenericInstantiation { - tdi, - inst: method_spec.class_inst_index as usize, - }); - - let generic_inst = - &metadata.metadata_registration.generic_insts[method_spec.class_inst_index as usize]; - - // Why is the borrow checker so dumb? - // Using entries causes borrow checker to die :( - if self.filled_types.contains(&generic_class_ty_data) { - return Some(self.all_contexts.get_mut(&context_root_tag).unwrap()); - } - - if self.get_cpp_type(generic_class_ty_data).is_some() { - return self.get_context_mut(generic_class_ty_data); - } - - // make original type a stub - self.borrow_cpp_type(type_data, |_, mut cpptype| { - cpptype.is_stub = true; - - cpptype - }); - - let mut new_cpp_type = CppType::make_cpp_type( - metadata, - config, - tdi, - generic_class_ty_data, - Some(&generic_inst.types), - ) - .expect("Failed to make generic type"); - new_cpp_type.self_tag = generic_class_ty_data; - self.alias_type_to_context(new_cpp_type.self_tag, context_root_tag, true, false); - - // TODO: Not needed since making a cpp type will already be a stub in other passes? - // this is the generic stub - // this might cause problems, hopefully not - // since two types can coexist with the TDI though only one is nested - // let mut stub = new_cpp_type.clone(); - // stub.self_tag = type_data; - - new_cpp_type.requirements.add_dependency_tag(type_data); - - // if generic type is a nested type - // put it under the parent's `nested_types` field - // otherwise put it in the typedef's hashmap - - let context = self.get_context_mut(generic_class_ty_data).unwrap(); - - // context.insert_cpp_type(stub); - context.insert_cpp_type(new_cpp_type); - - Some(context) - } - - /// - /// It's important this gets called AFTER the type is filled - /// - pub fn fill_generic_method_inst( - &mut self, - method_spec: &Il2CppMethodSpec, - metadata: &mut Metadata, - config: &GenerationConfig, - ) -> Option<&mut CppContext> { - if method_spec.method_inst_index == u32::MAX { - return None; - } - - let method = - &metadata.metadata.global_metadata.methods[method_spec.method_definition_index]; - - // is reference type - // only make generic spatialization - let type_data = CppTypeTag::TypeDefinitionIndex(method.declaring_type); - let tdi = method.declaring_type; - - let ty_def = &metadata.metadata.global_metadata.type_definitions[method.declaring_type]; - - if metadata.blacklisted_types.contains(&tdi) { - info!( - "Skipping {tdi:?} {} since it is blacklisted", - ty_def.full_name(metadata.metadata, true) - ); - return None; - } - - if ty_def.is_interface() { - // Skip interface - info!( - "Skipping fill generic method interface for generic instantiation {}", - ty_def.full_name(metadata.metadata, true) - ); - return None; - } - - let context_root_tag = self.get_context_root_tag(type_data); - - let generic_class_ty_data = if method_spec.class_inst_index != u32::MAX { - CppTypeTag::GenericInstantiation(GenericInstantiation { - tdi, - inst: method_spec.class_inst_index as usize, - }) - } else { - type_data - }; - - self.borrow_cpp_type(generic_class_ty_data, |collection, mut cpp_type| { - let method_index = method_spec.method_definition_index; - cpp_type.add_method_generic_inst(method_spec, metadata); - cpp_type.create_method(ty_def, method_index, metadata, collection, config, true); - - cpp_type - }); - - self.all_contexts.get_mut(&context_root_tag) - } - - pub fn fill_generic_class_inst( - &mut self, - method_spec: &Il2CppMethodSpec, - metadata: &mut Metadata, - config: &GenerationConfig, - ) -> Option<&mut CppContext> { - if method_spec.class_inst_index == u32::MAX { - return None; - } - // Skip generic methods? - if method_spec.method_inst_index != u32::MAX { - return None; - } - - let method = - &metadata.metadata.global_metadata.methods[method_spec.method_definition_index]; - - let ty_def = &metadata.metadata.global_metadata.type_definitions[method.declaring_type]; - - // only make generic spatialization - let type_data = CppTypeTag::TypeDefinitionIndex(method.declaring_type); - let tdi = method.declaring_type; - - if metadata.blacklisted_types.contains(&tdi) { - info!( - "Skipping {tdi:?} {} since it is blacklisted", - ty_def.full_name(metadata.metadata, true) - ); - return None; - } - - if ty_def.is_interface() { - // Skip interface - info!( - "Skipping fill class interface for generic instantiation {}", - ty_def.full_name(metadata.metadata, true) - ); - return None; - } - - let context_root_tag = self.get_context_root_tag(type_data); - - let generic_class_ty_data = if method_spec.class_inst_index != u32::MAX { - CppTypeTag::GenericInstantiation(GenericInstantiation { - tdi, - inst: method_spec.class_inst_index as usize, - }) - } else { - type_data - }; - - self.borrow_cpp_type(generic_class_ty_data, |collection, mut cpp_type| { - // cpp_type.make_generics_args(metadata, collection); - collection.fill_cpp_type(&mut cpp_type, metadata, config); - - cpp_type - }); - - self.all_contexts.get_mut(&context_root_tag) - } - - pub fn make_from( - &mut self, - metadata: &Metadata, - config: &GenerationConfig, - type_tag: TypeData, - generic_inst: Option<&Vec>, - ) -> &mut CppContext { - assert!( - !metadata - .child_to_parent_map - .contains_key(&CppType::get_tag_tdi(type_tag)), - "Cannot create context for nested type", - ); - let context_root_tag = self.get_context_root_tag(type_tag.into()); - - if self.filling_types.contains(&context_root_tag) { - panic!("Currently filling type {context_root_tag:?}, cannot fill") - } - - if self.borrowing_types.contains(&context_root_tag) { - panic!("Currently borrowing context {context_root_tag:?}, cannot fill") - } - - // Why is the borrow checker so dumb? - // Using entries causes borrow checker to die :( - if self.all_contexts.contains_key(&context_root_tag) { - return self.all_contexts.get_mut(&context_root_tag).unwrap(); - } - - let tdi = CppType::get_cpp_tag_tdi(context_root_tag); - let context = CppContext::make(metadata, config, tdi, context_root_tag, generic_inst); - // Now do children - for cpp_type in context.typedef_types.values() { - self.alias_nested_types(cpp_type, cpp_type.self_tag, false); - } - self.all_contexts.insert(context_root_tag, context); - self.all_contexts.get_mut(&context_root_tag).unwrap() - } - - /// - /// By default will only look for nested types of the context, ignoring other CppTypes - /// - pub fn get_cpp_type(&self, ty: CppTypeTag) -> Option<&CppType> { - let tag = ty; - let context_root_tag = self.get_context_root_tag(tag); - let parent_root_tag = self.get_parent_or_self_tag(tag); - - self.get_context(context_root_tag) - .and_then(|c| c.get_cpp_type_recursive(parent_root_tag, tag)) - } - - /// - /// By default will only look for nested types of the context, ignoring other CppTypes - /// - pub fn get_cpp_type_mut(&mut self, ty: CppTypeTag) -> Option<&mut CppType> { - let tag = ty; - let context_root_tag = self.get_context_root_tag(tag); - let parent_root_tag = self.get_parent_or_self_tag(tag); - self.get_context_mut(context_root_tag) - .and_then(|c| c.get_cpp_type_recursive_mut(parent_root_tag, tag)) - } - - pub fn borrow_cpp_type(&mut self, ty: CppTypeTag, func: F) - where - F: Fn(&mut Self, CppType) -> CppType, - { - let context_ty = self.get_context_root_tag(ty); - if self.borrowing_types.contains(&context_ty) { - panic!("Already borrowing this context!"); - } - - let declaring_ty = self.get_parent_or_self_tag(ty); - - let (result_cpp_type, old_tag); - - { - let context = self.all_contexts.get_mut(&context_ty).unwrap(); - - // TODO: Needed? - // self.borrowing_types.insert(context_ty); - - // search in root - // clone to avoid failing il2cpp_name - let declaring_cpp_type = context.typedef_types.get(&declaring_ty).cloned(); - (result_cpp_type, old_tag) = match declaring_cpp_type { - Some(old_cpp_type) => { - let old_tag = old_cpp_type.self_tag; - let new_cpp_ty = func(self, old_cpp_type); - - (new_cpp_ty, Some(old_tag)) - } - None => { - let mut declaring_ty = context - .typedef_types - .get(&declaring_ty) - .expect("Parent ty not found in context") - .clone(); - - let found = declaring_ty.borrow_nested_type_mut(ty, self, &func); - - if !found { - panic!("No nested or parent type found for type {ty:?}!"); - } - - (declaring_ty, None) - } - }; - } - - // avoid the borrow checker's wrath - let context = self.all_contexts.get_mut(&context_ty).unwrap(); - if let Some(old_tag) = old_tag { - context.typedef_types.remove(&old_tag); - } - context.insert_cpp_type(result_cpp_type); - self.borrowing_types.remove(&context_ty); - } - - pub fn get_context(&self, type_tag: CppTypeTag) -> Option<&CppContext> { - let context_tag = self.get_context_root_tag(type_tag); - if self.borrowing_types.contains(&context_tag) { - panic!("Borrowing this context! {context_tag:?}"); - } - self.all_contexts.get(&context_tag) - } - pub fn get_context_mut(&mut self, type_tag: CppTypeTag) -> Option<&mut CppContext> { - let context_tag = self.get_context_root_tag(type_tag); - if self.borrowing_types.contains(&context_tag) { - panic!("Borrowing this context! {context_tag:?}"); - } - self.all_contexts - .get_mut(&self.get_context_root_tag(context_tag)) - } - - pub fn new() -> CppContextCollection { - CppContextCollection { - all_contexts: Default::default(), - filled_types: Default::default(), - filling_types: Default::default(), - alias_nested_type_to_parent: Default::default(), - alias_context: Default::default(), - borrowing_types: Default::default(), - } - } - pub fn get(&self) -> &HashMap { - &self.all_contexts - } - pub fn get_mut(&mut self) -> &mut HashMap { - &mut self.all_contexts - } - - pub fn write_all(&self, config: &GenerationConfig) -> color_eyre::Result<()> { - let amount = self.all_contexts.len() as f64; - self.all_contexts - .iter() - .enumerate() - .try_for_each(|(i, (_, c))| { - trace!( - "Writing {:.4}% ({}/{}) {}", - (i as f64 / amount * 100.0), - i, - amount, - c.fundamental_path.display(), - ); - c.write(config) - }) - } - - pub fn write_namespace_headers(&self) -> color_eyre::Result<()> { - self.all_contexts - .iter() - .into_group_map_by(|(_, c)| c.fundamental_path.parent()) - .into_iter() - .try_for_each(|(dir, contexts)| -> color_eyre::Result<()> { - let namespace = if dir.unwrap() == STATIC_CONFIG.header_path { - "GlobalNamespace" - } else { - dir.unwrap().file_name().unwrap().to_str().unwrap() - }; - - let str = contexts - .iter() - // ignore empty contexts - .filter(|(_, c)| !c.typedef_types.is_empty()) - // ignore weird named types - .filter(|(_, c)| { - !c.fundamental_path - .file_name() - .unwrap() - .to_str() - .unwrap() - .starts_with('_') - }) - // add includes - .map(|(_, c)| { - let stripped_path = - diff_paths(&c.fundamental_path, &STATIC_CONFIG.header_path).unwrap(); - - let stripped_path_friendly = if cfg!(windows) { - stripped_path.to_string_lossy().replace('\\', "/") - } else { - stripped_path.to_string_lossy().to_string() - }; - // replace \\ to / on Windows - format!("#include \"{stripped_path_friendly}\"") - }) - .sorted() - .unique() - .join("\n"); - - let path = dir.unwrap().join(namespace).with_extension("hpp"); - - info!( - "Creating namespace glob include {path:?} for {} files", - contexts.len() - ); - - let mut file = File::create(path)?; - - writeln!( - file, - "#ifdef __cpp_modules - module; - #endif - " - )?; - writeln!(file, "#pragma once")?; - file.write_all(str.as_bytes())?; - - writeln!(file)?; - writeln!( - file, - "#ifdef __cpp_modules - export module {namespace}; - #endif - " - )?; - - Ok(()) - })?; - Ok(()) - } -} - -// Get root parent for a reference type, which is System.Object -// for generic sharing -fn get_root_parent<'a>( - metadata: &mut Metadata<'a>, - ty_def: &'a brocolib::global_metadata::Il2CppTypeDefinition, -) -> Option<&'a brocolib::global_metadata::Il2CppTypeDefinition> { - // is reference type - // only make generic spatialization - if ty_def.is_value_type() || ty_def.is_enum_type() { - return Some(ty_def); - } - - let mut parent_index = ty_def.parent_index; - loop { - if parent_index == u32::MAX { - break; - } - - let parent_ty = metadata - .metadata_registration - .types - .get(parent_index as usize) - .unwrap(); - if let TypeData::TypeDefinitionIndex(parent_tdi) = parent_ty.data { - let parent_ty_def = &metadata.metadata.global_metadata.type_definitions[parent_tdi]; - - parent_index = parent_ty_def.parent_index; - } else { - break; - } - } - if parent_index == u32::MAX { - return Some(ty_def); - } - - let parent_ty = metadata - .metadata_registration - .types - .get(parent_index as usize) - .unwrap(); - if let TypeData::TypeDefinitionIndex(parent_tdi) = parent_ty.data { - Some(&metadata.metadata.global_metadata.type_definitions[parent_tdi]) - } else { - Some(ty_def) - } -} diff --git a/src/generate/config.rs b/src/generate/cpp/config.rs similarity index 91% rename from src/generate/config.rs rename to src/generate/cpp/config.rs index 275682e0f..9bf84ce3f 100644 --- a/src/generate/config.rs +++ b/src/generate/cpp/config.rs @@ -1,6 +1,16 @@ -use std::path::PathBuf; +use std::{path::PathBuf, sync::LazyLock}; -pub struct GenerationConfig { +pub static STATIC_CONFIG: LazyLock = LazyLock::new(|| CppGenerationConfig { + header_path: PathBuf::from("./codegen/include"), + source_path: PathBuf::from("./codegen/src"), + dst_internals_path: PathBuf::from("./codegen/include/cordl_internals"), + dst_header_internals_file: PathBuf::from( + "./codegen/include/cordl_internals/cordl_internals.hpp", + ), + use_anonymous_namespace: false, +}); + +pub struct CppGenerationConfig { pub source_path: PathBuf, pub header_path: PathBuf, pub dst_internals_path: PathBuf, @@ -8,7 +18,7 @@ pub struct GenerationConfig { pub use_anonymous_namespace: bool, } -impl GenerationConfig { +impl CppGenerationConfig { pub fn namespace_cpp(&self, string: &str) -> String { let final_ns = if string.is_empty() { "GlobalNamespace".to_owned() diff --git a/src/generate/cpp/cpp_context.rs b/src/generate/cpp/cpp_context.rs new file mode 100644 index 000000000..a81abda23 --- /dev/null +++ b/src/generate/cpp/cpp_context.rs @@ -0,0 +1,546 @@ +use std::cmp::Ordering; +use std::io::Write; +use std::{ + collections::{HashMap, HashSet}, + fs::{create_dir_all, remove_file, File}, + path::{Path, PathBuf}, +}; + +use color_eyre::eyre::ContextCompat; + +use itertools::Itertools; +use log::trace; +use pathdiff::diff_paths; + +use crate::generate::context::TypeContext; +use crate::generate::cpp::config::STATIC_CONFIG; +use crate::generate::cpp::cpp_members::{CppForwardDeclare, CppInclude}; +use crate::generate::cpp::cpp_type::CORDL_NO_INCLUDE_IMPL_DEFINE; + +use crate::generate::cs_type_tag::CsTypeTag; +use crate::generate::metadata::CordlMetadata; +use crate::generate::type_extensions::TypeDefinitionExtensions; +use crate::generate::writer::{CppWritable, CppWriter}; +use crate::helpers::sorting::DependencyGraph; + +use super::config::CppGenerationConfig; +use super::cpp_members::CppUsingAlias; +use super::cpp_name_resolver::{IL2CPP_OBJECT_TYPE, VALUE_WRAPPER_TYPE}; +use super::cpp_type::CppType; + +// Holds the contextual information for creating a C++ file +// Will hold various metadata, such as includes, type definitions, and extraneous writes +#[derive(Debug, Clone)] +pub struct CppContext { + pub typedef_path: PathBuf, + pub type_impl_path: PathBuf, + + // combined header + pub fundamental_path: PathBuf, + + // Types to write, typedef + pub typedef_types: HashMap, + + // Namespace -> alias + pub typealias_types: HashSet<(String, CppUsingAlias)>, +} + +/// `CppContext` provides methods to manage and generate C++ type definitions and implementations +/// based on the provided metadata and configuration. +/// +/// # Methods +/// +/// - `get_cpp_type_recursive_mut`: Retrieves a mutable reference to a C++ type based on the given root tag. +/// - `get_cpp_type_recursive`: Retrieves an immutable reference to a C++ type based on the given root tag. +/// - `get_include_path`: Returns the include path for the C++ type definitions. +/// - `get_types`: Returns an immutable reference to the map of C++ types. +/// - `get_types_mut`: Returns a mutable reference to the map of C++ types. +/// - `make`: Creates a new `CppContext` instance based on the provided context tag, type context, metadata, and configuration. +/// - `insert_cpp_type`: Inserts a new C++ type into the context. +/// - `write`: Writes the C++ type definitions and implementations to the appropriate files. +/// +/// # Example +/// +/// ```rust +/// let cpp_context = CppContext::make(context_tag, &type_context, &metadata, &config); +/// cpp_context.write(&config)?; +/// ``` +/// +/// # Errors +/// +/// The `write` method can return an error if file operations fail, such as creating or removing files and directories. +/// +/// # Internal Functions +/// +/// - `write_il2cpp_arg_macros`: Writes IL2CPP argument macros for the given C++ type. +impl CppContext { + /// Retrieves a mutable reference to a C++ type based on the given root tag. + pub fn get_cpp_type_recursive_mut(&mut self, root_tag: CsTypeTag) -> Option<&mut CppType> { + let ty = self.typedef_types.get_mut(&root_tag); + + ty + } + + /// Retrieves an immutable reference to a C++ type based on the given root tag. + pub fn get_cpp_type_recursive(&self, root_tag: CsTypeTag) -> Option<&CppType> { + let ty = self.typedef_types.get(&root_tag); + + ty + } + + /// Returns the include path for the C++ type definitions. + pub fn get_include_path(&self) -> &PathBuf { + &self.typedef_path + } + + /// Returns an immutable reference to the map of C++ types. + pub fn get_types(&self) -> &HashMap { + &self.typedef_types + } + + /// Returns a mutable reference to the map of C++ types. + pub fn get_types_mut(&mut self) -> &mut HashMap { + &mut self.typedef_types + } + + /// Creates a new `CppContext` instance based on the provided context tag, type context, metadata, and configuration. + pub fn make( + context_tag: CsTypeTag, + context: &TypeContext, + metadata: &CordlMetadata, + config: &CppGenerationConfig, + ) -> CppContext { + let tdi = context_tag.get_tdi(); + let t = &metadata.metadata.global_metadata.type_definitions[tdi]; + + let components = t.get_name_components(metadata.metadata); + + let ns = &components.namespace.unwrap_or_default(); + let name = &components.name; + + let ns_path = config.namespace_path(ns); + let path = if ns_path.is_empty() { + "GlobalNamespace/".to_string() + } else { + ns_path + "/" + }; + let path_name = match t.declaring_type_index != u32::MAX { + true => { + let name = config.path_name(name); + let base_name = components.declaring_types.unwrap_or_default().join("_"); + + format!("{base_name}_{name}") + } + false => config.path_name(name), + }; + + let mut x = CppContext { + typedef_path: config + .header_path + .join(format!("{path}zzzz__{path_name}_def.hpp")), + type_impl_path: config + .header_path + .join(format!("{path}zzzz__{path_name}_impl.hpp")), + fundamental_path: config.header_path.join(format!("{path}{path_name}.hpp")), + typedef_types: Default::default(), + typealias_types: Default::default(), + }; + + for (tag, ty) in &context.typedef_types { + let tdi = tag.get_tdi(); + + let mut cpp_ty = CppType::make_cpp_type(*tag, ty, config); + cpp_ty.nested_fixup(ty, metadata, config); + + if metadata.blacklisted_types.contains(&tdi) { + let result = match t.is_value_type() { + true => format!( + "{VALUE_WRAPPER_TYPE}<{:x}>", + ty.size_info.as_ref().unwrap().instance_size + ), + false => IL2CPP_OBJECT_TYPE.to_string(), + }; + + if !t.is_value_type() { + x.typealias_types.insert(( + cpp_ty.cpp_namespace(), + CppUsingAlias { + alias: cpp_ty.name().to_string(), + result, + template: Default::default(), + }, + )); + continue; + } + } + + x.typedef_types.insert(*tag, cpp_ty); + } + + x + } + + /// Inserts a new C++ type into the context. + pub fn insert_cpp_type(&mut self, cpp_type: CppType) { + self.typedef_types.insert(cpp_type.self_tag, cpp_type); + } + + /// Writes the C++ type definitions and implementations to the appropriate files. + pub fn write(&self, config: &CppGenerationConfig) -> color_eyre::Result<()> { + // Write typedef file first + if self.typedef_path.exists() { + remove_file(self.typedef_path.as_path())?; + } + if !self + .typedef_path + .parent() + .context("parent is not a directory!")? + .is_dir() + { + // Assume it's never a file + create_dir_all(self.typedef_path.parent().context("Failed to create all directories!")?)?; + } + + let base_path = &config.header_path; + + trace!("Writing {:?}", self.typedef_path.as_path()); + let mut typedef_writer = CppWriter { + stream: File::create(self.typedef_path.as_path())?, + indent: 0, + newline: true, + }; + let mut typeimpl_writer = CppWriter { + stream: File::create(self.type_impl_path.as_path())?, + indent: 0, + newline: true, + }; + let mut fundamental_writer = CppWriter { + stream: File::create(self.fundamental_path.as_path())?, + indent: 0, + newline: true, + }; + + writeln!(typedef_writer, "#pragma once")?; + writeln!(typeimpl_writer, "#pragma once")?; + writeln!(fundamental_writer, "#pragma once")?; + + // add IWYU + let typedef_include_path = diff_paths(&self.typedef_path, base_path) + .context("Failed to get typedef include path")?; + let _typeimpl_include_path = diff_paths(&self.type_impl_path, base_path) + .context("Failed to get typeimpl include path")?; + let fundamental_include_path = diff_paths(&self.fundamental_path, base_path) + .context("Failed to get fundamental include path")?; + + let fundamental_include_pragma = format!( + "// IWYU pragma private; include \"{}\"", + fundamental_include_path.display() + ); + writeln!(typedef_writer, "{fundamental_include_pragma}")?; + writeln!(typeimpl_writer, "{fundamental_include_pragma}")?; + writeln!(fundamental_writer, "// IWYU pragma: begin_exports")?; + + // Include cordl config + // this is so confusing but basically gets the relative folder + // navigation for `_config.hpp` + let dest_path = diff_paths( + &STATIC_CONFIG.dst_header_internals_file, + self.typedef_path.parent().unwrap(), + ) + .unwrap(); + + // write typedefs.h include first - this makes include order mostly happy (probably System.Object would still be weird!) + CppInclude::new_exact("beatsaber-hook/shared/utils/typedefs.h") + .write(&mut typedef_writer)?; + CppInclude::new_exact(dest_path).write(&mut typedef_writer)?; + + // after including cordl internals + // macro module init + writeln!(typedef_writer, "CORDL_MODULE_INIT")?; + + // alphabetical sorted + let typedef_types = self + .typedef_types + .values() + .sorted_by(|a, b| a.cpp_name_components.cmp(&b.cpp_name_components)) + // Enums go after stubs + .sorted_by(|a, b| { + if a.is_enum_type == b.is_enum_type { + return Ordering::Equal; + } + + if a.is_enum_type { + Ordering::Less + } else if b.is_enum_type { + Ordering::Greater + } else { + Ordering::Equal + } + }) + // Value types are last + .sorted_by(|a, b| { + let a_strictly_vt = a.is_value_type && !a.is_enum_type; + let b_strictly_vt = b.is_value_type && !b.is_enum_type; + + if a_strictly_vt == b_strictly_vt { + return Ordering::Equal; + } + + if a_strictly_vt { + Ordering::Greater + } else if b_strictly_vt { + Ordering::Less + } else { + Ordering::Equal + } + }) + .collect_vec(); + + let typedef_root_types = typedef_types + .iter() + .filter(|t| self.typedef_types.contains_key(&t.self_tag)) + .collect_vec(); + + let mut ts = DependencyGraph::::new(|a, b| a.cmp(b)); + for cpp_type in &typedef_root_types { + ts.add_root_dependency(&cpp_type.self_tag); + + for dep in cpp_type.requirements.depending_types.iter().sorted() { + ts.add_dependency(&cpp_type.self_tag, dep); + + // add dependency for generic instantiations + // for all types with the same TDI + if let CsTypeTag::TypeDefinitionIndex(tdi) = dep { + // find all generic tags that have the same TDI + let generic_tags_in_context = + typedef_root_types.iter().filter(|t| match t.self_tag { + CsTypeTag::TypeDefinitionIndex(_) => false, + CsTypeTag::GenericInstantiation(gen_inst) => gen_inst.tdi == *tdi, + }); + + generic_tags_in_context.for_each(|generic_dep| { + ts.add_dependency(&cpp_type.self_tag, &generic_dep.self_tag); + }) + } + } + } + + // types that don't depend on anyone + // we take these because they get undeterministically sorted + // and can be first anyways + let mut undepended_cpp_types = vec![]; + + // currently sorted from root to dependencies + // aka least depended to most depended + let mut typedef_root_types_sorted = ts + .topological_sort() + .into_iter() + .filter_map(|t| self.typedef_types.get(t)) + .collect_vec(); + + // add the items with no dependencies at the tail + // when reversed these will be first and can be allowed to be first + typedef_root_types_sorted.append(&mut undepended_cpp_types); + // typedef_root_types_sorted.reverse(); + + // Write includes for typedef + typedef_types + .iter() + .flat_map(|t| &t.requirements.required_def_includes) + .unique() + .sorted() + .try_for_each(|i| i.write(&mut typedef_writer))?; + + // Write includes for typeimpl + typedef_types + .iter() + .flat_map(|t| &t.requirements.required_impl_includes) + .unique() + .sorted() + .try_for_each(|i| i.write(&mut typeimpl_writer))?; + + // add module declarations + writeln!( + typedef_writer, + "CORDL_MODULE_EXPORT({})", + self.fundamental_path.file_stem().unwrap().to_string_lossy() + )?; + + // anonymous namespace + if STATIC_CONFIG.use_anonymous_namespace { + writeln!(typedef_writer, "CORDL_MODULE_EXPORT_STRUCT namespace {{")?; + writeln!(typeimpl_writer, "CORDL_MODULE_EXPORT_STRUCT namespace {{")?; + } + + // write forward declares + // and includes for impl + { + CppInclude::new_exact(&typedef_include_path).write(&mut typeimpl_writer)?; + + let forward_declare_and_includes = || { + typedef_types + .iter() + .flat_map(|t| &t.requirements.forward_declares) + }; + + forward_declare_and_includes() + .map(|(_fd, inc)| inc) + .unique() + .sorted() + // TODO: Check forward declare is not of own type + .try_for_each(|i| -> color_eyre::Result<()> { + i.write(&mut typeimpl_writer)?; + Ok(()) + })?; + + forward_declare_and_includes() + .map(|(fd, _inc)| fd) + .unique() + .sorted_by(|a, b| { + let do_format = |fd: &CppForwardDeclare| { + format!( + "{}_{}_{}_{}", + fd.cpp_namespace.clone().unwrap_or_default(), + fd.cpp_name, + fd.literals.clone().unwrap_or_default().join(","), + fd.templates + .clone() + .unwrap_or_default() + .just_names() + .join(",") + ) + }; + let a_str = do_format(a); + let b_str = do_format(b); + + a_str.cmp(&b_str) + }) + .try_for_each(|fd| fd.write(&mut typedef_writer))?; + + writeln!(typedef_writer, "// Forward declare root types")?; + //Forward declare all types + typedef_root_types + .iter() + .map(|t| CppForwardDeclare::from_cpp_type(t)) + // TODO: Check forward declare is not of own type + .try_for_each(|fd| { + // Forward declare and include + fd.write(&mut typedef_writer) + })?; + + writeln!(typedef_writer, "// Write type traits")?; + typedef_root_types + .iter() + .try_for_each(|cpp_type| -> color_eyre::Result<()> { + if cpp_type.generic_instantiations_args_types.is_none() { + cpp_type.write_type_trait(&mut typedef_writer)?; + } + Ok(()) + })?; + } + + for t in &typedef_root_types_sorted { + t.write_def(&mut typedef_writer)?; + t.write_impl(&mut typeimpl_writer)?; + } + + // end anonymous namespace + if STATIC_CONFIG.use_anonymous_namespace { + writeln!(typedef_writer, "}} // end anonymous namespace")?; + writeln!(typeimpl_writer, "}} // end anonymous namespace")?; + } + + // write macros + typedef_types + .iter() + .try_for_each(|t| Self::write_il2cpp_arg_macros(t, &mut typedef_writer))?; + + // Fundamental + { + CppInclude::new_exact(typedef_include_path).write(&mut fundamental_writer)?; + + // if guard for intellisense + writeln!(fundamental_writer, "#ifndef {CORDL_NO_INCLUDE_IMPL_DEFINE}")?; + CppInclude::new_exact(diff_paths(&self.type_impl_path, base_path).unwrap()) + .write(&mut fundamental_writer)?; + writeln!(fundamental_writer, "#endif")?; + + // end IWYU + writeln!(fundamental_writer, "// IWYU pragma: end_exports")?; + } + + Ok(()) + } + + /// Writes IL2CPP argument macros for the given C++ type. + fn write_il2cpp_arg_macros(ty: &CppType, writer: &mut CppWriter) -> color_eyre::Result<()> { + let is_generic_instantiation = ty.generic_instantiations_args_types.is_some(); + if is_generic_instantiation { + return Ok(()); + } + + let template_container_type = ty + .cpp_template + .as_ref() + .is_some_and(|t| !t.names.is_empty()); + + if !ty.is_value_type && !template_container_type && !is_generic_instantiation { + // reference types need no boxing + writeln!( + writer, + "NEED_NO_BOX({});", + ty.cpp_name_components + .clone() + .remove_generics() + .remove_pointer() + .combine_all() + )?; + } + + let macro_arg_define = { + match //ty.generic_instantiation_args.is_some() || + template_container_type { + true => match ty.is_value_type { + true => "DEFINE_IL2CPP_ARG_TYPE_GENERIC_STRUCT", + false => "DEFINE_IL2CPP_ARG_TYPE_GENERIC_CLASS", + }, + false => "DEFINE_IL2CPP_ARG_TYPE", + } + }; + + // Essentially splits namespace.foo/nested_foo into (namespace, foo/nested_foo) + + let namespace = ty.cs_name_components.namespace.clone().unwrap_or_default(); + let combined_name = match &ty.cs_name_components.declaring_types { + None => ty.cs_name_components.name.clone(), + Some(declaring_types) => format!( + "{}/{}", + declaring_types.join("/"), + ty.cs_name_components.name.clone() + ), + }; + + // generics shouldn't emit with a pointer, while regular types should honor the pointer + let cpp_name = match template_container_type { + true => ty + .cpp_name_components + .clone() + .remove_generics() + .remove_pointer() + .combine_all(), + + false => ty + .cpp_name_components + .clone() + .remove_generics() + .combine_all(), + }; + + writeln!( + writer, + "{macro_arg_define}({cpp_name}, \"{namespace}\", \"{combined_name}\");", + )?; + + Ok(()) + } +} diff --git a/src/generate/cpp/cpp_context_collection.rs b/src/generate/cpp/cpp_context_collection.rs new file mode 100644 index 000000000..8a89637f6 --- /dev/null +++ b/src/generate/cpp/cpp_context_collection.rs @@ -0,0 +1,375 @@ +use std::{ + collections::{HashMap, HashSet}, + fs::File, + io::Write, + path::{Path, PathBuf}, +}; + +use rayon::collections::hash_map::Iter; +use rayon::prelude::*; + +use color_eyre::eyre::bail; +use itertools::Itertools; +use log::{info, trace}; +use pathdiff::diff_paths; +use rayon::iter::{IntoParallelRefIterator, ParallelBridge, ParallelIterator}; + +use crate::generate::{ + cpp::config::STATIC_CONFIG, cs_context_collection::TypeContextCollection, cs_type::CsType, + cs_type_tag::CsTypeTag, metadata::CordlMetadata, +}; + +unsafe impl Send for CsTypeTag {} +unsafe impl Send for CppContext {} + +use super::{ + config::CppGenerationConfig, cpp_context::CppContext, cpp_members::CppInclude, + cpp_name_resolver::CppNameResolver, cpp_type::CppType, +}; + +#[derive(Default)] +pub struct CppContextCollection { + // Should always be a TypeDefinitionIndex + all_contexts: HashMap, + pub alias_context: HashMap, + filled_types: HashSet, + filling_types: HashSet, + borrowing_types: HashSet, +} + +impl CppContextCollection { + pub fn from_cs_collection( + collection: TypeContextCollection, + metadata: &CordlMetadata, + config: &CppGenerationConfig, + ) -> CppContextCollection { + let mut cpp_collection = CppContextCollection::default(); + + info!("Making CppContextCollection from TypeContextCollection"); + for (tag, context) in collection.get() { + cpp_collection + .all_contexts + .insert(*tag, CppContext::make(*tag, context, metadata, config)); + } + cpp_collection.alias_context = collection.alias_context; + + info!("Filling typedefs in CppContextCollection"); + for (_, context) in collection.all_contexts { + for (tag, cs_type) in context.typedef_types { + cpp_collection.fill(tag, cs_type, metadata, config); + } + } + + cpp_collection + } + + fn do_fill_cpp_type( + &mut self, + cpp_type: &mut CppType, + cs_type: CsType, + metadata: &CordlMetadata, + config: &CppGenerationConfig, + ) { + let tag = cpp_type.self_tag; + + if self.filled_types.contains(&tag) { + return; + } + if self.filling_types.contains(&tag) { + panic!("Currently filling type {tag:?}, cannot fill") + } + + // Move ownership to local + self.filling_types.insert(tag); + + let name_resolver = CppNameResolver { + cordl_metadata: metadata, + collection: self, + }; + + cpp_type.fill(cs_type, &name_resolver, config); + + self.filled_types.insert(tag); + self.filling_types.remove(&tag.clone()); + } + + pub fn fill( + &mut self, + type_tag: CsTypeTag, + cs_type: CsType, + metadata: &CordlMetadata, + config: &CppGenerationConfig, + ) { + let context_tag = self.get_context_root_tag(type_tag); + + if self.filled_types.contains(&type_tag) { + return; + } + + if self.borrowing_types.contains(&context_tag) { + panic!("Borrowing context {context_tag:?}"); + } + + // Move ownership to local + let cpp_type_entry = self + .all_contexts + .get_mut(&context_tag) + .expect("No cpp context") + .typedef_types + .remove_entry(&type_tag); + + // In some occasions, the CppContext can be empty + if let Some((_t, mut cpp_type)) = cpp_type_entry { + self.do_fill_cpp_type(&mut cpp_type, cs_type, metadata, config); + + // Move ownership back up + self.all_contexts + .get_mut(&context_tag) + .expect("No cpp context") + .insert_cpp_type(cpp_type); + } + } + + /// + /// By default will only look for nested types of the context, ignoring other CppTypes + /// + pub fn get_cpp_type(&self, ty: CsTypeTag) -> Option<&CppType> { + let context_root_tag = self.get_context_root_tag(ty); + + self.get_context(context_root_tag) + .and_then(|c| c.get_types().get(&ty)) + } + + /// + /// By default will only look for nested types of the context, ignoring other CppTypes + /// + pub fn get_cpp_type_mut(&mut self, ty: CsTypeTag) -> Option<&mut CppType> { + let context_root_tag = self.get_context_root_tag(ty); + + self.get_context_mut(context_root_tag) + .and_then(|c| c.get_types_mut().get_mut(&ty)) + } + + pub fn borrow_cpp_type(&mut self, ty: CsTypeTag, func: F) + where + F: Fn(&mut Self, CppType) -> CppType, + { + let context_ty = self.get_context_root_tag(ty); + if self.borrowing_types.contains(&context_ty) { + panic!("Already borrowing this context!"); + } + + let context = self.all_contexts.get_mut(&context_ty).unwrap(); + + // TODO: Needed? + // self.borrowing_types.insert(context_ty); + + // search in root + // clone to avoid failing il2cpp_name + let Some(declaring_cpp_type) = context.typedef_types.get(&ty).cloned() else { + panic!("No type {context_ty:#?} found!") + }; + let _old_tag = declaring_cpp_type.self_tag; + let new_cpp_ty = func(self, declaring_cpp_type); + + let context = self.all_contexts.get_mut(&context_ty).unwrap(); + + context.insert_cpp_type(new_cpp_ty); + + self.borrowing_types.remove(&context_ty); + } + + pub fn get_context(&self, type_tag: CsTypeTag) -> Option<&CppContext> { + let context_tag = self.get_context_root_tag(type_tag); + if self.borrowing_types.contains(&context_tag) { + panic!("Borrowing this context! {context_tag:?}"); + } + self.all_contexts.get(&context_tag) + } + pub fn get_context_mut(&mut self, type_tag: CsTypeTag) -> Option<&mut CppContext> { + let context_tag = self.get_context_root_tag(type_tag); + if self.borrowing_types.contains(&context_tag) { + panic!("Borrowing this context! {context_tag:?}"); + } + self.all_contexts + .get_mut(&self.get_context_root_tag(context_tag)) + } + + pub fn get_context_root_tag(&self, ty: CsTypeTag) -> CsTypeTag { + self.alias_context + .get(&ty) + .cloned() + // .map(|t| self.get_context_root_tag(*t)) + .unwrap_or(ty) + } + + pub(crate) fn get(&self) -> &HashMap { + &self.all_contexts + } + pub(crate) fn get_mut(&mut self) -> &mut HashMap { + &mut self.all_contexts + } + + pub fn write_all(&self, config: &CppGenerationConfig) -> color_eyre::Result<()> { + let amount = self.all_contexts.len() as f64; + self.all_contexts + .iter() + .enumerate() + .try_for_each(|(i, (_, c))| { + trace!( + "Writing {:.4}% ({}/{}) {}", + (i as f64 / amount * 100.0), + i, + amount, + c.fundamental_path.display(), + ); + c.write(config) + }) + } + + pub fn write_namespace_headers(&self) -> color_eyre::Result<()> { + self.all_contexts + .iter() + .into_group_map_by(|(_, c)| c.fundamental_path.parent()) + .into_iter() + .try_for_each(|(dir, contexts)| -> color_eyre::Result<()> { + let namespace = if dir.unwrap() == STATIC_CONFIG.header_path { + "GlobalNamespace" + } else { + dir.unwrap().file_name().unwrap().to_str().unwrap() + }; + + let str = contexts + .iter() + // ignore empty contexts + .filter(|(_, c)| !c.typedef_types.is_empty()) + // ignore weird named types + .filter(|(_, c)| { + !c.fundamental_path + .file_name() + .unwrap() + .to_str() + .unwrap() + .starts_with('_') + }) + // add includes + .map(|(_, c)| { + let stripped_path = + diff_paths(&c.fundamental_path, &STATIC_CONFIG.header_path).unwrap(); + + let stripped_path_friendly = if cfg!(windows) { + stripped_path.to_string_lossy().replace('\\', "/") + } else { + stripped_path.to_string_lossy().to_string() + }; + // replace \\ to / on Windows + format!("#include \"{stripped_path_friendly}\"") + }) + .sorted() + .unique() + .join("\n"); + + let path = dir.unwrap().join(namespace).with_extension("hpp"); + + info!( + "Creating namespace glob include {path:?} for {} files", + contexts.len() + ); + + let mut file = File::create(path)?; + + writeln!( + file, + "#ifdef __cpp_modules + module; + #endif + " + )?; + writeln!(file, "#pragma once")?; + file.write_all(str.as_bytes())?; + + writeln!(file)?; + writeln!( + file, + "#ifdef __cpp_modules + export module {namespace}; + #endif + " + )?; + + Ok(()) + })?; + Ok(()) + } + + pub(crate) fn cyclic_include_check(&self) -> color_eyre::Result<()> { + info!("Checking for cyclic includes"); + + fn get_headers(context: &CppContext) -> HashSet<&CppInclude> { + let headers: HashSet<&CppInclude> = context + .get_types() + .values() + .flat_map(|t| &t.requirements.required_def_includes) + .collect(); + headers + } + + fn check_includes( + collection: &HashMap, + typedef_path: &Path, + context_headers: HashSet<&CppInclude>, + visited: &HashSet, + ) -> bool { + for header in context_headers { + if visited.contains(&header.include) { + return true; + } + + // find a context from the header + let Some(next_context) = collection + .values() + .find(|next_context| next_context.typedef_path.ends_with(&header.include)) + else { + println!("No context found for include: {:?}", header.include); + continue; + }; + + let mut visited_copy = visited.clone(); + visited_copy.insert(header.include.clone()); + + if check_includes( + collection, + &next_context.typedef_path, + get_headers(next_context), + &visited_copy, + ) { + println!( + "Context {} includes -> {}", + typedef_path.display(), + next_context.typedef_path.display() + ); + return true; + } + + return false; + } + + false + } + + self.all_contexts + .values() + .map(|c| (&c.typedef_path, get_headers(c))) + .try_for_each(|(path, headers)| { + let mut visited = HashSet::new(); + visited.insert(path.clone()); + + if check_includes(&self.all_contexts, path, headers, &visited) { + bail!("Cyclic include detected in context: {:?}", path); + } + Ok(()) + })?; + + Ok(()) + } +} diff --git a/src/generate/cs_fields.rs b/src/generate/cpp/cpp_fields.rs similarity index 64% rename from src/generate/cs_fields.rs rename to src/generate/cpp/cpp_fields.rs index 4a58cf54f..61b0eb8ee 100644 --- a/src/generate/cs_fields.rs +++ b/src/generate/cpp/cpp_fields.rs @@ -1,12 +1,14 @@ -use std::collections::HashMap; +use crate::data::type_resolver::{ResolvedTypeData, TypeUsage}; +use crate::generate::cpp::cpp_type::CORDL_ACCESSOR_FIELD_PREFIX; -use crate::generate::cpp_type::CppType; -use crate::generate::cs_type::CORDL_ACCESSOR_FIELD_PREFIX; -use crate::generate::members::CppLine; -use crate::generate::members::CppNestedUnion; +use crate::generate::cs_members::CsField; +use crate::generate::cs_type_tag::CsTypeTag; +use crate::generate::metadata::CordlMetadata; +use crate::generate::type_extensions::{ + TypeDefinitionExtensions, TypeDefinitionIndexExtensions, TypeExtentions, +}; +use crate::generate::writer::CppWritable; -use brocolib::global_metadata::Il2CppFieldDefinition; -use brocolib::runtime_metadata::Il2CppType; use itertools::Itertools; use log::warn; @@ -16,60 +18,27 @@ use brocolib::runtime_metadata::Il2CppTypeEnum; use brocolib::global_metadata::TypeDefinitionIndex; -use super::context_collection::CppContextCollection; -use super::cpp_type::CORDL_METHOD_HELPER_NAMESPACE; -use super::cpp_type_tag::CppTypeTag; -use super::cs_type::CSType; -use super::members::CppFieldDecl; -use super::members::CppFieldImpl; -use super::members::CppInclude; -use super::members::CppMember; -use super::members::CppMethodDecl; -use super::members::CppMethodImpl; -use super::members::CppNestedStruct; -use super::members::CppNonMember; -use super::members::CppParam; -use super::members::CppPropertyDecl; -use super::members::CppStaticAssert; -use super::members::CppTemplate; -use super::metadata::Metadata; -use super::type_extensions::Il2CppTypeEnumExtensions; -use super::type_extensions::TypeDefinitionExtensions; -use super::type_extensions::TypeExtentions; -use super::writer::Writable; - -#[derive(Clone, Debug)] -pub struct FieldInfo<'a> { - pub cpp_field: CppFieldDecl, - pub field: &'a Il2CppFieldDefinition, - pub field_type: &'a Il2CppType, - pub is_constant: bool, - pub is_static: bool, - pub is_pointer: bool, - - pub offset: Option, - pub size: usize, -} - -pub struct FieldInfoSet<'a> { - fields: Vec>>, - size: u32, - offset: u32, -} - -impl<'a> FieldInfoSet<'a> { - fn max(&self) -> u32 { - self.size + self.offset - } -} +use super::config::CppGenerationConfig; +use super::cpp_members::{ + CppFieldDecl, CppFieldImpl, CppInclude, CppNestedStruct, CppNestedUnion, CppNonMember, + CppStaticAssert, CppTemplate, +}; +use super::cpp_members::{ + CppLine, CppMember, CppMethodDecl, CppMethodImpl, CppParam, CppPropertyDecl, +}; +use super::cpp_name_resolver::CppNameResolver; +use super::cpp_type::{CppType, CORDL_METHOD_HELPER_NAMESPACE}; pub fn handle_static_fields( cpp_type: &mut CppType, - fields: &[FieldInfo], - metadata: &Metadata, - tdi: TypeDefinitionIndex, + fields: &[CsField], + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, ) { - let t = CppType::get_type_definition(metadata, tdi); + let metadata = name_resolver.cordl_metadata; + + let tdi = cpp_type.self_tag.get_tdi(); + let t = tdi.get_type_definition(metadata.metadata); // if no fields, skip if t.field_count == 0 { @@ -78,17 +47,20 @@ pub fn handle_static_fields( // we want only static fields // we ignore constants - for field_info in fields.iter().filter(|f| f.is_static && !f.is_constant) { - let f_type = field_info.field_type; - let f_name = field_info.field.name(metadata.metadata); + for field_info in fields.iter().filter(|f| !f.instance && !f.is_const) { + let _f_type = &field_info.field_ty; + let f_name = &field_info.name; let f_offset = field_info.offset.unwrap_or(u32::MAX); let f_size = field_info.size; - let field_ty_cpp_name = &field_info.cpp_field.field_ty; + + let f_cpp_decl = make_cpp_field_decl(cpp_type, field_info, name_resolver, config); + + let field_ty_cpp_name = &f_cpp_decl.field_ty; // non const field // instance field access on ref types is special // ref type instance fields are specially named because the field getters are supposed to be used - let f_cpp_name = field_info.cpp_field.cpp_name.clone(); + let f_cpp_name = f_cpp_decl.cpp_name.clone(); let klass_resolver = cpp_type.classof_cpp_name(); @@ -123,7 +95,7 @@ pub fn handle_static_fields( body: None, // TODO: // Const if instance for now is_const: false, - is_constexpr: !f_type.is_static() || f_type.is_constant(), + is_constexpr: field_info.instance || field_info.is_const, is_inline: true, is_virtual: false, is_implicit_operator: false, @@ -143,7 +115,7 @@ pub fn handle_static_fields( brief: None, body: None, //TODO: is_const: false, // TODO: readonly fields? - is_constexpr: !f_type.is_static() || f_type.is_constant(), + is_constexpr: field_info.instance || field_info.is_const, is_inline: true, is_virtual: false, is_implicit_operator: false, @@ -181,7 +153,7 @@ pub fn handle_static_fields( let prop_decl = CppPropertyDecl { cpp_name: f_cpp_name, prop_ty: field_ty_cpp_name.clone(), - instance: !f_type.is_static() && !f_type.is_constant(), + instance: field_info.instance, getter: getter_decl.cpp_name.clone().into(), setter: setter_decl.cpp_name.clone().into(), indexable: false, @@ -217,15 +189,14 @@ pub fn handle_static_fields( pub(crate) fn handle_const_fields( cpp_type: &mut CppType, - fields: &[FieldInfo], - ctx_collection: &CppContextCollection, - metadata: &Metadata, - tdi: TypeDefinitionIndex, + fields: &[CsField], + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, ) { - let t = CppType::get_type_definition(metadata, tdi); + let metadata = name_resolver.cordl_metadata; // if no fields, skip - if t.field_count == 0 { + if fields.is_empty() { return; } @@ -239,33 +210,53 @@ pub(crate) fn handle_const_fields( None }; - for field_info in fields.iter().filter(|f| f.is_constant) { - let f_type = field_info.field_type; - let f_name = field_info.field.name(metadata.metadata); + for field_info in fields.iter().filter(|f| f.is_const) { + let cpp_field_template = make_cpp_field_decl(cpp_type, field_info, name_resolver, config); + let f_resolved_type = &field_info.field_ty; + let f_type = field_info.field_ty.get_type(metadata); + let f_name = &field_info.name; let f_offset = field_info.offset.unwrap_or(u32::MAX); let f_size = field_info.size; - let def_value = field_info.cpp_field.value.as_ref(); + let def_value = field_info.value.as_ref(); let def_value = def_value.expect("Constant with no default value?"); - match f_type.ty.is_primitive_builtin() { - false => { + match f_resolved_type.data { + ResolvedTypeData::Primitive(_) => { + // primitive type + let field_decl = CppFieldDecl { + instance: false, + const_expr: true, + readonly: field_info.readonly, + + brief_comment: Some(format!( + "Field {f_name} offset 0x{f_offset:x} size 0x{f_size:x}" + )), + value: Some(def_value.to_string()), + ..cpp_field_template + }; + + cpp_type + .declarations + .push(CppMember::FieldDecl(field_decl).into()); + } + _ => { // other type let field_decl = CppFieldDecl { instance: false, - readonly: f_type.is_constant(), + readonly: field_info.readonly, value: None, const_expr: false, - brief_comment: Some(format!("Field {f_name} value: {def_value}")), - ..field_info.cpp_field.clone() + brief_comment: Some(format!("Field {f_name} value: {def_value:?}")), + ..cpp_field_template.clone() }; let field_impl = CppFieldImpl { - value: def_value.clone(), + value: def_value.to_string(), const_expr: true, declaring_type: cpp_type.cpp_name_components.remove_pointer().combine_all(), declaring_type_template: declaring_cpp_template.clone(), - ..field_decl.clone().into() + ..cpp_field_template.clone().into() }; // get enum type to include impl @@ -273,13 +264,14 @@ pub(crate) fn handle_const_fields( // in the declaration // TODO: Make enum ctors inline defined if f_type.valuetype && f_type.ty == Il2CppTypeEnum::Valuetype { - let field_cpp_tag: CppTypeTag = - CppTypeTag::from_type_data(f_type.data, metadata.metadata); - let field_cpp_td_tag: CppTypeTag = field_cpp_tag.get_tdi().into(); - let field_cpp_type = ctx_collection.get_cpp_type(field_cpp_td_tag); + let field_cpp_tag: CsTypeTag = + CsTypeTag::from_type_data(f_type.data, metadata.metadata); + let field_cpp_td_tag: CsTypeTag = field_cpp_tag.get_tdi().into(); + let field_cpp_type = name_resolver.collection.get_cpp_type(field_cpp_td_tag); if field_cpp_type.is_some_and(|f| f.is_enum_type) { - let field_cpp_context = ctx_collection + let field_cpp_context = name_resolver + .collection .get_context(field_cpp_td_tag) .expect("No context for cpp enum type"); @@ -297,35 +289,17 @@ pub(crate) fn handle_const_fields( .implementations .push(CppMember::FieldImpl(field_impl).into()); } - true => { - // primitive type - let field_decl = CppFieldDecl { - instance: false, - const_expr: true, - readonly: f_type.is_constant(), - - brief_comment: Some(format!( - "Field {f_name} offset 0x{f_offset:x} size 0x{f_size:x}" - )), - value: Some(def_value.clone()), - ..field_info.cpp_field.clone() - }; - - cpp_type - .declarations - .push(CppMember::FieldDecl(field_decl).into()); - } } } } pub(crate) fn handle_instance_fields( cpp_type: &mut CppType, - fields: &[FieldInfo], - metadata: &Metadata, + fields: &[CppFieldDecl], + metadata: &CordlMetadata, tdi: TypeDefinitionIndex, ) { - let t = CppType::get_type_definition(metadata, tdi); + let t = tdi.get_type_definition(metadata.metadata); // if no fields, skip if t.field_count == 0 { @@ -334,7 +308,7 @@ pub(crate) fn handle_instance_fields( let instance_field_decls = fields .iter() - .filter(|f| f.offset.is_some() && !f.is_static && !f.is_constant) + .filter(|f| f.offset.is_some() && f.instance) .cloned() .collect_vec(); @@ -348,7 +322,7 @@ pub(crate) fn handle_instance_fields( let resulting_fields = instance_field_decls .into_iter() .map(|d| { - let mut f = d.cpp_field; + let mut f = d; if property_exists(&f.cpp_name) { f.cpp_name = format!("_cordl_{}", &f.cpp_name); @@ -356,14 +330,14 @@ pub(crate) fn handle_instance_fields( f.is_private = true; } - FieldInfo { cpp_field: f, ..d } + f }) .collect_vec(); // explicit layout types are packed into single unions if t.is_explicit_layout() { // oh no! the fields are unionizing! don't tell elon musk! - let u = pack_fields_into_single_union(resulting_fields); + let u = pack_fields_into_single_union(&resulting_fields); cpp_type.declarations.push(CppMember::NestedUnion(u).into()); } else { // TODO: Make field offset asserts for explicit layouts! @@ -371,12 +345,12 @@ pub(crate) fn handle_instance_fields( resulting_fields .into_iter() - .map(|member| CppMember::FieldDecl(member.cpp_field)) + .map(|member| CppMember::FieldDecl(member)) .for_each(|member| cpp_type.declarations.push(member.into())); }; } -fn add_field_offset_asserts(cpp_type: &mut CppType, fields: &[FieldInfo]) { +fn add_field_offset_asserts(cpp_type: &mut CppType, fields: &[CppFieldDecl]) { // let cpp_name = if let Some(cpp_template) = &cpp_type.cpp_template { // // We don't handle generic instantiations since we can't tell if a ge // let mut name_components = cpp_type.cpp_name_components.clone(); @@ -405,7 +379,7 @@ fn add_field_offset_asserts(cpp_type: &mut CppType, fields: &[FieldInfo]) { let cpp_name = cpp_type.cpp_name_components.remove_pointer().combine_all(); for field in fields { - let field_name = &field.cpp_field.cpp_name; + let field_name = &field.cpp_name; let offset = field.offset.unwrap_or(u32::MAX); let assert = CppStaticAssert { @@ -428,12 +402,14 @@ pub(crate) fn fixup_backing_field(fieldname: &str) -> String { pub(crate) fn handle_valuetype_fields( cpp_type: &mut CppType, - fields: &[FieldInfo], - metadata: &Metadata, - tdi: TypeDefinitionIndex, + fields: &[CsField], + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, ) { + let metadata = name_resolver.cordl_metadata; + let tdi = cpp_type.self_tag.get_tdi(); // Value types only need getter fixes for explicit layout types - let t = CppType::get_type_definition(metadata, tdi); + let t = tdi.get_type_definition(metadata.metadata); // if no fields, skip if t.field_count == 0 { @@ -442,22 +418,18 @@ pub(crate) fn handle_valuetype_fields( // instance fields for explicit layout value types are special if t.is_explicit_layout() { - for field_info in fields.iter().filter(|f| !f.is_constant && !f.is_static) { + for field_info in fields.iter().filter(|f| f.instance) { // don't get a template that has no names let template = cpp_type .cpp_template .clone() - .and_then(|t| match t.names.is_empty() { - true => None, - false => Some(t), - }); + .filter(|t| !t.names.is_empty()); - let declaring_cpp_full_name = - cpp_type.cpp_name_components.remove_pointer().combine_all(); + let cpp_field_decl = make_cpp_field_decl(cpp_type, field_info, name_resolver, config); - let prop = prop_decl_from_fieldinfo(metadata, field_info); + let prop = prop_decl_from_fieldinfo(metadata, field_info, &cpp_field_decl); let (accessor_decls, accessor_impls) = - prop_methods_from_fieldinfo(field_info, template, declaring_cpp_full_name, false); + prop_methods_from_fieldinfo(field_info, template, cpp_type, name_resolver, config); cpp_type.declarations.push(CppMember::Property(prop).into()); @@ -476,41 +448,46 @@ pub(crate) fn handle_valuetype_fields( let backing_fields = fields .iter() - .cloned() + .map(|f| make_cpp_field_decl(cpp_type, &f, name_resolver, config)) .map(|mut f| { - f.cpp_field.cpp_name = fixup_backing_field(&f.cpp_field.cpp_name); + f.cpp_name = fixup_backing_field(&f.cpp_name); f }) .collect_vec(); handle_instance_fields(cpp_type, &backing_fields, metadata, tdi); } else { - handle_instance_fields(cpp_type, fields, metadata, tdi); + let backing_fields = fields + .iter() + .map(|f| make_cpp_field_decl(cpp_type, &f, name_resolver, config)) + .collect_vec(); + + handle_instance_fields(cpp_type, &backing_fields, metadata, tdi); } } // create prop and field declaration from passed field info pub(crate) fn prop_decl_from_fieldinfo( - metadata: &Metadata, - field_info: &FieldInfo, + _metadata: &CordlMetadata, + cs_field: &CsField, + cpp_field: &CppFieldDecl, ) -> CppPropertyDecl { - if field_info.is_static { + if !cs_field.instance { panic!("Can't turn static fields into declspec properties!"); } - let f_name = field_info.field.name(metadata.metadata); - let f_offset = field_info.offset.unwrap_or(u32::MAX); - let f_size = field_info.size; - let field_ty_cpp_name = &field_info.cpp_field.field_ty; - - let f_cpp_name = &field_info.cpp_field.cpp_name; + let f_name = &cs_field.name; + let f_cpp_name = &cpp_field.cpp_name; + let f_offset = cs_field.offset.unwrap_or(u32::MAX); + let f_size = cs_field.size; + let _field_ty_cpp_name = &cs_field.field_ty; let (getter_name, setter_name) = method_names_from_fieldinfo(f_cpp_name); CppPropertyDecl { cpp_name: f_cpp_name.clone(), - prop_ty: field_ty_cpp_name.clone(), - instance: !field_info.is_static, + prop_ty: cpp_field.field_ty.clone(), + instance: cs_field.instance, getter: Some(getter_name), setter: Some(setter_name), indexable: false, @@ -528,37 +505,42 @@ fn method_names_from_fieldinfo(f_cpp_name: &str) -> (String, String) { } pub(crate) fn prop_methods_from_fieldinfo( - field_info: &FieldInfo, - template: Option, - declaring_cpp_name: String, - declaring_is_ref: bool, + field: &CsField, + field_template: Option, + cpp_type: &mut CppType, + + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, ) -> (Vec, Vec) { - let f_type = field_info.field_type; - let field_ty_cpp_name = &field_info.cpp_field.field_ty; + let metadata = name_resolver.cordl_metadata; + let f_type = field.field_ty.get_type(metadata); + + let cpp_field = make_cpp_field_decl(cpp_type, field, name_resolver, config); + let field_ty_cpp_name = &cpp_field.field_ty; + let f_cpp_name = &cpp_field.cpp_name; - let f_cpp_name = &field_info.cpp_field.cpp_name; let cordl_field_name = fixup_backing_field(f_cpp_name); let field_access = format!("this->{cordl_field_name}"); let (getter_name, setter_name) = method_names_from_fieldinfo(f_cpp_name); - let (get_return_type, const_get_return_type) = match field_info.is_pointer { - // Var types are default pointers - true => ( - field_ty_cpp_name.clone(), - format!("::cordl_internals::to_const_pointer<{field_ty_cpp_name}> const",), - ), - false => ( - field_ty_cpp_name.clone(), - format!("{field_ty_cpp_name} const"), - ), - }; + // let (get_return_type, const_get_return_type) = match !f_type.valuetype { + // // Var types are default pointers + // true => ( + // field_ty_cpp_name.clone(), + // format!("::cordl_internals::to_const_pointer<{field_ty_cpp_name}> const",), + // ), + // false => ( + // field_ty_cpp_name.clone(), + // format!("{field_ty_cpp_name} const"), + // ), + // }; // field accessors emit as ref because they are fields, you should be able to access them the same - let (get_return_type, const_get_return_type) = ( - format!("{get_return_type}&"), - format!("{const_get_return_type}&"), - ); + let get_return_type = format!("{field_ty_cpp_name}&"); + let const_get_return_type = format!("{field_ty_cpp_name} const&"); + + let declaring_is_ref = cpp_type.is_reference_type; // for ref types we emit an instance null check that is dependent on a compile time define, // that way we can prevent nullptr access and instead throw, if the user wants this @@ -573,7 +555,7 @@ pub(crate) fn prop_methods_from_fieldinfo( // if the declaring type is a value type, we should not use wbarrier let setter_call = match !f_type.valuetype && declaring_is_ref { // setter for generic type - true if template.as_ref().is_some_and(|s| !s.names.is_empty()) => { + true if field_template.as_ref().is_some_and(|s| !s.names.is_empty()) => { format!( "::cordl_internals::setInstanceField(this, &{field_access}, {setter_var_name});" ) @@ -658,25 +640,28 @@ pub(crate) fn prop_methods_from_fieldinfo( }; // construct getter and setter bodies - let getter_body: Vec> = if let Some(instance_null_check) = instance_null_check - { - vec![ - Arc::new(CppLine::make(instance_null_check.into())), - Arc::new(CppLine::make(getter_call)), - ] - } else { - vec![Arc::new(CppLine::make(getter_call))] - }; + let getter_body: Vec> = + if let Some(instance_null_check) = instance_null_check { + vec![ + Arc::new(CppLine::make(instance_null_check.into())), + Arc::new(CppLine::make(getter_call)), + ] + } else { + vec![Arc::new(CppLine::make(getter_call))] + }; - let setter_body: Vec> = if let Some(instance_null_check) = instance_null_check - { - vec![ - Arc::new(CppLine::make(instance_null_check.into())), - Arc::new(CppLine::make(setter_call)), - ] - } else { - vec![Arc::new(CppLine::make(setter_call))] - }; + let setter_body: Vec> = + if let Some(instance_null_check) = instance_null_check { + vec![ + Arc::new(CppLine::make(instance_null_check.into())), + Arc::new(CppLine::make(setter_call)), + ] + } else { + vec![Arc::new(CppLine::make(setter_call))] + }; + + let declaring_cpp_name = cpp_type.cpp_name_components.remove_pointer().combine_all(); + let template = cpp_type.cpp_template.clone(); let getter_impl = CppMethodImpl { body: getter_body.clone(), @@ -710,11 +695,13 @@ pub(crate) fn prop_methods_from_fieldinfo( pub(crate) fn handle_referencetype_fields( cpp_type: &mut CppType, - fields: &[FieldInfo], - metadata: &Metadata, - tdi: TypeDefinitionIndex, + fields: &[CsField], + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, ) { - let t = CppType::get_type_definition(metadata, tdi); + let metadata = name_resolver.cordl_metadata; + let tdi = cpp_type.self_tag.get_tdi(); + let t = tdi.get_type_definition(metadata.metadata); if t.is_explicit_layout() { warn!( @@ -728,7 +715,7 @@ pub(crate) fn handle_referencetype_fields( return; } - for field_info in fields.iter().filter(|f| !f.is_constant && !f.is_static) { + for field_info in fields.iter().filter(|f| f.instance) { // don't get a template that has no names let template = cpp_type .cpp_template @@ -738,11 +725,13 @@ pub(crate) fn handle_referencetype_fields( false => Some(t), }); - let declaring_cpp_full_name = cpp_type.cpp_name_components.remove_pointer().combine_all(); + let _declaring_cpp_full_name = cpp_type.cpp_name_components.remove_pointer().combine_all(); + + let cpp_field_decl = make_cpp_field_decl(cpp_type, field_info, name_resolver, config); - let prop = prop_decl_from_fieldinfo(metadata, field_info); + let prop = prop_decl_from_fieldinfo(metadata, field_info, &cpp_field_decl); let (accessor_decls, accessor_impls) = - prop_methods_from_fieldinfo(field_info, template, declaring_cpp_full_name, true); + prop_methods_from_fieldinfo(field_info, template, cpp_type, name_resolver, config); cpp_type.declarations.push(CppMember::Property(prop).into()); @@ -761,9 +750,9 @@ pub(crate) fn handle_referencetype_fields( let backing_fields = fields .iter() - .cloned() + .map(|f| make_cpp_field_decl(cpp_type, &f, name_resolver, config)) .map(|mut f| { - f.cpp_field.cpp_name = fixup_backing_field(&f.cpp_field.cpp_name); + f.cpp_name = fixup_backing_field(&f.cpp_name); f }) .collect_vec(); @@ -771,7 +760,7 @@ pub(crate) fn handle_referencetype_fields( handle_instance_fields(cpp_type, &backing_fields, metadata, tdi); } -pub(crate) fn field_collision_check(instance_fields: &[FieldInfo]) -> bool { +pub(crate) fn field_collision_check(instance_fields: &[CsField]) -> bool { let mut next_offset = 0; return instance_fields .iter() @@ -788,12 +777,13 @@ pub(crate) fn field_collision_check(instance_fields: &[FieldInfo]) -> bool { } // inspired by what il2cpp does for explicitly laid out types -pub(crate) fn pack_fields_into_single_union(fields: Vec) -> CppNestedUnion { +pub(crate) fn pack_fields_into_single_union(fields: &[CppFieldDecl]) -> CppNestedUnion { // get the min offset to use as a base for the packed structs let min_offset = fields.iter().map(|f| f.offset.unwrap()).min().unwrap_or(0); let packed_structs = fields .into_iter() + .cloned() .map(|field| { let structs = field_into_offset_structs(min_offset, field); @@ -817,7 +807,7 @@ pub(crate) fn pack_fields_into_single_union(fields: Vec) -> CppNested pub(crate) fn field_into_offset_structs( _min_offset: u32, - field: FieldInfo, + field: CppFieldDecl, ) -> (CppNestedStruct, CppNestedStruct) { // il2cpp basically turns each field into 2 structs within a union: // 1 which is packed with size 1, and padded with offset to fit to the end @@ -829,19 +819,17 @@ pub(crate) fn field_into_offset_structs( let padding = actual_offset; - let packed_padding_cpp_name = format!("{}_padding[0x{padding:x}]", field.cpp_field.cpp_name); - let alignment_padding_cpp_name = format!( - "{}_padding_forAlignment[0x{padding:x}]", - field.cpp_field.cpp_name - ); - let alignment_cpp_name = format!("{}_forAlignment", field.cpp_field.cpp_name); + let packed_padding_cpp_name = format!("{}_padding[0x{padding:x}]", field.cpp_name); + let alignment_padding_cpp_name = + format!("{}_padding_forAlignment[0x{padding:x}]", field.cpp_name); + let alignment_cpp_name = format!("{}_forAlignment", field.cpp_name); let packed_padding_field = CppFieldDecl { brief_comment: Some(format!("Padding field 0x{padding:x}")), const_expr: false, cpp_name: packed_padding_cpp_name, field_ty: "uint8_t".into(), - offset: *actual_offset, + offset: Some(*actual_offset), instance: true, is_private: false, readonly: false, @@ -853,7 +841,7 @@ pub(crate) fn field_into_offset_structs( const_expr: false, cpp_name: alignment_padding_cpp_name, field_ty: "uint8_t".into(), - offset: *actual_offset, + offset: Some(*actual_offset), instance: true, is_private: false, readonly: false, @@ -863,12 +851,12 @@ pub(crate) fn field_into_offset_structs( let alignment_field = CppFieldDecl { cpp_name: alignment_cpp_name, is_private: false, - ..field.cpp_field.clone() + ..field.clone() }; let packed_field = CppFieldDecl { is_private: false, - ..field.cpp_field + ..field }; let packed_struct = CppNestedStruct { @@ -902,125 +890,34 @@ pub(crate) fn field_into_offset_structs( (packed_struct, alignment_struct) } -/// generates the fields for the value type or reference type\ -/// handles unions -pub(crate) fn make_or_unionize_fields(instance_fields: &[FieldInfo]) -> Vec { - // make all fields like usual - if !field_collision_check(instance_fields) { - return instance_fields - .iter() - .map(|d| CppMember::FieldDecl(d.cpp_field.clone())) - .collect_vec(); - } - // we have a collision, investigate and handle - - let mut offset_map = HashMap::new(); - - fn accumulated_size(fields: &[FieldInfo]) -> u32 { - fields.iter().map(|f| f.size as u32).sum() - } - - let mut current_max: u32 = 0; - let mut current_offset: u32 = 0; - - // TODO: Field padding for exact offsets (explicit layouts?) - - // you can't sort instance fields on offset/size because it will throw off the unionization process - instance_fields - .iter() - .sorted_by(|a, b| a.size.cmp(&b.size)) - .rev() - .sorted_by(|a, b| a.offset.cmp(&b.offset)) - .for_each(|field| { - let offset = field.offset.unwrap_or(u32::MAX); - let size = field.size as u32; - let max = offset + size; - - if max > current_max { - current_offset = offset; - current_max = max; - } - - let current_set = offset_map - .entry(current_offset) - .or_insert_with(|| FieldInfoSet { - fields: vec![], - offset: current_offset, - size, - }); - - if current_max > current_set.max() { - current_set.size = size - } - - // if we have a last vector & the size of its fields + current_offset is smaller than current max add to that list - if let Some(last) = current_set.fields.last_mut() - && current_offset + accumulated_size(last) == offset - { - last.push(field.clone()); - } else { - current_set.fields.push(vec![field.clone()]); - } - }); +pub fn make_cpp_field_decl( + cpp_type: &mut CppType, + field: &CsField, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, +) -> CppFieldDecl { + let field_ty = field.field_ty.get_type(name_resolver.cordl_metadata); + let field_resolved_ty = name_resolver + .resolve_name( + cpp_type, + &field.field_ty, + TypeUsage::Field, + field_ty.valuetype + || field_ty.ty == Il2CppTypeEnum::Valuetype + || field_ty.ty == Il2CppTypeEnum::Enum, + ) + .combine_all(); + let field_decl = CppFieldDecl { + cpp_name: config.name_cpp_plus(&field.name, &[cpp_type.cpp_name().as_str()]), + field_ty: field_resolved_ty, + offset: field.offset, + instance: field.instance, + readonly: field.readonly, + const_expr: field.is_const, + value: field.value.as_ref().map(|v| v.to_string()), + brief_comment: field.brief_comment.clone(), + is_private: false, + }; - offset_map - .into_values() - .map(|field_set| { - // if we only have one list, just emit it as a set of fields - if field_set.fields.len() == 1 { - return field_set - .fields - .into_iter() - .flat_map(|v| v.into_iter()) - .map(|d| CppMember::FieldDecl(d.cpp_field)) - .collect_vec(); - } - // we had more than 1 list, so we have unions to emit - let declarations = field_set - .fields - .into_iter() - .map(|struct_contents| { - if struct_contents.len() == 1 { - // emit a struct with only 1 field as just a field - return struct_contents - .into_iter() - .map(|d| CppMember::FieldDecl(d.cpp_field)) - .collect_vec(); - } - vec![ - // if we have more than 1 field, emit a nested struct - CppMember::NestedStruct(CppNestedStruct { - base_type: None, - declaring_name: "".to_string(), - is_enum: false, - is_class: false, - is_private: false, - declarations: struct_contents - .into_iter() - .map(|d| CppMember::FieldDecl(d.cpp_field).into()) - .collect_vec(), - brief_comment: Some(format!( - "Anonymous struct offset 0x{:x}, size 0x{:x}", - field_set.offset, field_set.size - )), - packing: None, - }), - ] - }) - .flat_map(|v| v.into_iter()) - .collect_vec(); - - // wrap our set into a union - vec![CppMember::NestedUnion(CppNestedUnion { - brief_comment: Some(format!( - "Anonymous union offset 0x{:x}, size 0x{:x}", - field_set.offset, field_set.size - )), - declarations: declarations.into_iter().map(|d| d.into()).collect_vec(), - offset: field_set.offset, - is_private: false, - })] - }) - .flat_map(|v| v.into_iter()) - .collect_vec() + field_decl } diff --git a/src/generate/cpp/cpp_main.rs b/src/generate/cpp/cpp_main.rs new file mode 100644 index 000000000..411532b76 --- /dev/null +++ b/src/generate/cpp/cpp_main.rs @@ -0,0 +1,435 @@ +use std::process::Command; + +use brocolib::{global_metadata::TypeDefinitionIndex, runtime_metadata::TypeData}; +use color_eyre::{eyre::Result, Section}; +use filesize::PathExt; +use itertools::Itertools; +use log::{error, info, warn}; +use rayon::iter::{ParallelBridge, ParallelIterator}; +use walkdir::DirEntry; + +use crate::{ + generate::{ + cpp::{ + config::STATIC_CONFIG, + cpp_context_collection::{self, CppContextCollection}, + cpp_members::CppMember, + handlers::{object, unity, value_type}, + }, + cs_context_collection::TypeContextCollection, + metadata::CordlMetadata, + }, + INTERNALS_DIR, +}; + +pub fn run_cpp( + cs_collection: TypeContextCollection, + metadata: &CordlMetadata, +) -> color_eyre::Result<()> { + let mut cpp_context_collection = + CppContextCollection::from_cs_collection(cs_collection, metadata, &STATIC_CONFIG); + + info!("Registering handlers!"); + // il2cpp_internals::register_il2cpp_types(&mut metadata)?; + unity::register_unity(metadata, &mut cpp_context_collection)?; + object::register_system(metadata, &mut cpp_context_collection)?; + value_type::register_value_type(metadata, &mut cpp_context_collection)?; + + // let e = cpp_context_collection.cyclic_include_check()?; + + if STATIC_CONFIG.header_path.exists() { + std::fs::remove_dir_all(&STATIC_CONFIG.header_path)?; + } + std::fs::create_dir_all(&STATIC_CONFIG.header_path)?; + + info!( + "Copying config to codegen folder {:?}", + STATIC_CONFIG.dst_internals_path + ); + + std::fs::create_dir_all(&STATIC_CONFIG.dst_internals_path)?; + + // extract contents of the cordl internals folder into destination + INTERNALS_DIR.extract(&STATIC_CONFIG.dst_internals_path)?; + + const write_all: bool = true; + if write_all { + info!("Writing all"); + cpp_context_collection.write_all(&STATIC_CONFIG)?; + cpp_context_collection.write_namespace_headers()?; + } else { + // for t in &metadata.type_definitions { + // // Handle the generation for a single type + // let dest = open_writer(&metadata, &config, &t); + // write_type(&metadata, &config, &t, &dest); + // } + fn make_td_tdi(idx: u32) -> TypeData { + TypeData::TypeDefinitionIndex(TypeDefinitionIndex::new(idx)) + } + // All indices require updating + // cpp_context_collection.get()[&make_td_tdi(123)].write()?; + // cpp_context_collection.get()[&make_td_tdi(342)].write()?; + // cpp_context_collection.get()[&make_td_tdi(512)].write()?; + // cpp_context_collection.get()[&make_td_tdi(1024)].write()?; + // cpp_context_collection.get()[&make_td_tdi(600)].write()?; + // cpp_context_collection.get()[&make_td_tdi(1000)].write()?; + // cpp_context_collection.get()[&make_td_tdi(420)].write()?; + // cpp_context_collection.get()[&make_td_tdi(69)].write()?; + // cpp_context_collection.get()[&make_td_tdi(531)].write()?; + // cpp_context_collection.get()[&make_td_tdi(532)].write()?; + // cpp_context_collection.get()[&make_td_tdi(533)].write()?; + // cpp_context_collection.get()[&make_td_tdi(534)].write()?; + // cpp_context_collection.get()[&make_td_tdi(535)].write()?; + // cpp_context_collection.get()[&make_td_tdi(1455)].write()?; + info!("Generic type"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| c.get_types().iter().any(|(_, t)| t.cpp_template.is_some())) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("List Generic type"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types().iter().any(|(_, t)| { + t.cpp_name_components.generics.is_some() && t.cpp_name() == "List_1" + }) + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("Value type"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types().iter().any(|(_, t)| { + t.is_value_type && t.name() == "Color" && t.namespace() == "UnityEngine" + }) + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + // info!("Nested type"); + // cpp_context_collection + // .get() + // .iter() + // .find(|(_, c)| { + // c.get_types().iter().any(|(_, t)| { + // t.nested_types + // .iter() + // .any(|(_, n)| !n.declarations.is_empty()) + // }) + // }) + // .unwrap() + // .1 + // .write()?; + // Doesn't exist anymore? + // info!("AlignmentUnion type"); + // cpp_context_collection + // .get() + // .iter() + // .find(|(_, c)| { + // c.get_types() + // .iter() + // .any(|(_, t)| t.is_value_type && &t.name()== "AlignmentUnion") + // }) + // .unwrap() + // .1 + // .write()?; + info!("Array type"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.name() == "Array" && t.namespace() == "System") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("Default param"); + cpp_context_collection + .get() + .iter() + .filter(|(_, c)| { + c.get_types().iter().any(|(_, t)| { + t.implementations.iter().any(|d| { + if let CppMember::MethodImpl(m) = d.as_ref() { + m.parameters.iter().any(|p| p.def_value.is_some()) + } else { + false + } + }) + }) + }) + .nth(2) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("Enum type"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| c.get_types().iter().any(|(_, t)| t.is_enum_type)) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("UnityEngine.Object"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.name() == "Object" && t.namespace() == "UnityEngine") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("BeatmapSaveDataHelpers"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.name() == "BeatmapSaveDataHelpers") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("HMUI.ViewController"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "HMUI" && t.name() == "ViewController") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("UnityEngine.Component"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "UnityEngine" && t.name() == "Component") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("UnityEngine.GameObject"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "UnityEngine" && t.name() == "GameObject") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("MainFlowCoordinator"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace().is_empty() && t.name() == "MainFlowCoordinator") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("OVRPlugin"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace().is_empty() && t.name() == "OVRPlugin") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("HMUI.IValueChanger"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "HMUI" && t.name() == "IValueChanger`1") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("System.ValueType"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "System" && t.name() == "ValueType") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("System.ValueTuple_2"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "System" && t.name() == "ValueTuple`2") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("System.Decimal"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "System" && t.name() == "Decimal") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("System.Enum"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "System" && t.name() == "Enum") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("System.Multicast"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "System" && t.name() == "MulticastDelegate") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("System.Delegate"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.namespace() == "System" && t.name() == "Delegate") + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + info!("BeatmapSaveDataVersion3.BeatmapSaveData.EventBoxGroup`1"); + cpp_context_collection + .get() + .iter() + .find(|(_, c)| { + c.get_types() + .iter() + .any(|(_, t)| t.name().contains("EventBoxGroup`1")) + }) + .unwrap() + .1 + .write(&STATIC_CONFIG)?; + // for (_, context) in cpp_context_collection.get() { + // context.write().unwrap(); + // } + } + + Ok(()) +} + +fn format_files() -> color_eyre::Result<()> { + info!("Formatting!"); + + use walkdir::WalkDir; + + let files: Vec = WalkDir::new(&STATIC_CONFIG.header_path) + .into_iter() + .filter(|f| f.as_ref().is_ok_and(|f| f.path().is_file())) + .try_collect()?; + + let file_count = files.len(); + + info!( + "{file_count} files across {} threads", + rayon::current_num_threads() + ); + // easily get file size for a given file + fn file_size(file: &DirEntry) -> usize { + match std::fs::metadata(file.path()) { + Ok(data) => file.path().size_on_disk_fast(&data).unwrap() as usize, + Err(_) => 0, + } + } + + // TODO: Debug + warn!("Do not run with debugger, for some reason an early abrupt exit."); + + files + .iter() + // sort on file size + .sorted_by(|a, b| file_size(a).cmp(&file_size(b))) + // reverse to go big -> small, so we can work on other files while big files are happening + .rev() + // parallelism + .enumerate() + .par_bridge() + .try_for_each(|(file_num, file)| -> Result<()> { + let path = file.path(); + info!( + "Formatting [{}/{file_count}] {}", + file_num + 1, + path.display() + ); + let mut command = Command::new("clang-format"); + command.arg("-i").arg(path); + + let spawn = command + .output() + .suggestion("You may be missing clang-format. Ensure it is on PATH")?; + + if !spawn.stderr.is_empty() { + error!( + "Error {} {}", + path.display(), + String::from_utf8(spawn.stderr)? + ); + } + + spawn.status.exit_ok()?; + + Ok(()) + })?; + + info!("Done formatting!"); + Ok(()) +} diff --git a/src/generate/members.rs b/src/generate/cpp/cpp_members.rs similarity index 93% rename from src/generate/members.rs rename to src/generate/cpp/cpp_members.rs index b6114e68c..ece57373a 100644 --- a/src/generate/members.rs +++ b/src/generate/cpp/cpp_members.rs @@ -1,12 +1,9 @@ use itertools::Itertools; use pathdiff::diff_paths; -use crate::STATIC_CONFIG; - -use super::{ - context::CppContext, - cpp_type::{CppType, CORDL_REFERENCE_TYPE_CONSTRAINT}, - writer::Writable, +use crate::generate::{ + cs_members::{CsGenericTemplate, CsGenericTemplateType}, + writer::CppWritable, }; use std::{ @@ -17,6 +14,12 @@ use std::{ sync::Arc, }; +use super::{ + config::STATIC_CONFIG, + cpp_context::CppContext, + cpp_type::{CppType, CORDL_REFERENCE_TYPE_CONSTRAINT}, +}; + #[derive(Debug, Eq, Hash, PartialEq, Clone, Default, PartialOrd, Ord)] pub struct CppTemplate { pub names: Vec<(String, String)>, @@ -45,6 +48,27 @@ impl CppTemplate { } } +impl From for CppTemplate { + fn from(value: CsGenericTemplate) -> Self { + CppTemplate { + names: value + .names + .into_iter() + .map(|(constraint, name)| { + let cpp_ty = match constraint { + CsGenericTemplateType::Any => "typename".to_string(), + CsGenericTemplateType::Reference => { + CORDL_REFERENCE_TYPE_CONSTRAINT.to_string() + } + }; + + (cpp_ty, name) + }) + .collect(), + } + } +} + #[derive(Debug, Eq, Hash, PartialEq, Clone, Default, PartialOrd, Ord)] pub struct CppStaticAssert { pub condition: String, @@ -159,6 +183,7 @@ pub struct CppMethodSizeStruct { pub method_info_lines: Vec, pub method_info_var: String, + pub declaring_template: Option, pub template: Option, pub generic_literals: Option>, @@ -170,7 +195,7 @@ pub struct CppMethodSizeStruct { pub struct CppFieldDecl { pub cpp_name: String, pub field_ty: String, - pub offset: u32, + pub offset: Option, pub instance: bool, pub readonly: bool, pub const_expr: bool, @@ -260,7 +285,7 @@ pub struct CppMethodDecl { pub is_inline: bool, pub brief: Option, - pub body: Option>>, + pub body: Option>>, } impl PartialEq for CppMethodDecl { @@ -364,7 +389,7 @@ pub struct CppMethodImpl { pub prefix_modifiers: Vec, pub brief: Option, - pub body: Vec>, + pub body: Vec>, } impl PartialEq for CppMethodImpl { @@ -445,7 +470,7 @@ pub struct CppConstructorDecl { pub initialized_values: HashMap, pub brief: Option, - pub body: Option>>, + pub body: Option>>, } impl PartialEq for CppConstructorDecl { @@ -496,7 +521,7 @@ pub struct CppConstructorImpl { pub template: Option, - pub body: Vec>, + pub body: Vec>, } impl PartialEq for CppConstructorImpl { @@ -578,19 +603,17 @@ impl CppForwardDeclare { Self::from_cpp_type_long(cpp_type, false) } pub fn from_cpp_type_long(cpp_type: &CppType, force_generics: bool) -> Self { - let ns = if !cpp_type.nested { - Some(cpp_type.cpp_namespace().to_string()) - } else { - None - }; + let ns = cpp_type.cpp_namespace().to_string(); - assert!( - cpp_type.cpp_name_components.declaring_types.is_none(), - "Can't forward declare nested types!" - ); + // TODO: Proper nested check + // assert!( + // cpp_type.is_nested, + // "Can't forward declare nested types! {:#?}", + // cpp_type.cpp_name_components + // ); // literals should only be added for generic specializations - let literals = if cpp_type.generic_instantiations_args_types.is_some() || force_generics { + let literals = if force_generics { cpp_type.cpp_name_components.generics.clone() } else { None @@ -598,7 +621,7 @@ impl CppForwardDeclare { Self { is_struct: cpp_type.is_value_type, - cpp_namespace: ns, + cpp_namespace: Some(ns), cpp_name: cpp_type.cpp_name().clone(), templates: cpp_type.cpp_template.clone(), literals, diff --git a/src/generate/members_serialize.rs b/src/generate/cpp/cpp_members_serialize.rs similarity index 91% rename from src/generate/members_serialize.rs rename to src/generate/cpp/cpp_members_serialize.rs index d78becec3..7d42af6d0 100644 --- a/src/generate/members_serialize.rs +++ b/src/generate/cpp/cpp_members_serialize.rs @@ -1,12 +1,16 @@ -use super::{ - members::*, - writer::{CppWriter, SortLevel, Sortable, Writable}, -}; - use itertools::Itertools; use std::io::Write; -impl Writable for CppTemplate { +use crate::generate::writer::{CppWritable, CppWriter, SortLevel, Sortable}; + +use super::cpp_members::{ + CppCommentedString, CppConstructorDecl, CppConstructorImpl, CppFieldDecl, CppFieldImpl, + CppForwardDeclare, CppInclude, CppLine, CppMember, CppMethodDecl, CppMethodImpl, + CppMethodSizeStruct, CppNestedStruct, CppNestedUnion, CppNonMember, CppParam, CppPropertyDecl, + CppStaticAssert, CppTemplate, CppUsingAlias, +}; + +impl CppWritable for CppTemplate { fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { writeln!( writer, @@ -22,7 +26,7 @@ impl Writable for CppTemplate { } } -impl Writable for CppForwardDeclare { +impl CppWritable for CppForwardDeclare { fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { if let Some(namespace) = &self.cpp_namespace { writeln!(writer, "namespace {namespace} {{")?; @@ -62,7 +66,7 @@ impl Writable for CppForwardDeclare { } } -impl Writable for CppCommentedString { +impl CppWritable for CppCommentedString { fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { writeln!(writer, "{}", self.data)?; if let Some(val) = &self.comment { @@ -72,7 +76,7 @@ impl Writable for CppCommentedString { } } -impl Writable for CppInclude { +impl CppWritable for CppInclude { fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { // this is so bad let path = if cfg!(windows) { @@ -89,7 +93,7 @@ impl Writable for CppInclude { Ok(()) } } -impl Writable for CppUsingAlias { +impl CppWritable for CppUsingAlias { fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { if let Some(template) = &self.template { template.write(writer)?; @@ -111,8 +115,8 @@ impl Sortable for CppUsingAlias { } } -impl Writable for CppFieldDecl { - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { +impl CppWritable for CppFieldDecl { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { if let Some(comment) = &self.brief_comment { writeln!(writer, "/// @brief {comment}")?; } @@ -158,8 +162,8 @@ impl Sortable for CppFieldDecl { } } -impl Writable for CppFieldImpl { - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { +impl CppWritable for CppFieldImpl { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { if let Some(template) = &self.declaring_type_template { template.write(writer)?; } @@ -197,9 +201,9 @@ impl Sortable for CppFieldImpl { } } -impl Writable for CppMethodDecl { +impl CppWritable for CppMethodDecl { // declaration - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { if let Some(brief) = &self.brief { writeln!(writer, "/// @brief {brief}")?; } @@ -292,9 +296,9 @@ impl Sortable for CppMethodDecl { } } -impl Writable for CppMethodImpl { +impl CppWritable for CppMethodImpl { // declaration - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { if let Some(brief) = &self.brief { writeln!(writer, "/// @brief {brief}")?; } @@ -379,9 +383,9 @@ impl Sortable for CppMethodImpl { } } -impl Writable for CppConstructorDecl { +impl CppWritable for CppConstructorDecl { // declaration - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { // I'm lazy if self.is_protected { writeln!(writer, "protected:")?; @@ -478,9 +482,9 @@ impl Sortable for CppConstructorDecl { } } -impl Writable for CppConstructorImpl { +impl CppWritable for CppConstructorImpl { // declaration - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { writeln!(writer, "// Ctor Parameters {:?}", self.parameters)?; // Constructor @@ -547,12 +551,8 @@ impl Sortable for CppConstructorImpl { } } -impl Writable for CppPropertyDecl { - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { - if !self.instance { - return Ok(()) - } - +impl CppWritable for CppPropertyDecl { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { let mut prefix_modifiers: Vec<&str> = vec![]; let suffix_modifiers: Vec<&str> = vec![]; @@ -596,13 +596,14 @@ impl Sortable for CppPropertyDecl { } } -impl Writable for CppMethodSizeStruct { - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { +impl CppWritable for CppMethodSizeStruct { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { writeln!( writer, "// Writing Method size for method: {}.{}", self.declaring_type_name, self.cpp_method_name )?; + let template = self.template.clone().unwrap_or_default(); let complete_type_name = &self.declaring_type_name; @@ -639,6 +640,9 @@ impl Writable for CppMethodSizeStruct { "".to_string() }; + if let Some(declaring_template) = self.declaring_template.as_ref() { + declaring_template.write(writer)?; + } template.write(writer)?; writeln!( @@ -664,7 +668,7 @@ impl Sortable for CppMethodSizeStruct { } } -impl Writable for CppStaticAssert { +impl CppWritable for CppStaticAssert { fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { let condition = &self.condition; match &self.message { @@ -674,7 +678,7 @@ impl Writable for CppStaticAssert { Ok(()) } } -impl Writable for CppLine { +impl CppWritable for CppLine { fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { writer.write_all(self.line.as_bytes())?; writeln!(writer)?; // add line ending @@ -682,8 +686,8 @@ impl Writable for CppLine { } } -impl Writable for CppNestedStruct { - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { +impl CppWritable for CppNestedStruct { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { if self.is_private { writeln!(writer, "private:")?; } @@ -737,8 +741,8 @@ impl Sortable for CppNestedStruct { } } -impl Writable for CppNestedUnion { - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { +impl CppWritable for CppNestedUnion { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { if self.is_private { writeln!(writer, "private:")?; } @@ -769,8 +773,8 @@ impl Sortable for CppNestedUnion { } } -impl Writable for CppMember { - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { +impl CppWritable for CppMember { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { match self { CppMember::FieldDecl(f) => f.write(writer), CppMember::FieldImpl(f) => f.write(writer), @@ -789,8 +793,8 @@ impl Writable for CppMember { } } -impl Writable for CppNonMember { - fn write(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { +impl CppWritable for CppNonMember { + fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { match self { CppNonMember::SizeStruct(ss) => ss.write(writer), CppNonMember::Comment(c) => c.write(writer), diff --git a/src/generate/cpp/cpp_name_resolver.rs b/src/generate/cpp/cpp_name_resolver.rs new file mode 100644 index 000000000..f720e6686 --- /dev/null +++ b/src/generate/cpp/cpp_name_resolver.rs @@ -0,0 +1,281 @@ +use brocolib::{global_metadata::Il2CppTypeDefinition, runtime_metadata::Il2CppTypeEnum}; +use itertools::Itertools; + +use crate::{ + data::{ + name_components::NameComponents, + type_resolver::{ResolvedType, ResolvedTypeData, TypeUsage}, + }, + generate::{metadata::CordlMetadata, type_extensions::TypeDefinitionExtensions}, +}; + +use super::{ + cpp_context_collection::CppContextCollection, + cpp_members::{CppForwardDeclare, CppInclude}, + cpp_type::CppType, + handlers::unity, +}; + +pub const VALUE_WRAPPER_TYPE: &str = "::bs_hook::ValueType"; +pub const ENUM_WRAPPER_TYPE: &str = "::bs_hook::EnumType"; +pub const INTERFACE_WRAPPER_TYPE: &str = "::cordl_internals::InterfaceW"; +pub const IL2CPP_OBJECT_TYPE: &str = "Il2CppObject"; + +pub struct CppNameResolver<'a, 'b> { + pub cordl_metadata: &'a CordlMetadata<'b>, + pub collection: &'a CppContextCollection, +} + +impl<'a, 'b> CppNameResolver<'a, 'b> { + pub fn resolve_name( + &self, + declaring_cpp_type: &mut CppType, + ty: &ResolvedType, + type_usage: TypeUsage, + hard_include: bool, + ) -> NameComponents { + let metadata = self.cordl_metadata; + match &ty.data { + ResolvedTypeData::Array(array_type) => { + let generic = + self.resolve_name(declaring_cpp_type, array_type, type_usage, hard_include); + let generic_formatted = generic.combine_all(); + + NameComponents { + name: "ArrayW".into(), + namespace: Some("".into()), + generics: Some(vec![ + generic_formatted.clone(), + format!("::Array<{generic_formatted}>*"), + ]), + is_pointer: false, + ..Default::default() + } + } + ResolvedTypeData::GenericInst(resolved_type, vec) => { + let type_def_name_components = + self.resolve_name(declaring_cpp_type, resolved_type, type_usage, hard_include); + let generic_types_formatted = vec + .iter() + .map(|(r, inc)| { + self.resolve_name(declaring_cpp_type, r, type_usage, *inc && hard_include) + }) + .map(|n| n.combine_all()) + .collect_vec(); + + // add generics to type def + NameComponents { + generics: Some(generic_types_formatted), + ..type_def_name_components + } + } + ResolvedTypeData::GenericArg(gen_param_idx, _arg_idx) => { + let generic_param = + &metadata.metadata.global_metadata.generic_parameters[*gen_param_idx]; + + generic_param.name(metadata.metadata).to_string().into() + } + ResolvedTypeData::GenericMethodArg(_method_index, gen_param_idx, _method_arg) => { + let generic_param = + &metadata.metadata.global_metadata.generic_parameters[*gen_param_idx]; + + // let arg = declaring_cpp_type + // .method_generic_instantiation_map + // .get(&method_index) + // .and_then(|v| v.get(method_arg as usize)); + + generic_param.name(metadata.metadata).to_string().into() + } + ResolvedTypeData::Ptr(resolved_type) => { + let generic_formatted = + self.resolve_name(declaring_cpp_type, resolved_type, type_usage, hard_include); + NameComponents { + namespace: Some("cordl_internals".into()), + generics: Some(vec![generic_formatted.combine_all()]), + name: "Ptr".into(), + ..Default::default() + } + } + ResolvedTypeData::Type(resolved_tag) => { + if *resolved_tag == declaring_cpp_type.self_tag { + return declaring_cpp_type.cpp_name_components.clone(); + } + + let resolved_context_root_tag = self.collection.get_context_root_tag(*resolved_tag); + let self_context_root_tag = self + .collection + .get_context_root_tag(declaring_cpp_type.self_tag); + + let incl_context = self + .collection + .get_context(*resolved_tag) + .unwrap_or_else(|| panic!("Unable to find type {ty:#?}")); + let incl_ty = self + .collection + .get_cpp_type(*resolved_tag) + .unwrap_or_else(|| { + let td = &metadata.metadata.global_metadata.type_definitions + [resolved_tag.get_tdi()]; + + println!( + "ty {resolved_tag:#?} vs aliased {:#?}", + self.collection.alias_context.get(resolved_tag) + ); + println!("{}", incl_context.fundamental_path.display()); + panic!( + "Unable to find type {ty:#?} {}", + td.full_name(metadata.metadata, true) + ); + }); + + if hard_include { + declaring_cpp_type.requirements.add_dependency(incl_ty); + } + + let is_own_context = resolved_context_root_tag == self_context_root_tag; + + if !is_own_context { + match hard_include { + // can add include + true => { + declaring_cpp_type.requirements.add_def_include( + Some(incl_ty), + CppInclude::new_context_typedef(incl_context), + ); + declaring_cpp_type.requirements.add_impl_include( + Some(incl_ty), + CppInclude::new_context_typeimpl(incl_context), + ); + } + // add forward declare + false => { + declaring_cpp_type.requirements.add_forward_declare(( + CppForwardDeclare::from_cpp_type(incl_ty), + CppInclude::new_context_typedef(incl_context), + )); + } + } + } + + self.resolve_redirect(incl_ty, type_usage) + } + ResolvedTypeData::Primitive(il2_cpp_type_enum) => { + let requirements = &mut declaring_cpp_type.requirements; + + match il2_cpp_type_enum { + Il2CppTypeEnum::I1 + | Il2CppTypeEnum::U1 + | Il2CppTypeEnum::I2 + | Il2CppTypeEnum::U2 + | Il2CppTypeEnum::I4 + | Il2CppTypeEnum::U4 + | Il2CppTypeEnum::I8 + | Il2CppTypeEnum::U8 + | Il2CppTypeEnum::I + | Il2CppTypeEnum::U => { + requirements.needs_int_include(); + } + Il2CppTypeEnum::R4 | Il2CppTypeEnum::R8 => { + requirements.needs_math_include(); + } + _ => (), + }; + + let s: String = match il2_cpp_type_enum { + Il2CppTypeEnum::I1 => "int8_t".to_string(), + Il2CppTypeEnum::I2 => "int16_t".to_string(), + Il2CppTypeEnum::I4 => "int32_t".to_string(), + Il2CppTypeEnum::I8 => "int64_t".to_string(), + Il2CppTypeEnum::U1 => "uint8_t".to_string(), + Il2CppTypeEnum::U2 => "uint16_t".to_string(), + Il2CppTypeEnum::U4 => "uint32_t".to_string(), + Il2CppTypeEnum::U8 => "uint64_t".to_string(), + + Il2CppTypeEnum::R4 => "float_t".to_string(), + Il2CppTypeEnum::R8 => "double_t".to_string(), + + Il2CppTypeEnum::Void => "void".to_string(), + Il2CppTypeEnum::Boolean => "bool".to_string(), + Il2CppTypeEnum::Char => "char16_t".to_string(), + Il2CppTypeEnum::Object => "void*".to_string(), + + Il2CppTypeEnum::String => { + requirements.needs_stringw_include(); + "::StringW".to_string() + } + + _ => panic!("Unsupported type {il2_cpp_type_enum:#?}"), + }; + NameComponents::from(s) + } + ResolvedTypeData::Blacklisted(cs_type_tag) => { + let td = &metadata.metadata.global_metadata.type_definitions[cs_type_tag.get_tdi()]; + + Self::wrapper_type_for_tdi(td) + } + ResolvedTypeData::ByRef(resolved_type) => { + let generic = + self.resolve_name(declaring_cpp_type, resolved_type, type_usage, hard_include); + let generic_formatted = generic.combine_all(); + + NameComponents { + name: "ByRef".into(), + namespace: Some("".into()), + generics: Some(vec![generic_formatted.clone()]), + is_pointer: false, + ..Default::default() + } + } + ResolvedTypeData::ByRefConst(resolved_type) => { + let generic = + self.resolve_name(declaring_cpp_type, resolved_type, type_usage, hard_include); + let generic_formatted = generic.combine_all(); + + NameComponents { + name: "ByRefConst".into(), + namespace: Some("".into()), + generics: Some(vec![generic_formatted.clone()]), + is_pointer: false, + ..Default::default() + } + } + } + } + + fn resolve_redirect(&self, incl_ty: &CppType, type_usage: TypeUsage) -> NameComponents { + let mut name_components = incl_ty.cpp_name_components.clone(); + name_components = unity::unity_object_resolve_handler( + name_components, + incl_ty, + self.cordl_metadata, + type_usage, + ); + name_components + } + + fn wrapper_type_for_tdi(td: &Il2CppTypeDefinition) -> NameComponents { + if td.is_enum_type() { + return ENUM_WRAPPER_TYPE.to_string().into(); + } + + if td.is_value_type() { + return VALUE_WRAPPER_TYPE.to_string().into(); + } + + if td.is_interface() { + return INTERFACE_WRAPPER_TYPE.to_string().into(); + } + + il2cpp_object_name_component() + } +} + +fn il2cpp_object_name_component() -> NameComponents { + NameComponents { + name: IL2CPP_OBJECT_TYPE.to_string(), + is_pointer: true, + generics: None, + namespace: None, + declaring_types: None, + } +} diff --git a/src/generate/cpp/cpp_type.rs b/src/generate/cpp/cpp_type.rs new file mode 100644 index 000000000..125c81ea3 --- /dev/null +++ b/src/generate/cpp/cpp_type.rs @@ -0,0 +1,2168 @@ +use std::{ + collections::{HashMap, HashSet}, + rc::Rc, + sync::Arc, +}; + +use brocolib::global_metadata::{FieldIndex, MethodIndex, TypeDefinitionIndex}; +use color_eyre::eyre::Context; +use itertools::Itertools; + +use std::io::Write; + +use crate::{ + data::{ + name_components::NameComponents, + type_resolver::{ResolvedType, ResolvedTypeData, TypeUsage}, + }, + generate::{ + cpp::cpp_members::{CppMethodSizeStruct, CppStaticAssert}, + cs_members::{ + CSMethodFlags, CsConstructor, CsField, CsMethod, CsParam, CsProperty, CsValue, + }, + cs_type::CsType, + cs_type_tag::CsTypeTag, + metadata::CordlMetadata, + offsets::SizeInfo, + type_extensions::{ + TypeDefinitionExtensions, TypeDefinitionIndexExtensions, TypeExtentions, + }, + writer::{CppWritable, CppWriter, Sortable}, + }, +}; + +use super::{ + config::CppGenerationConfig, + cpp_fields, + cpp_members::{ + CppConstructorDecl, CppConstructorImpl, CppFieldDecl, CppForwardDeclare, CppInclude, + CppLine, CppMember, CppMethodData, CppMethodDecl, CppMethodImpl, CppNestedStruct, + CppNonMember, CppParam, CppPropertyDecl, CppTemplate, CppUsingAlias, + }, + cpp_name_resolver::{CppNameResolver, VALUE_WRAPPER_TYPE}, +}; + +pub const CORDL_TYPE_MACRO: &str = "CORDL_TYPE"; +pub const __CORDL_IS_VALUE_TYPE: &str = "__IL2CPP_IS_VALUE_TYPE"; +pub const __CORDL_BACKING_ENUM_TYPE: &str = "__CORDL_BACKING_ENUM_TYPE"; + +pub const CORDL_REFERENCE_TYPE_CONSTRAINT: &str = "::il2cpp_utils::il2cpp_reference_type"; +pub const CORDL_NUM_ENUM_TYPE_CONSTRAINT: &str = "::cordl_internals::is_or_is_backed_by"; +pub const CORDL_METHOD_HELPER_NAMESPACE: &str = "::cordl_internals"; + +// negative +pub const VALUE_TYPE_SIZE_OFFSET: u32 = 0x10; + +pub const VALUE_TYPE_WRAPPER_SIZE: &str = "__IL2CPP_VALUE_TYPE_SIZE"; +pub const REFERENCE_TYPE_WRAPPER_SIZE: &str = "__IL2CPP_REFERENCE_TYPE_SIZE"; +pub const REFERENCE_TYPE_FIELD_SIZE: &str = "__fields"; +pub const REFERENCE_WRAPPER_INSTANCE_NAME: &str = "::bs_hook::Il2CppWrapperType::instance"; + +pub const CORDL_NO_INCLUDE_IMPL_DEFINE: &str = "CORDL_NO_IMPL_INCLUDE"; +pub const CORDL_ACCESSOR_FIELD_PREFIX: &str = "___"; + +pub const ENUM_PTR_TYPE: &str = "::bs_hook::EnumPtr"; +pub const VT_PTR_TYPE: &str = "::bs_hook::VTPtr"; + +const SIZEOF_IL2CPP_OBJECT: u32 = 0x10; + +#[derive(Debug, Clone)] +pub struct CppTypeRequirements { + pub self_tag: CsTypeTag, + pub forward_declares: HashSet<(CppForwardDeclare, CppInclude)>, + + // Only value types or classes + pub required_def_includes: HashSet, + pub required_impl_includes: HashSet, + + // Lists both types we forward declare or include + pub depending_types: HashSet, +} + +impl CppTypeRequirements { + pub fn add_forward_declare(&mut self, cpp_data: (CppForwardDeclare, CppInclude)) { + // self.depending_types.insert(cpp_type.self_tag); + self.forward_declares.insert(cpp_data); + } + + pub fn add_def_include(&mut self, cpp_type: Option<&CppType>, cpp_include: CppInclude) { + if let Some(cpp_type) = cpp_type { + self.add_dependency(cpp_type); + } + self.required_def_includes.insert(cpp_include); + } + pub fn add_impl_include(&mut self, cpp_type: Option<&CppType>, cpp_include: CppInclude) { + if let Some(cpp_type) = cpp_type { + self.add_dependency(cpp_type); + } + self.required_impl_includes.insert(cpp_include); + } + pub fn add_dependency(&mut self, cpp_type: &CppType) { + self.add_dependency_tag(cpp_type.self_tag); + } + + pub fn add_dependency_tag(&mut self, tag: CsTypeTag) { + if tag == self.self_tag { + panic!("Cannot depend on self!"); + } + + self.depending_types.insert(tag); + } + + pub fn need_wrapper(&mut self) { + self.add_def_include( + None, + CppInclude::new_exact("beatsaber-hook/shared/utils/base-wrapper-type.hpp"), + ); + } + pub fn needs_int_include(&mut self) { + self.add_def_include(None, CppInclude::new_system("cstdint")); + } + pub fn needs_byte_include(&mut self) { + self.add_def_include(None, CppInclude::new_system("cstddef")); + } + pub fn needs_math_include(&mut self) { + self.add_def_include(None, CppInclude::new_system("cmath")); + } + pub fn needs_stringw_include(&mut self) { + self.add_def_include( + None, + CppInclude::new_exact("beatsaber-hook/shared/utils/typedefs-string.hpp"), + ); + } + pub fn needs_arrayw_include(&mut self) { + self.add_def_include( + None, + CppInclude::new_exact("beatsaber-hook/shared/utils/typedefs-array.hpp"), + ); + } + + pub fn needs_byref_include(&mut self) { + self.add_def_include( + None, + CppInclude::new_exact("beatsaber-hook/shared/utils/byref.hpp"), + ); + } + + pub fn needs_enum_include(&mut self) { + self.add_def_include( + None, + CppInclude::new_exact("beatsaber-hook/shared/utils/enum-type.hpp"), + ); + } + + pub fn needs_value_include(&mut self) { + self.add_def_include( + None, + CppInclude::new_exact("beatsaber-hook/shared/utils/value-type.hpp"), + ); + } +} + +#[derive(Clone, Debug)] +pub struct CppType { + pub declarations: Vec>, + pub nonmember_declarations: Vec>, + pub implementations: Vec>, + pub nonmember_implementations: Vec>, + + pub parent: Option, + pub interfaces: Vec, + + pub is_value_type: bool, + pub is_enum_type: bool, + pub is_reference_type: bool, + pub is_interface: bool, + + pub requirements: CppTypeRequirements, + pub self_tag: CsTypeTag, + + /// contains the array of generic Il2CppType indexes + pub generic_instantiations_args_types: Option>, // GenericArg -> Instantiation Arg + pub method_generic_instantiation_map: HashMap>, // MethodIndex -> Generic Args + + pub cpp_template: Option, + pub cs_name_components: NameComponents, + pub cpp_name_components: NameComponents, + pub(crate) prefix_comments: Vec, + pub packing: Option, + pub size_info: Option, +} + +impl CppType { + pub fn write_impl(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { + self.write_impl_internal(writer) + } + + pub fn write_def(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { + self.write_def_internal(writer, Some(&self.cpp_namespace())) + } + + pub fn write_impl_internal(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { + self.nonmember_implementations + .iter() + .try_for_each(|d| d.write(writer))?; + + // Write all declarations within the type here + self.implementations + .iter() + .sorted_by(|a, b| a.sort_level().cmp(&b.sort_level())) + .try_for_each(|d| d.write(writer))?; + + Ok(()) + } + + fn write_def_internal( + &self, + writer: &mut CppWriter, + namespace: Option<&str>, + ) -> color_eyre::Result<()> { + self.prefix_comments + .iter() + .try_for_each(|pc| writeln!(writer, "// {pc}").context("Prefix comment"))?; + + let type_kind = match self.is_value_type { + true => "struct", + false => "class", + }; + + // Just forward declare + if let Some(n) = &namespace { + writeln!(writer, "namespace {n} {{")?; + writer.indent(); + } + + // Write type definition + if let Some(generic_args) = &self.cpp_template { + writeln!(writer, "// cpp template")?; + generic_args.write(writer)?; + } + writeln!(writer, "// Is value type: {}", self.is_value_type)?; + + let clazz_name = self.cpp_name_components.formatted_name(false); + + writeln!( + writer, + "// CS Name: {}", + self.cs_name_components.combine_all() + )?; + + if let Some(packing) = &self.packing { + writeln!(writer, "#pragma pack(push, {packing})")?; + } + + let inherits = self.get_inherits().collect_vec(); + match inherits.is_empty() { + true => writeln!(writer, "{type_kind} {CORDL_TYPE_MACRO} {clazz_name} {{")?, + false => writeln!( + writer, + "{type_kind} {CORDL_TYPE_MACRO} {clazz_name} : {} {{", + inherits + .into_iter() + .map(|s| format!("public {s}")) + .join(", ") + )?, + } + + writer.indent(); + + // add public access + writeln!(writer, "public:")?; + writeln!(writer, "// Declarations")?; + // Write all declarations within the type here + self.declarations + .iter() + .sorted_by(|a, b| a.as_ref().partial_cmp(b.as_ref()).unwrap()) + .sorted_by(|a, b| { + // fields and unions need to be sorted by offset to work correctly + + let a_offset = match a.as_ref() { + CppMember::FieldDecl(f) => f.offset, + CppMember::NestedUnion(u) => Some(u.offset), + _ => None, + }; + + let b_offset = match b.as_ref() { + CppMember::FieldDecl(f) => f.offset, + CppMember::NestedUnion(u) => Some(u.offset), + _ => None, + }; + + a_offset.cmp(&b_offset) + }) + // sort by sort level after fields have been ordered correctly + .sorted_by(|a, b| a.sort_level().cmp(&b.sort_level())) + .try_for_each(|d| -> color_eyre::Result<()> { + d.write(writer)?; + writeln!(writer)?; + Ok(()) + })?; + + writeln!( + writer, + "static constexpr bool {__CORDL_IS_VALUE_TYPE} = {};", + self.is_value_type + )?; + // Type complete + writer.dedent(); + writeln!(writer, "}};")?; + + if self.packing.is_some() { + writeln!(writer, "#pragma pack(pop)")?; + } + + // NON MEMBER DECLARATIONS + writeln!(writer, "// Non member Declarations")?; + + self.nonmember_declarations + .iter() + .try_for_each(|d| -> color_eyre::Result<()> { + d.write(writer)?; + writeln!(writer)?; + Ok(()) + })?; + + // Namespace complete + if let Some(n) = namespace { + writer.dedent(); + writeln!(writer, "}} // namespace end def {n}")?; + } + + // TODO: Write additional meta-info here, perhaps to ensure correct conversions? + Ok(()) + } + + pub fn write_type_trait(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { + if self.cpp_template.is_some() { + // generic + // macros from bs hook + let type_trait_macro = if self.is_enum_type || self.is_value_type { + "MARK_GEN_VAL_T" + } else { + "MARK_GEN_REF_PTR_T" + }; + + writeln!( + writer, + "{type_trait_macro}({});", + self.cpp_name_components + .clone() + .remove_generics() + .remove_pointer() + .combine_all() + )?; + } else { + // non-generic + // macros from bs hook + let type_trait_macro = if self.is_enum_type || self.is_value_type { + "MARK_VAL_T" + } else { + "MARK_REF_PTR_T" + }; + + writeln!( + writer, + "{type_trait_macro}({});", + self.cpp_name_components.remove_pointer().combine_all() + )?; + } + + Ok(()) + } + + pub fn make_cpp_type( + tag: CsTypeTag, + cs_type: &CsType, + config: &CppGenerationConfig, + ) -> CppType { + let cs_name_components = &cs_type.cs_name_components; + + let cpp_name_components = NameComponents { + declaring_types: cs_name_components + .declaring_types + .as_ref() + .map(|declaring_types| { + declaring_types + .iter() + .map(|s| config.name_cpp(s)) + .collect_vec() + }), + generics: cs_name_components.generics.clone(), + name: config.name_cpp(&cs_name_components.name), + namespace: cs_name_components + .namespace + .as_ref() + .map(|s| config.namespace_cpp(s)), + is_pointer: cs_name_components.is_pointer, + }; + + let generic_instantiations_args_types = cs_type.generic_instantiations_args_types.clone(); + let method_generic_instantiation_map = cs_type.method_generic_instantiation_map.clone(); + + CppType { + declarations: vec![], + nonmember_declarations: vec![], + implementations: vec![], + nonmember_implementations: vec![], + parent: None, + interfaces: vec![], + + is_value_type: cs_type.is_value_type, + is_enum_type: cs_type.is_enum_type, + is_reference_type: cs_type.is_reference_type, + + requirements: CppTypeRequirements { + self_tag: tag, + forward_declares: Default::default(), + required_def_includes: Default::default(), + required_impl_includes: Default::default(), + depending_types: Default::default(), + }, + self_tag: tag, + + generic_instantiations_args_types, + method_generic_instantiation_map, + + cpp_template: cs_type.generic_template.clone().map(|t| t.into()), + cpp_name_components, // TODO + cs_name_components: cs_type.cs_name_components.clone(), + prefix_comments: vec![], + packing: cs_type.packing.map(|p| p as u32), + size_info: cs_type.size_info.clone(), + is_interface: cs_type.is_interface, + } + } + + pub fn nested_fixup( + &mut self, + cs_type: &CsType, + metadata: &CordlMetadata, + config: &CppGenerationConfig, + ) { + // Nested type unnesting fix + let Some(declaring_tag) = cs_type.declaring_ty.as_ref() else { + return; + }; + + let declaring_td = declaring_tag + .get_tdi() + .get_type_definition(metadata.metadata); + + let combined_name = self + .cpp_name_components + .clone() + .remove_generics() + .remove_pointer() + .combine_all(); + + self.cpp_name_components.namespace = + Some(config.namespace_cpp(declaring_td.namespace(metadata.metadata))); + self.cpp_name_components.declaring_types = None; // remove declaring types + + self.cpp_name_components.name = config.sanitize_to_cpp_name(&combined_name); + } + + pub fn fill( + &mut self, + cs_type: CsType, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + ) { + let tdi: TypeDefinitionIndex = cs_type.self_tag.into(); + let metadata = name_resolver.cordl_metadata; + let t = &metadata.metadata.global_metadata.type_definitions[tdi]; + + // we depend on parents and generic args here + // default ctor + if t.is_value_type() || t.is_enum_type() { + self.create_valuetype_constructor(&cs_type.fields, name_resolver, config); + self.create_valuetype_field_wrapper(); + if t.is_enum_type() { + let tdi = self.self_tag.get_tdi(); + let t = tdi.get_type_definition(metadata.metadata); + + let backing_field_idx = t.element_type_index as usize; + let backing_field_ty = &metadata.metadata_registration.types[backing_field_idx]; + + let backing_field_resolved_ty = ResolvedType { + data: ResolvedTypeData::Primitive(backing_field_ty.ty), + ty: backing_field_idx, + }; + + self.create_enum_wrapper(backing_field_resolved_ty.clone(), name_resolver, config); + self.create_enum_backing_type_constant( + backing_field_resolved_ty, + name_resolver, + config, + ); + } + self.add_default_ctor(false); + } else if t.is_interface() { + // self.make_interface_constructors(); + + self.delete_copy_ctor(); + // self.delete_default_ctor(); + } else { + // ref type + self.delete_move_ctor(); + self.delete_copy_ctor(); + self.add_default_ctor(true); + // self.delete_default_ctor(); + } + + // Fill type from CS data + self.make_fields(cs_type.fields, name_resolver, config); + self.make_methods(cs_type.methods, name_resolver, config); + self.make_properties(cs_type.properties, name_resolver, config); + self.make_constructors(cs_type.constructors, name_resolver, config); + + self.make_parent(cs_type.parent, name_resolver); + self.make_interfaces(cs_type.interfaces, name_resolver, config); + self.make_nested_types(cs_type.nested_types, name_resolver, config); + + if !t.is_interface() { + self.create_size_assert(); + } + + self.add_type_index_member(); + + if !t.is_interface() { + self.create_size_padding(cs_type.size_info); + } + + let dependencies = self + .requirements + .depending_types + .iter() + .map(|t| { + t.get_tdi() + .get_type_definition(metadata.metadata) + .full_name(metadata.metadata, true) + }) + .sorted() + .collect_vec(); + self.prefix_comments + .push(format!("Dependencies {}", dependencies.join(", "))); + + // if let Some(func) = metadata.custom_type_handler.get(&tdi) { + // func(self) + // } + } + + fn make_fields( + &mut self, + fields: Vec, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + ) { + if self.is_value_type || self.is_enum_type { + cpp_fields::handle_valuetype_fields(self, &fields, name_resolver, config); + } else { + cpp_fields::handle_referencetype_fields(self, &fields, name_resolver, config); + } + + cpp_fields::handle_static_fields(self, &fields, name_resolver, config); + cpp_fields::handle_const_fields(self, &fields, name_resolver, config); + } + + fn make_methods( + &mut self, + methods: Vec, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + ) { + // 2 because each method gets a method struct and method decl + // a constructor will add an additional one for each + self.declarations.reserve(2 * (methods.len() + 1)); + self.implementations.reserve(methods.len() + 1); + + for method in methods { + if method.name == ".cctor" { + continue; + } + self.create_method(&method, name_resolver, config, false); + } + } + + fn make_param( + &mut self, + p: CsParam, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + ) -> CppParam { + let ty = name_resolver.resolve_name(self, &p.il2cpp_ty, TypeUsage::Parameter, false); + CppParam { + name: config.name_cpp(&p.name), + ty: ty.combine_all(), + modifiers: "".to_string(), // TODO: Convert flags + def_value: p.def_value.as_ref().map(|v| v.to_string()), + } + } + + fn make_properties( + &mut self, + properties: Vec, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + ) { + self.declarations.reserve(properties.len()); + for prop in properties { + if !prop.instance { + continue; + } + + let prop_ty = prop.prop_ty.get_type(name_resolver.cordl_metadata); + + let prop_resolved_ty = + name_resolver.resolve_name(self, &prop.prop_ty, TypeUsage::Property, false); + + let getter = prop.getter.map(|g| config.name_cpp(&g)); + let setter = prop.setter.map(|s| config.name_cpp(&s)); + + let prop_decl = CppPropertyDecl { + cpp_name: config.name_cpp(&prop.name), + prop_ty: prop_resolved_ty.combine_all(), + getter, + setter, + indexable: prop.indexable, + brief_comment: prop.brief_comment.clone(), + instance: prop.instance, + }; + + self.declarations + .push(CppMember::Property(prop_decl).into()); + } + } + + fn make_constructors( + &mut self, + constructors: Vec, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + ) { + self.declarations.reserve(constructors.len()); + for ctor in constructors { + let ctor_decl = CppConstructorDecl { + cpp_name: ctor.cpp_name.clone(), + parameters: ctor + .parameters + .into_iter() + .map(|p| self.make_param(p, name_resolver, config)) + .collect(), + template: ctor.template.clone().map(|t| t.into()), + brief: ctor.brief.clone(), + is_constexpr: true, + is_explicit: false, + is_default: false, + is_no_except: true, + is_delete: false, + is_protected: false, + base_ctor: None, + initialized_values: Default::default(), + body: ctor.body.clone(), + }; + + self.declarations + .push(CppMember::ConstructorDecl(ctor_decl).into()); + } + } + + fn make_parent(&mut self, parent: Option, name_resolver: &CppNameResolver) { + if self.is_enum_type || self.is_value_type { + return; + } + + let Some(parent) = parent else { + return; + }; + + let cordl_metadata = name_resolver.cordl_metadata; + let parent_ty = &cordl_metadata.metadata_registration.types[parent.ty]; + + let parent_name = name_resolver.resolve_name(self, &parent, TypeUsage::TypeName, true); + + let parent_tag = CsTypeTag::from_type_data(parent_ty.data, cordl_metadata.metadata); + let parent_tdi: TypeDefinitionIndex = parent_tag.into(); + let ctx_collection = name_resolver.collection; + + let base_type_context = ctx_collection + .get_context(parent_tag) + .or_else(|| ctx_collection.get_context(parent_tdi.into())) + .unwrap_or_else(|| panic!("No CppContext for base type {parent_name:?}.")); + + let base_type_cpp_type = ctx_collection + .get_cpp_type(parent_tag) + .or_else(|| ctx_collection.get_cpp_type(parent_tdi.into())) + .unwrap_or_else(|| panic!("No CppType for base type {parent_name:?}.")); + + self.requirements.add_impl_include( + Some(base_type_cpp_type), + CppInclude::new_context_typeimpl(base_type_context), + ); + + self.parent = Some(parent_name.remove_pointer().combine_all()); + } + + fn make_interfaces( + &mut self, + interfaces: Vec, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + ) { + let self_td = self + .self_tag + .get_tdi() + .get_type_definition(name_resolver.cordl_metadata.metadata); + + for interface in interfaces { + // We have an interface, lets do something with it + let interface_name_il2cpp = + name_resolver.resolve_name(self, &interface, TypeUsage::TypeName, true); + + let interface_cpp_name = interface_name_il2cpp.remove_pointer().combine_all(); + let interface_cpp_pointer = interface_name_il2cpp.as_pointer().combine_all(); + + let operator_method_decl = CppMethodDecl { + body: Default::default(), + brief: Some(format!("Convert operator to {interface_cpp_name:?}")), + cpp_name: interface_cpp_pointer.clone(), + return_type: "".to_string(), + instance: true, + is_const: false, + is_constexpr: true, + is_no_except: !self_td.is_value_type() && !self_td.is_enum_type(), + is_implicit_operator: true, + is_explicit_operator: false, + + is_virtual: false, + is_inline: true, + parameters: vec![], + template: None, + prefix_modifiers: vec![], + suffix_modifiers: vec![], + }; + let helper_method_decl = CppMethodDecl { + brief: Some(format!("Convert to {interface_cpp_name:?}")), + is_implicit_operator: false, + return_type: interface_cpp_pointer.clone(), + cpp_name: format!("i_{}", config.sanitize_to_cpp_name(&interface_cpp_name)), + ..operator_method_decl.clone() + }; + + let method_impl_template = self + .cpp_template + .as_ref() + .is_some_and(|c| !c.names.is_empty()) + .then(|| self.cpp_template.clone()) + .flatten(); + + let convert_line = match self_td.is_value_type() || self_td.is_enum_type() { + true => { + // box + "static_cast(::il2cpp_utils::Box(this))".to_string() + } + false => "static_cast(this)".to_string(), + }; + + let body: Vec> = vec![Arc::new(CppLine::make(format!( + "return static_cast<{interface_cpp_pointer}>({convert_line});" + )))]; + let declaring_cpp_full_name = self.cpp_name_components.remove_pointer().combine_all(); + let operator_method_impl = CppMethodImpl { + body: body.clone(), + declaring_cpp_full_name: declaring_cpp_full_name.clone(), + template: method_impl_template.clone(), + ..operator_method_decl.clone().into() + }; + + let helper_method_impl = CppMethodImpl { + body: body.clone(), + declaring_cpp_full_name, + template: method_impl_template, + ..helper_method_decl.clone().into() + }; + + // operator + self.declarations + .push(CppMember::MethodDecl(operator_method_decl).into()); + self.implementations + .push(CppMember::MethodImpl(operator_method_impl).into()); + + // helper method + self.declarations + .push(CppMember::MethodDecl(helper_method_decl).into()); + self.implementations + .push(CppMember::MethodImpl(helper_method_impl).into()); + } + } + + fn make_nested_types( + &mut self, + nested_types: HashSet, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + ) { + if nested_types.is_empty() { + return; + } + + let metadata = name_resolver.cordl_metadata; + let ctx_collection = name_resolver.collection; + let generic_instantiation_args = self.cpp_name_components.generics.clone(); + + let aliases = nested_types + .into_iter() + .filter(|nested_resolved_ty| { + !metadata + .blacklisted_types + .contains(&nested_resolved_ty.get_tdi()) + }) + .map(|nested_tag| { + let nested_td = nested_tag.get_tdi().get_type_definition(metadata.metadata); + + let nested_context = ctx_collection + .get_context(nested_tag) + .expect("Unable to find CppContext"); + let nested = ctx_collection + .get_cpp_type(nested_tag) + .expect("Unable to find nested CppType"); + + let alias = CppUsingAlias::from_cpp_type( + config.name_cpp(nested_td.name(metadata.metadata)), + nested, + generic_instantiation_args.clone(), + // if no generic args are made, we can do the generic fixup + // ORDER OF PASSES MATTERS + nested.generic_instantiations_args_types.is_none(), + ); + let fd = CppForwardDeclare::from_cpp_type(nested); + let inc = CppInclude::new_context_typedef(nested_context); + + (alias, fd, inc) + }) + .collect_vec(); + + for (alias, fd, inc) in aliases { + self.declarations + .insert(0, CppMember::CppUsingAlias(alias).into()); + self.requirements.add_forward_declare((fd, inc)); + } + } + fn create_method( + &mut self, + method: &CsMethod, + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + is_generic_method_inst: bool, + ) { + // TODO: sanitize method name for c++ + let m_name = &method.name; + + let m_params_with_def = method + .parameters + .iter() + .map(|p| self.make_param(p.clone(), name_resolver, config)) + .collect_vec(); + + let m_params_no_def = m_params_with_def + .iter() + .cloned() + .map(|mut p| { + p.def_value = None; + p + }) + .collect_vec(); + + // TODO: Add template if a generic inst e.g + // T UnityEngine.Component::GetComponent() -> bs_hook::Il2CppWrapperType UnityEngine.Component::GetComponent() + let template = method.template.clone().map(|t| t.into()); + + if method.name == ".ctor" { + Self::create_ref_constructor(self, method, &m_params_with_def, &template); + } + + let mut cpp_ret_type = + name_resolver.resolve_name(self, &method.return_type, TypeUsage::ReturnType, false); + + if cpp_ret_type.combine_all() == "System.Enum" { + self.requirements.needs_enum_include(); + cpp_ret_type = ENUM_PTR_TYPE.to_string().into(); + } + + if cpp_ret_type.combine_all() == "System.ValueType" { + self.requirements.needs_value_include(); + cpp_ret_type = VT_PTR_TYPE.to_string().into(); + } + + let cpp_m_name = { + let cpp_m_name = config.name_cpp(m_name); + + // static functions with same name and params but + // different ret types can exist + // so we add their ret types + let fixup_name = match cpp_m_name == "op_Implicit" || cpp_m_name == "op_Explicit" { + true => { + cpp_m_name + + "_" + + &config + .sanitize_to_cpp_name(&cpp_ret_type.combine_all()) + .replace('*', "_") + } + false => cpp_m_name, + }; + + fixup_name + }; + + let metadata = name_resolver.cordl_metadata; + + // generic methods don't have definitions if not an instantiation + let method_stub = !is_generic_method_inst && template.is_some(); + + let is_virtual = method.method_flags.contains(CSMethodFlags::VIRTUAL); + let is_abstract = method.method_flags.contains(CSMethodFlags::ABSTRACT); + let is_final = method.method_flags.contains(CSMethodFlags::FINAL); + let is_static = method.method_flags.contains(CSMethodFlags::STATIC); + + let method_decl = CppMethodDecl { + body: None, + brief: format!( + "Method {m_name}, addr 0x{:x}, size 0x{:x}, virtual {}, abstract: {}, final {}", + method.method_data.addrs.unwrap_or(u64::MAX), + method.method_data.estimated_size.unwrap_or(usize::MAX), + is_virtual, + is_abstract, + is_final + ) + .into(), + is_const: false, + is_constexpr: false, + is_no_except: false, + cpp_name: cpp_m_name.clone(), + return_type: cpp_ret_type.combine_all(), + parameters: m_params_no_def.clone(), + instance: !is_static, + template: template.clone(), + suffix_modifiers: Default::default(), + prefix_modifiers: Default::default(), + is_virtual: false, + is_implicit_operator: false, + is_explicit_operator: false, + + is_inline: true, + }; + + let instance_ptr: String = if is_static { + "nullptr".into() + } else { + "this".into() + }; + + const METHOD_INFO_VAR_NAME: &str = "___internal_method"; + + let method_invoke_params = vec![instance_ptr.as_str(), METHOD_INFO_VAR_NAME]; + let param_names = CppParam::params_names(&method_decl.parameters).map(|s| s.as_str()); + let declaring_type_cpp_full_name = self.cpp_name_components.remove_pointer().combine_all(); + + let declaring_classof_call = format!( + "::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class<{}>::get()", + self.cpp_name_components.combine_all() + ); + + let extract_self_class = + "il2cpp_functions::object_get_class(reinterpret_cast(this))"; + + let params_types_format: String = CppParam::params_types(&method_decl.parameters) + .map(|t| format!("::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type<{t}>::get()")) + .join(", "); + let params_types_count = method_decl.parameters.len(); + + let resolve_instance_slot_lines = if let Some(slot) = method.method_data.slot { + vec![format!( + "auto* {METHOD_INFO_VAR_NAME} = THROW_UNLESS((::il2cpp_utils::ResolveVtableSlot( + {extract_self_class}, + {declaring_classof_call}, + {slot} + )));" + )] + } else { + vec![] + }; + + // if no params, just empty span + // avoid allocs + let params_types_array_cpp = match params_types_count { + 0 => "::std::span()".to_string(), + _ => format!( + "::std::array{{{params_types_format}}}" + ), + }; + + let method_info_lines = match &template { + Some(template) => { + // generic + let template_names = template + .just_names() + .map(|t| { + format!( + "::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class<{t}>::get()" + ) + }) + .join(", "); + let template_count = template.names.len(); + + // if no template params, just empty span + // avoid allocs + let template_classes_array_cpp = match template_count { + 0 => "std::span()".to_string(), + _ => format!( + "std::array{{{template_names}}}" + ), + }; + + vec![ + format!("static auto* ___internal_method_base = THROW_UNLESS((::il2cpp_utils::FindMethod( + {declaring_classof_call}, + \"{m_name}\", + {template_classes_array_cpp}, + {params_types_array_cpp} + )));"), + format!("static auto* {METHOD_INFO_VAR_NAME} = THROW_UNLESS(::il2cpp_utils::MakeGenericMethod( + ___internal_method_base, + {template_classes_array_cpp} + ));"), + ] + } + None => { + vec![ + format!("static auto* {METHOD_INFO_VAR_NAME} = THROW_UNLESS((::il2cpp_utils::FindMethod( + {declaring_classof_call}, + \"{m_name}\", + std::span(), + {params_types_array_cpp} + )));"), + ] + } + }; + + let method_body_lines = [format!( + "return ::cordl_internals::RunMethodRethrow<{}, false>({});", + cpp_ret_type.combine_all(), + method_invoke_params + .into_iter() + .chain(param_names) + .join(", ") + )]; + + // instance methods should resolve slots if this is an interface, or if this is a virtual/abstract method, and not a final method + // static methods can't be virtual or interface anyway so checking for that here is irrelevant + let should_resolve_slot = self.is_interface || ((is_virtual || is_abstract) && !is_final); + + let method_body = match should_resolve_slot { + true => resolve_instance_slot_lines + .iter() + .chain(method_body_lines.iter()) + .cloned() + .map(|l| -> Arc { Arc::new(CppLine::make(l)) }) + .collect_vec(), + false => method_info_lines + .iter() + .chain(method_body_lines.iter()) + .cloned() + .map(|l| -> Arc { Arc::new(CppLine::make(l)) }) + .collect_vec(), + }; + + let method_impl = CppMethodImpl { + body: method_body, + parameters: m_params_with_def.clone(), + brief: None, + declaring_cpp_full_name: declaring_type_cpp_full_name, + instance: !is_static, + suffix_modifiers: Default::default(), + prefix_modifiers: Default::default(), + template: template.clone(), + declaring_type_template: self.cpp_template.clone(), + + // defaults + ..method_decl.clone().into() + }; + + // don't emit method size structs for generic methods + + // don't emit method size structs for generic methods + + // if type is a generic + let has_template_args = self + .cpp_template + .as_ref() + .is_some_and(|t| !t.names.is_empty()); + + // don't emit method size structs for generic methods + if let Some(addr) = &method.method_data.addrs + && let Some(size) = method.method_data.estimated_size + { + let il2cpp_method = &metadata.metadata.global_metadata.methods[method.method_index]; + let declaring_tdi = &il2cpp_method.declaring_type; + let declaring_td = declaring_tdi.get_type_definition(metadata.metadata); + let declaring_tag: CsTypeTag = CsTypeTag::TypeDefinitionIndex(*declaring_tdi); + + let resolved_generic_types = self + .method_generic_instantiation_map + .get(&method.method_index) + .cloned() + .map(|g| { + g.iter() + .map(|t| name_resolver.resolve_name(self, t, TypeUsage::TypeName, false)) + .map(|n| n.combine_all()) + .collect_vec() + }); + + let interface_declaring_cpp_type: Option<&CppType> = + if *declaring_tdi == self.self_tag.get_tdi() { + Some(self) + } else { + name_resolver.collection.get_cpp_type(declaring_tag) + }; + + let has_template_args = self + .cpp_template + .as_ref() + .is_some_and(|t| !t.names.is_empty()); + + // don't emit method size structs for generic methods + if template.is_none() && !has_template_args && !is_generic_method_inst { + self.nonmember_implementations + .push(Arc::new(CppNonMember::SizeStruct( + CppMethodSizeStruct { + ret_ty: method_decl.return_type.clone(), + cpp_method_name: method_decl.cpp_name.clone(), + method_name: m_name.to_string(), + declaring_type_name: method_impl.declaring_cpp_full_name.clone(), + declaring_classof_call, + method_info_lines, + method_info_var: METHOD_INFO_VAR_NAME.to_string(), + instance: method_decl.instance, + params: method_decl.parameters.clone(), + declaring_template: self.cpp_template.clone(), + template: template.clone(), + generic_literals: resolved_generic_types, + method_data: CppMethodData { + addrs: *addr, + estimated_size: size, + }, + interface_clazz_of: interface_declaring_cpp_type + .map(|d| d.classof_cpp_name()) + .unwrap_or_else(|| format!("Bad stuff happened {declaring_td:?}")), + is_final, + slot: method.method_data.slot, + } + .into(), + ))); + } + } + + // TODO: Revise this + const ALLOW_GENERIC_METHOD_STUBS_IMPL: bool = true; + // If a generic instantiation or not a template + if !method_stub || ALLOW_GENERIC_METHOD_STUBS_IMPL { + self.implementations + .push(CppMember::MethodImpl(method_impl).into()); + } + + if !is_generic_method_inst { + self.declarations + .push(CppMember::MethodDecl(method_decl).into()); + } + } + + pub fn classof_cpp_name(&self) -> String { + format!( + "::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class<{}>::get", + self.cpp_name_components.combine_all() + ) + } + + fn create_size_assert(&mut self) { + // FIXME: make this work with templated types that either: have a full template (complete instantiation), or only require a pointer (size should be stable) + // for now, skip templated types + if self.cpp_template.is_some() { + return; + } + + if let Some(size) = self.size_info.as_ref().map(|s| s.instance_size) { + let cpp_name: String = self.cpp_name_components.remove_pointer().combine_all(); + + assert!(!cpp_name.trim().is_empty(), "CPP Name cannot be empty!"); + + let assert = CppStaticAssert { + condition: format!("::cordl_internals::size_check_v<{cpp_name}, 0x{size:x}>"), + message: Some("Size mismatch!".to_string()), + }; + + self.nonmember_declarations + .push(Arc::new(CppNonMember::CppStaticAssert(assert))); + } else { + todo!("Why does this type not have a valid size??? {self:?}"); + } + } + + /// + /// add missing size for type + /// + fn create_size_padding(&mut self, size_info: Option) { + // // get type metadata size + let Some(size_info) = size_info else { + return; + }; + + // // ignore types that aren't sized + if size_info.instance_size == 0 || size_info.instance_size == u32::MAX { + return; + } + + // // if the size matches what we calculated, we're fine + // if metadata_size.instance_size == calculated_size { + // return; + // } + // let remaining_size = metadata_size.instance_size.abs_diff(calculated_size); + + // for all types, the size il2cpp metadata says the type should be, for generics this is calculated though + let metadata_size_instance = size_info.instance_size; + + // align the calculated size to the next multiple of natural_alignment, similiar to what happens when clang compiles our generated code + // this comes down to adding our size, and removing any bits that make it more than the next multiple of alignment + #[cfg(feature = "il2cpp_v29")] + let aligned_calculated_size = match size_info.natural_alignment as u32 { + 0 => size_info.calculated_instance_size, + alignment => (size_info.calculated_instance_size + alignment) & !(alignment - 1), + }; + #[cfg(feature = "il2cpp_v31")] + let aligned_calculated_size = size_info.calculated_instance_size; + + // return if calculated layout size == metadata size + if aligned_calculated_size == metadata_size_instance { + return; + } + + let remaining_size = metadata_size_instance.abs_diff(size_info.calculated_instance_size); + + // pack the remaining size to fit the packing of the type + let closest_packing = |size: u32| match size { + 0 => 0, + 1 => 1, + 2 => 2, + 3 => 4, + 4 => 4, + _ => 8, + }; + + let packing = self + .packing + .unwrap_or_else(|| closest_packing(size_info.calculated_instance_size)); + let packed_remaining_size = match packing == 0 { + true => remaining_size, + false => remaining_size & !(packing - 1), + }; + + // if the packed remaining size ends up being 0, don't emit padding + if packed_remaining_size == 0 { + return; + } + + self.declarations.push( + CppMember::FieldDecl(CppFieldDecl { + cpp_name: format!("_cordl_size_padding[0x{packed_remaining_size:x}]").to_string(), + field_ty: "uint8_t".into(), + offset: Some(size_info.instance_size), + instance: true, + readonly: false, + const_expr: false, + value: None, + brief_comment: Some(format!( + "Size padding 0x{:x} - 0x{:x} = 0x{remaining_size:x}, packed as 0x{packed_remaining_size:x}", + metadata_size_instance, size_info.calculated_instance_size + )), + is_private: false, + }) + .into(), + ); + } + + fn create_ref_size(&mut self) { + if let Some(size) = self.size_info.as_ref().map(|s| s.instance_size) { + self.declarations.push( + CppMember::FieldDecl(CppFieldDecl { + cpp_name: REFERENCE_TYPE_WRAPPER_SIZE.to_string(), + field_ty: "auto".to_string(), + offset: None, + instance: false, + readonly: false, + const_expr: true, + value: Some(format!("0x{size:x}")), + brief_comment: Some("The size of the true reference type".to_string()), + is_private: false, + }) + .into(), + ); + + // here we push an instance field like uint8_t __fields[total_size - base_size] to make sure ref types are the exact size they should be + let inherits = self.get_inherits().collect_vec(); + let fixup_size = match inherits.first() { + Some(base_type) => format!("0x{size:x} - sizeof({base_type})"), + None => format!("0x{size:x}"), + }; + + self.declarations.push( + CppMember::FieldDecl(CppFieldDecl { + cpp_name: format!("{REFERENCE_TYPE_FIELD_SIZE}[{fixup_size}]"), + field_ty: "uint8_t".to_string(), + offset: None, + instance: true, + readonly: false, + const_expr: false, + value: Some("".into()), + brief_comment: Some( + "The size this ref type adds onto its base type, may evaluate to 0" + .to_string(), + ), + is_private: false, + }) + .into(), + ); + } else { + todo!("Why does this type not have a valid size??? {:?}", self); + } + } + + fn create_enum_backing_type_constant( + &mut self, + backing_type: ResolvedType, + name_resolver: &CppNameResolver, + _config: &CppGenerationConfig, + ) { + let enum_base = name_resolver + .resolve_name(self, &backing_type, TypeUsage::TypeName, true) + .remove_pointer() + .combine_all(); + + self.declarations.push( + CppMember::CppUsingAlias(CppUsingAlias { + alias: __CORDL_BACKING_ENUM_TYPE.to_string(), + result: enum_base, + template: None, + }) + .into(), + ); + } + + fn create_enum_wrapper( + &mut self, + backing_type: ResolvedType, + name_resolver: &CppNameResolver, + _config: &CppGenerationConfig, + ) { + let metadata = name_resolver.cordl_metadata; + + let tdi: TypeDefinitionIndex = self.self_tag.get_tdi(); + let t = tdi.get_type_definition(metadata.metadata); + + let unwrapped_name = format!("__{}_Unwrapped", self.cpp_name()); + + let enum_base = name_resolver + .resolve_name(self, &backing_type, TypeUsage::TypeName, true) + .remove_pointer() + .combine_all(); + + let enum_entries = t + .fields(metadata.metadata) + .iter() + .enumerate() + .map(|(i, field)| { + let field_index = FieldIndex::new(t.field_start.index() + i as u32); + + (field_index, field) + }) + .filter_map(|(field_index, field)| { + let f_type = metadata + .metadata_registration + .types + .get(field.type_index as usize) + .unwrap(); + + f_type.is_static().then(|| { + // enums static fields are always the enum values + let f_name = field.name(metadata.metadata); + let value = CsType::field_default_value(metadata, field_index) + .expect("Enum without value!") + .to_string(); + + // prepend enum name with __E_ to prevent accidentally creating enum values that are reserved for builtin macros + format!("__E_{f_name} = {value},") + }) + }) + .map(|s| -> CppMember { CppMember::CppLine(s.into()) }); + + let nested_struct = CppNestedStruct { + base_type: Some(enum_base.clone()), + declaring_name: unwrapped_name.clone(), + is_class: false, + is_enum: true, + is_private: false, + declarations: enum_entries.map(Rc::new).collect(), + brief_comment: Some(format!("Nested struct {unwrapped_name}")), + packing: None, + }; + self.declarations + .push(CppMember::NestedStruct(nested_struct).into()); + + let operator_body = format!("return static_cast<{unwrapped_name}>(this->value__);"); + let unwrapped_operator_decl = CppMethodDecl { + cpp_name: Default::default(), + instance: true, + return_type: unwrapped_name, + + brief: Some("Conversion into unwrapped enum value".to_string()), + body: Some(vec![Arc::new(CppLine::make(operator_body))]), + is_const: true, + is_constexpr: true, + is_virtual: false, + is_explicit_operator: false, + is_implicit_operator: true, + is_no_except: true, + parameters: vec![], + prefix_modifiers: vec![], + suffix_modifiers: vec![], + template: None, + is_inline: true, + }; + // convert to proper backing type + let backing_operator_body = format!("return static_cast<{enum_base}>(this->value__);"); + let backing_operator_decl = CppMethodDecl { + brief: Some("Conversion into unwrapped enum value".to_string()), + return_type: enum_base, + body: Some(vec![Arc::new(CppLine::make(backing_operator_body))]), + is_explicit_operator: true, + ..unwrapped_operator_decl.clone() + }; + + self.declarations + .push(CppMember::MethodDecl(unwrapped_operator_decl).into()); + self.declarations + .push(CppMember::MethodDecl(backing_operator_decl).into()); + } + + fn create_valuetype_field_wrapper(&mut self) { + if self.size_info.is_none() { + todo!("Why does this type not have a valid size??? {:?}", self); + } + + let size = self.size_info.as_ref().map(|s| s.instance_size).unwrap(); + + self.requirements.needs_byte_include(); + self.declarations.push( + CppMember::FieldDecl(CppFieldDecl { + cpp_name: VALUE_TYPE_WRAPPER_SIZE.to_string(), + field_ty: "auto".to_string(), + offset: None, + instance: false, + readonly: false, + const_expr: true, + value: Some(format!("0x{size:x}")), + brief_comment: Some("The size of the true value type".to_string()), + is_private: false, + }) + .into(), + ); + } + fn create_valuetype_constructor( + &mut self, + fields: &[CsField], + name_resolver: &CppNameResolver, + config: &CppGenerationConfig, + ) { + let instance_fields = fields + .iter() + .filter_map(|field| { + // ignore statics or constants + if field.is_const || !field.instance { + return None; + } + + let field_il2cpp_ty = field.field_ty.get_type(name_resolver.cordl_metadata); + + let f_type_cpp_name = name_resolver + .resolve_name( + self, + &field.field_ty, + TypeUsage::Field, + field_il2cpp_ty.valuetype, + ) + .combine_all(); + + // Get the inner type of a Generic Inst + // e.g ReadOnlySpan -> ReadOnlySpan + let def_value = CsValue::Null.to_string(); + + let f_cpp_name = config.name_cpp(&field.name); + + Some(CppParam { + name: f_cpp_name, + ty: f_type_cpp_name, + modifiers: "".to_string(), + // no default value for first param + def_value: Some(def_value), + }) + }) + .collect_vec(); + + if instance_fields.is_empty() { + return; + } + // Maps into the first parent -> "" + // so then Parent() + let base_ctor = self.parent.as_ref().map(|s| (s.clone(), "".to_string())); + + let body: Vec> = instance_fields + .iter() + .map(|p| { + let name = &p.name; + CppLine::make(format!("this->{name} = {name};")) + }) + .map(Arc::new) + // Why is this needed? _sigh_ + .map(|arc| -> Arc { arc }) + .collect_vec(); + + let params_no_def = instance_fields + .iter() + .cloned() + .map(|mut c| { + c.def_value = None; + c + }) + .collect_vec(); + + let constructor_decl = CppConstructorDecl { + cpp_name: self.cpp_name().clone(), + template: None, + is_constexpr: true, + is_explicit: false, + is_default: false, + is_no_except: true, + is_delete: false, + is_protected: false, + + base_ctor, + initialized_values: HashMap::new(), + // initialize values with params + // initialized_values: instance_fields + // .iter() + // .map(|p| (p.name.to_string(), p.name.to_string())) + // .collect(), + parameters: params_no_def, + brief: None, + body: None, + }; + + let method_impl_template = if self + .cpp_template + .as_ref() + .is_some_and(|c| !c.names.is_empty()) + { + self.cpp_template.clone() + } else { + None + }; + + let constructor_impl = CppConstructorImpl { + body, + template: method_impl_template, + parameters: instance_fields, + declaring_full_name: self.cpp_name_components.remove_pointer().combine_all(), + ..constructor_decl.clone().into() + }; + + self.declarations + .push(CppMember::ConstructorDecl(constructor_decl).into()); + self.implementations + .push(CppMember::ConstructorImpl(constructor_impl).into()); + } + + fn create_valuetype_default_constructors(&mut self) { + // create the various copy and move ctors and operators + let cpp_name = self.cpp_name(); + let wrapper = format!("{VALUE_WRAPPER_TYPE}<{VALUE_TYPE_WRAPPER_SIZE}>::instance"); + + let move_ctor = CppConstructorDecl { + cpp_name: cpp_name.clone(), + parameters: vec![CppParam { + ty: cpp_name.clone(), + name: "".to_string(), + modifiers: "&&".to_string(), + def_value: None, + }], + template: None, + is_constexpr: true, + is_explicit: false, + is_default: true, + is_no_except: false, + is_delete: false, + is_protected: false, + base_ctor: None, + initialized_values: Default::default(), + brief: None, + body: None, + }; + + let copy_ctor = CppConstructorDecl { + cpp_name: cpp_name.clone(), + parameters: vec![CppParam { + ty: cpp_name.clone(), + name: "".to_string(), + modifiers: "const &".to_string(), + def_value: None, + }], + template: None, + is_constexpr: true, + is_explicit: false, + is_default: true, + is_no_except: false, + is_delete: false, + is_protected: false, + base_ctor: None, + initialized_values: Default::default(), + brief: None, + body: None, + }; + + let move_operator_eq = CppMethodDecl { + cpp_name: "operator=".to_string(), + return_type: format!("{cpp_name}&"), + parameters: vec![CppParam { + ty: cpp_name.clone(), + name: "o".to_string(), + modifiers: "&&".to_string(), + def_value: None, + }], + instance: true, + template: None, + suffix_modifiers: vec![], + prefix_modifiers: vec![], + is_virtual: false, + is_constexpr: true, + is_const: false, + is_no_except: true, + is_implicit_operator: false, + is_explicit_operator: false, + + is_inline: false, + brief: None, + body: Some(vec![ + Arc::new(CppLine::make(format!( + "this->{wrapper} = std::move(o.{wrapper});" + ))), + Arc::new(CppLine::make("return *this;".to_string())), + ]), + }; + + let copy_operator_eq = CppMethodDecl { + cpp_name: "operator=".to_string(), + return_type: format!("{cpp_name}&"), + parameters: vec![CppParam { + ty: cpp_name.clone(), + name: "o".to_string(), + modifiers: "const &".to_string(), + def_value: None, + }], + instance: true, + template: None, + suffix_modifiers: vec![], + prefix_modifiers: vec![], + is_virtual: false, + is_constexpr: true, + is_const: false, + is_no_except: true, + is_implicit_operator: false, + is_explicit_operator: false, + + is_inline: false, + brief: None, + body: Some(vec![ + Arc::new(CppLine::make(format!("this->{wrapper} = o.{wrapper};"))), + Arc::new(CppLine::make("return *this;".to_string())), + ]), + }; + + self.declarations + .push(CppMember::ConstructorDecl(move_ctor).into()); + self.declarations + .push(CppMember::ConstructorDecl(copy_ctor).into()); + self.declarations + .push(CppMember::MethodDecl(move_operator_eq).into()); + self.declarations + .push(CppMember::MethodDecl(copy_operator_eq).into()); + } + + fn create_ref_default_constructor(&mut self) { + let cpp_name = self.cpp_name().clone(); + + let cs_name = self.name().clone(); + + // Skip if System.ValueType or System.Enum + if self.namespace() == "System" && (cs_name == "ValueType" || cs_name == "Enum") { + return; + } + + let default_ctor = CppConstructorDecl { + cpp_name: cpp_name.clone(), + parameters: vec![], + template: None, + is_constexpr: true, + is_explicit: false, + is_default: true, + is_no_except: true, + is_delete: false, + is_protected: true, + + base_ctor: None, + initialized_values: HashMap::new(), + brief: Some("Default ctor for custom type constructor invoke".to_string()), + body: None, + }; + let copy_ctor = CppConstructorDecl { + cpp_name: cpp_name.clone(), + parameters: vec![CppParam { + name: "".to_string(), + modifiers: " const&".to_string(), + ty: cpp_name.clone(), + def_value: None, + }], + template: None, + is_constexpr: true, + is_explicit: false, + is_default: true, + is_no_except: true, + is_delete: false, + is_protected: false, + + base_ctor: None, + initialized_values: HashMap::new(), + brief: None, + body: None, + }; + let move_ctor = CppConstructorDecl { + cpp_name: cpp_name.clone(), + parameters: vec![CppParam { + name: "".to_string(), + modifiers: "&&".to_string(), + ty: cpp_name.clone(), + def_value: None, + }], + template: None, + is_constexpr: true, + is_explicit: false, + is_default: true, + is_no_except: true, + is_delete: false, + is_protected: false, + + base_ctor: None, + initialized_values: HashMap::new(), + brief: None, + body: None, + }; + + self.declarations + .push(CppMember::ConstructorDecl(default_ctor).into()); + self.declarations + .push(CppMember::ConstructorDecl(copy_ctor).into()); + self.declarations + .push(CppMember::ConstructorDecl(move_ctor).into()); + + // // Delegates and such are reference types with no inheritance + // if self.inherit.is_empty() { + // return; + // } + + // let base_type = self + // .inherit + // .get(0) + // .expect("No parent for reference type?"); + + // self.declarations.push( + // CppMember::ConstructorDecl(CppConstructorDecl { + // cpp_name: cpp_name.clone(), + // parameters: vec![CppParam { + // name: "ptr".to_string(), + // modifiers: "".to_string(), + // ty: "void*".to_string(), + // def_value: None, + // }], + // template: None, + // is_constexpr: true, + // is_explicit: true, + // is_default: false, + // is_no_except: true, + // is_delete: false, + // is_protected: false, + + // base_ctor: Some((base_type.clone(), "ptr".to_string())), + // initialized_values: HashMap::new(), + // brief: None, + // body: Some(vec![]), + // }) + // .into(), + // ); + } + fn make_interface_constructors(&mut self) { + let cpp_name = self.cpp_name().clone(); + + let base_type = self.parent.as_ref().expect("No parent for interface type?"); + + self.declarations.push( + CppMember::ConstructorDecl(CppConstructorDecl { + cpp_name: cpp_name.clone(), + parameters: vec![CppParam { + name: "ptr".to_string(), + modifiers: "".to_string(), + ty: "void*".to_string(), + def_value: None, + }], + template: None, + is_constexpr: true, + is_explicit: true, + is_default: false, + is_no_except: true, + is_delete: false, + is_protected: false, + + base_ctor: Some((base_type.clone(), "ptr".to_string())), + initialized_values: HashMap::new(), + brief: None, + body: Some(vec![]), + }) + .into(), + ); + } + fn create_ref_default_operators(&mut self) { + let cpp_name = self.cpp_name(); + + // Skip if System.ValueType or System.Enum + if self.namespace() == "System" + && (self.cpp_name() == "ValueType" || self.cpp_name() == "Enum") + { + return; + } + + // Delegates and such are reference types with no inheritance + if self.get_inherits().count() > 0 { + return; + } + + self.declarations.push( + CppMember::CppLine(CppLine { + line: format!( + " + constexpr {cpp_name}& operator=(std::nullptr_t) noexcept {{ + this->{REFERENCE_WRAPPER_INSTANCE_NAME} = nullptr; + return *this; + }}; + + constexpr {cpp_name}& operator=(void* o) noexcept {{ + this->{REFERENCE_WRAPPER_INSTANCE_NAME} = o; + return *this; + }}; + + constexpr {cpp_name}& operator=({cpp_name}&& o) noexcept = default; + constexpr {cpp_name}& operator=({cpp_name} const& o) noexcept = default; + " + ), + }) + .into(), + ); + } + + fn delete_move_ctor(&mut self) { + let t = &self.cpp_name_components.name; + + let move_ctor = CppConstructorDecl { + cpp_name: t.clone(), + parameters: vec![CppParam { + def_value: None, + modifiers: "&&".to_string(), + name: "".to_string(), + ty: t.clone(), + }], + template: None, + is_constexpr: false, + is_explicit: false, + is_default: false, + is_no_except: false, + is_protected: false, + is_delete: true, + base_ctor: None, + initialized_values: Default::default(), + brief: Some("delete move ctor to prevent accidental deref moves".to_string()), + body: None, + }; + + self.declarations + .push(CppMember::ConstructorDecl(move_ctor).into()); + } + + fn delete_copy_ctor(&mut self) { + let t = &self.cpp_name_components.name; + + let move_ctor = CppConstructorDecl { + cpp_name: t.clone(), + parameters: vec![CppParam { + def_value: None, + modifiers: "const&".to_string(), + name: "".to_string(), + ty: t.clone(), + }], + template: None, + is_constexpr: false, + is_explicit: false, + is_default: false, + is_no_except: false, + is_delete: true, + is_protected: false, + base_ctor: None, + initialized_values: Default::default(), + brief: Some("delete copy ctor to prevent accidental deref copies".to_string()), + body: None, + }; + + self.declarations + .push(CppMember::ConstructorDecl(move_ctor).into()); + } + + fn add_default_ctor(&mut self, protected: bool) { + let t = &self.cpp_name_components.name; + + let default_ctor_decl = CppConstructorDecl { + cpp_name: t.clone(), + parameters: vec![], + template: None, + is_constexpr: true, + is_explicit: false, + is_default: false, + is_no_except: false, + is_delete: false, + is_protected: protected, + base_ctor: None, + initialized_values: Default::default(), + brief: Some("default ctor".to_string()), + body: None, + }; + + let default_ctor_impl = CppConstructorImpl { + body: vec![], + declaring_full_name: self.cpp_name_components.remove_pointer().combine_all(), + template: self.cpp_template.clone(), + ..default_ctor_decl.clone().into() + }; + + self.declarations + .push(CppMember::ConstructorDecl(default_ctor_decl).into()); + + self.implementations + .push(CppMember::ConstructorImpl(default_ctor_impl).into()); + } + + fn add_type_index_member(&mut self) { + let tdi: TypeDefinitionIndex = self.self_tag.get_tdi(); + + let il2cpp_metadata_type_index = CppFieldDecl { + cpp_name: "__IL2CPP_TYPE_DEFINITION_INDEX".into(), + field_ty: "uint32_t".into(), + offset: None, + instance: false, + readonly: true, + const_expr: true, + value: Some(tdi.index().to_string()), + brief_comment: Some("IL2CPP Metadata Type Index".into()), + is_private: false, + }; + + self.declarations + .push(CppMember::FieldDecl(il2cpp_metadata_type_index).into()); + } + + fn delete_default_ctor(&mut self) { + let t = &self.cpp_name_components.name; + + let default_ctor = CppConstructorDecl { + cpp_name: t.clone(), + parameters: vec![], + template: None, + is_constexpr: false, + is_explicit: false, + is_default: false, + is_no_except: false, + is_delete: true, + is_protected: false, + base_ctor: None, + initialized_values: Default::default(), + brief: Some( + "delete default ctor to prevent accidental value type instantiations of ref types" + .to_string(), + ), + body: None, + }; + + self.declarations + .push(CppMember::ConstructorDecl(default_ctor).into()); + } + + fn create_ref_constructor( + &mut self, + _method: &CsMethod, + m_params: &[CppParam], + template: &Option, + ) { + if self.is_value_type || self.is_enum_type { + return; + } + + let params_no_default = m_params + .iter() + .cloned() + .map(|mut c| { + c.def_value = None; + c + }) + .collect_vec(); + + let ty_full_cpp_name = self.cpp_name_components.combine_all(); + + let decl: CppMethodDecl = CppMethodDecl { + cpp_name: "New_ctor".into(), + return_type: ty_full_cpp_name.clone(), + parameters: params_no_default, + template: template.clone(), + body: None, // TODO: + brief: None, + is_no_except: false, + is_constexpr: false, + instance: false, + is_const: false, + is_implicit_operator: false, + is_explicit_operator: false, + + is_virtual: false, + is_inline: true, + prefix_modifiers: vec![], + suffix_modifiers: vec![], + }; + + // To avoid trailing ({},) + let base_ctor_params = CppParam::params_names(&decl.parameters).join(", "); + + let allocate_call = format!( + "THROW_UNLESS(::il2cpp_utils::NewSpecific<{ty_full_cpp_name}>({base_ctor_params}))" + ); + + let declaring_template = self + .cpp_template + .as_ref() + .is_some_and(|t| !t.names.is_empty()) + .then(|| self.cpp_template.clone()) + .flatten(); + + let cpp_constructor_impl = CppMethodImpl { + body: vec![Arc::new(CppLine::make(format!("return {allocate_call};")))], + + declaring_cpp_full_name: self.cpp_name_components.remove_pointer().combine_all(), + parameters: m_params.to_vec(), + template: declaring_template, + ..decl.clone().into() + }; + + self.implementations + .push(CppMember::MethodImpl(cpp_constructor_impl).into()); + + self.declarations.push(CppMember::MethodDecl(decl).into()); + } + + pub fn get_inherits(&self) -> impl Iterator { + std::iter::once(&self.parent) + .flatten() + .chain(self.interfaces.iter()) + } + + pub fn cpp_namespace(&self) -> String { + self.cpp_name_components + .namespace + .clone() + .unwrap_or("GlobalNamespace".to_owned()) + } + + pub fn namespace(&self) -> String { + self.cs_name_components + .namespace + .clone() + .unwrap_or("GlobalNamespace".to_owned()) + } + + pub fn cpp_name(&self) -> &std::string::String { + &self.cpp_name_components.name + } + + pub fn name(&self) -> &String { + &self.cpp_name_components.name + } +} + +impl ToString for CsValue { + fn to_string(&self) -> String { + match self { + CsValue::String(s) => format!("\"{s}\""), + CsValue::Bool(v) => match v { + true => "true", + false => "false", + } + .to_string(), + CsValue::U8(x) => format!("static_cast(0x{x:x}u)"), + CsValue::U16(x) => format!("static_cast(0x{x:x}u)"), + CsValue::U32(x) => format!("static_cast(0x{x:x}u)"), + CsValue::U64(x) => format!("static_cast(0x{x:x}u)"), + CsValue::I8(x) => format!("static_cast(0x{x:x})"), + CsValue::I16(x) => format!("static_cast(0x{x:x})"), + CsValue::I32(x) => format!("static_cast(0x{x:x})"), + CsValue::I64(x) => format!("static_cast(0x{x:x})"), + CsValue::F32(f) => { + if *f == f32::INFINITY { + return "INFINITY".to_owned(); + } + if *f == f32::NEG_INFINITY { + return "-INFINITY".to_owned(); + } + if f.is_nan() { + return "NAN".to_owned(); + } + // make it include at least one decimal place + + format!("static_cast({f:1}f)") + } + CsValue::F64(f) => { + if *f == f64::INFINITY { + return "INFINITY".to_owned(); + } + if *f == f64::NEG_INFINITY { + return "-INFINITY".to_owned(); + } + if f.is_nan() { + return "NAN".to_owned(); + } + + format!("static_cast({f:1})") + } + CsValue::Object(_bytes) => todo!(), + CsValue::ValueType(_bytes) => todo!(), + CsValue::Null => "{}".to_string(), + } + } +} diff --git a/src/handlers/comment_omit.rs b/src/generate/cpp/handlers/comment_omit.rs similarity index 85% rename from src/handlers/comment_omit.rs rename to src/generate/cpp/handlers/comment_omit.rs index 5ebb208b1..bee66f823 100644 --- a/src/handlers/comment_omit.rs +++ b/src/generate/cpp/handlers/comment_omit.rs @@ -1,18 +1,16 @@ use color_eyre::Result; use log::info; -use std::rc::Rc; +use std::sync::Arc; -use crate::generate::{ - context_collection::CppContextCollection, - cs_context_collection::CsContextCollection, - members::{CppMember, CppNonMember}, +use crate::generate::cpp::{ + cpp_context_collection::CppContextCollection, + cpp_members::{CppMember, CppNonMember}, }; pub fn remove_coments(context_collection: &mut CppContextCollection) -> Result<()> { info!("Removing comments"); context_collection - .get_mut_cpp_context_collection() .get_mut() .values_mut() .flat_map(|cpp_context| cpp_context.typedef_types.values_mut()) @@ -21,7 +19,7 @@ pub fn remove_coments(context_collection: &mut CppContextCollection) -> Result<( .declarations .iter_mut() .try_for_each(|d| -> Result<()> { - match Rc::make_mut(d) { + match Arc::make_mut(d) { CppMember::FieldDecl(cpp_field_decl) => { cpp_field_decl.brief_comment = None; } @@ -45,7 +43,7 @@ pub fn remove_coments(context_collection: &mut CppContextCollection) -> Result<( .nonmember_declarations .iter_mut() .try_for_each(|d| -> Result<()> { - match Rc::make_mut(d) { + match Arc::make_mut(d) { CppNonMember::CppStaticAssert(static_asert) => { static_asert.condition = "".to_string(); } diff --git a/src/handlers/il2cpp_internals.rs b/src/generate/cpp/handlers/il2cpp_internals.rs similarity index 92% rename from src/handlers/il2cpp_internals.rs rename to src/generate/cpp/handlers/il2cpp_internals.rs index df99ff951..989a4b795 100644 --- a/src/handlers/il2cpp_internals.rs +++ b/src/generate/cpp/handlers/il2cpp_internals.rs @@ -6,10 +6,13 @@ use std::{ }; use crate::generate::{ - cpp_type::CppType, - cs_type::VALUE_TYPE_WRAPPER_SIZE, - members::{CppConstructorDecl, CppLine, CppMember, CppMethodDecl, CppParam}, - metadata::{Il2cppFullName, Metadata}, + cpp::{ + cpp_context_collection::CppContextCollection, + cpp_members::{CppConstructorDecl, CppLine, CppMember, CppMethodDecl, CppParam}, + cpp_type::{CppType, VALUE_TYPE_WRAPPER_SIZE}, + }, + cs_type_tag::CsTypeTag, + metadata::{CordlMetadata, Il2cppFullName}, }; static EQUIVALENTS: LazyLock> = LazyLock::new(|| { @@ -93,8 +96,10 @@ static EQUIVALENTS: LazyLock> = LazyLock::new(|| { ), ]) }); - -pub fn register_il2cpp_types(metadata: &mut Metadata) -> Result<()> { +pub fn register_il2cpp_types( + metadata: &mut CordlMetadata, + cpp_context_collection: &mut CppContextCollection, +) -> Result<()> { info!("Registering il2cpp type handler!"); for (cordl_t, il2cpp_t) in EQUIVALENTS.iter() { @@ -104,13 +109,14 @@ pub fn register_il2cpp_types(metadata: &mut Metadata) -> Result<()> { let il2cpp_name = Il2cppFullName(cordl_t_ns, cordl_t_name); let cordl_tdi = metadata.name_to_tdi.get(&il2cpp_name); - - match cordl_tdi { - Some(cordl_tdi) => { - metadata.custom_type_handler.insert( - *cordl_tdi, - Box::new(|cpp_type| il2cpp_alias_handler(cpp_type, cordl_t, il2cpp_t)), - ); + let cpp_type = cordl_tdi.and_then(|cordl_tdi| { + let tag = CsTypeTag::TypeDefinitionIndex(*cordl_tdi); + cpp_context_collection.get_cpp_type_mut(tag) + }); + + match cpp_type { + Some(cpp_type) => { + il2cpp_alias_handler(cpp_type, cordl_t, il2cpp_t); } None => { warn!("Could not find TDI for {cordl_t}"); diff --git a/src/handlers/mod.rs b/src/generate/cpp/handlers/mod.rs similarity index 100% rename from src/handlers/mod.rs rename to src/generate/cpp/handlers/mod.rs diff --git a/src/handlers/object.rs b/src/generate/cpp/handlers/object.rs similarity index 56% rename from src/handlers/object.rs rename to src/generate/cpp/handlers/object.rs index 04df96509..225bd1529 100644 --- a/src/handlers/object.rs +++ b/src/generate/cpp/handlers/object.rs @@ -1,21 +1,19 @@ -use color_eyre::Result; +use color_eyre::{eyre::ContextCompat, Result}; use log::info; use crate::generate::{ - cpp_type::CppType, - cs_type::IL2CPP_OBJECT_TYPE, - members::CppMember, - metadata::{Il2cppFullName, Metadata}, + cpp::{ + cpp_context_collection::CppContextCollection, cpp_members::CppMember, + cpp_name_resolver::IL2CPP_OBJECT_TYPE, cpp_type::CppType, + }, + cs_type_tag::CsTypeTag, + metadata::{CordlMetadata, Il2cppFullName}, }; -pub fn register_system(metadata: &mut Metadata) -> Result<()> { - info!("Registering system handler!"); - register_system_object_type_handler(metadata)?; - - Ok(()) -} - -fn register_system_object_type_handler(metadata: &mut Metadata) -> Result<()> { +pub fn register_system( + metadata: &CordlMetadata, + cpp_context_collection: &mut CppContextCollection, +) -> Result<()> { info!("Registering System.Object handler!"); let system_object_tdi = metadata @@ -23,9 +21,12 @@ fn register_system_object_type_handler(metadata: &mut Metadata) -> Result<()> { .get(&Il2cppFullName("System", "Object")) .expect("No System.Object TDI found"); - metadata - .custom_type_handler - .insert(*system_object_tdi, Box::new(system_object_handler)); + let tag = CsTypeTag::TypeDefinitionIndex(*system_object_tdi); + + let cpp_type = cpp_context_collection + .get_cpp_type_mut(tag) + .wrap_err("No System.Object type found")?; + system_object_handler(cpp_type); Ok(()) } @@ -34,7 +35,7 @@ fn system_object_handler(cpp_type: &mut CppType) { info!("Found System.Object type, adding systemW!"); // clear inherit so that bs hook can dof include order shenanigans cpp_type.requirements.need_wrapper(); - cpp_type.inherit = vec![IL2CPP_OBJECT_TYPE.to_string()]; + cpp_type.parent = Some(IL2CPP_OBJECT_TYPE.to_string()); // Remove field because it does not size properly and is not necessary cpp_type diff --git a/src/handlers/unity.rs b/src/generate/cpp/handlers/unity.rs similarity index 52% rename from src/handlers/unity.rs rename to src/generate/cpp/handlers/unity.rs index 45e8669c9..51238cd7b 100644 --- a/src/handlers/unity.rs +++ b/src/generate/cpp/handlers/unity.rs @@ -1,28 +1,25 @@ -use brocolib::{global_metadata::TypeDefinitionIndex, runtime_metadata::Il2CppType}; -use color_eyre::Result; +use color_eyre::{eyre::ContextCompat, Result}; use log::info; -use std::{path::PathBuf, rc::Rc}; +use std::{path::PathBuf, sync::Arc}; use crate::{ - data::name_components::NameComponents, + data::{name_components::NameComponents, type_resolver::TypeUsage}, generate::{ - context_collection::CppContextCollection, - cpp_type::CppType, - members::{CppInclude, CppMember}, - metadata::{Il2cppFullName, Metadata, TypeUsage}, + cpp::{ + cpp_context_collection::CppContextCollection, + cpp_members::{CppInclude, CppMember}, + cpp_type::CppType, + }, + cs_type_tag::CsTypeTag, + metadata::{CordlMetadata, Il2cppFullName}, type_extensions::TypeDefinitionExtensions, }, }; -pub fn register_unity(metadata: &mut Metadata) -> Result<()> { - info!("Registering unity handler!"); - // register_unity_object_type_handler(metadata)?; - register_unity_object_type_resolve_handler(metadata)?; - - Ok(()) -} - -fn register_unity_object_type_resolve_handler(metadata: &mut Metadata) -> Result<()> { +pub fn register_unity( + metadata: &CordlMetadata, + cpp_context_collection: &mut CppContextCollection, +) -> Result<()> { info!("Registering UnityEngine.Object resolve handler!"); let unity_object_tdi = *metadata @@ -30,45 +27,25 @@ fn register_unity_object_type_resolve_handler(metadata: &mut Metadata) -> Result .get(&Il2cppFullName("UnityEngine", "Object")) .expect("No UnityEngine.Object TDI found"); - metadata - .custom_type_resolve_handler - .push(Box::new(move |a, b, c, d, e, f| { - unity_object_resolve_handler(a, b, c, d, e, f, unity_object_tdi) - })); - - Ok(()) -} - -fn register_unity_object_type_handler(metadata: &mut Metadata) -> Result<()> { - info!("Registering UnityEngine.Object handler!"); - - let unity_object_tdi = metadata - .name_to_tdi - .get(&Il2cppFullName("UnityEngine", "Object")) - .expect("No UnityEngine.Object TDI found"); + let tag = CsTypeTag::TypeDefinitionIndex(unity_object_tdi); - metadata - .custom_type_handler - .insert(*unity_object_tdi, Box::new(unity_object_handler)); + let cpp_type = cpp_context_collection + .get_cpp_type_mut(tag) + .wrap_err("No System.Object type found")?; + // unity_object_handler(cpp_type); Ok(()) } -fn unity_object_resolve_handler( +pub fn unity_object_resolve_handler( original: NameComponents, cpp_type: &CppType, - _ctx_collection: &CppContextCollection, - metadata: &Metadata, - _typ: &Il2CppType, + metadata: &CordlMetadata, typ_usage: TypeUsage, - unity_tdi: TypeDefinitionIndex, ) -> NameComponents { if !matches!( typ_usage, - TypeUsage::FieldName - | TypeUsage::PropertyName - | TypeUsage::GenericArg - | TypeUsage::ReturnType + TypeUsage::Field | TypeUsage::Property | TypeUsage::GenericArg | TypeUsage::ReturnType ) { return original; } @@ -76,7 +53,7 @@ fn unity_object_resolve_handler( let tdi = cpp_type.self_tag.get_tdi(); let td = &metadata.metadata.global_metadata.type_definitions[tdi]; - let unity_td = &metadata.metadata.global_metadata.type_definitions[unity_tdi]; + let unity_td = &metadata.metadata.global_metadata.type_definitions[metadata.unity_object_tdi]; if !td.is_assignable_to(unity_td, metadata.metadata) { return original; @@ -93,7 +70,7 @@ fn unity_object_resolve_handler( fn unity_object_handler(cpp_type: &mut CppType) { info!("Found UnityEngine.Object type, adding UnityW!"); - cpp_type.inherit = vec!["bs_hook::UnityW".to_owned()]; + cpp_type.parent = Some("bs_hook::UnityW".to_string()); let path = PathBuf::from(r"beatsaber-hook/shared/utils/unityw.hpp"); @@ -107,7 +84,7 @@ fn unity_object_handler(cpp_type: &mut CppType) { .iter_mut() .filter(|t| matches!(t.as_ref(), CppMember::ConstructorDecl(_))) .for_each(|d| { - let CppMember::ConstructorDecl(constructor) = Rc::get_mut(d).unwrap() else { + let CppMember::ConstructorDecl(constructor) = Arc::get_mut(d).unwrap() else { panic!() }; @@ -121,7 +98,7 @@ fn unity_object_handler(cpp_type: &mut CppType) { .iter_mut() .filter(|t| matches!(t.as_ref(), CppMember::ConstructorImpl(_))) .for_each(|d| { - let CppMember::ConstructorImpl(constructor) = Rc::get_mut(d).unwrap() else { + let CppMember::ConstructorImpl(constructor) = Arc::get_mut(d).unwrap() else { panic!() }; diff --git a/src/handlers/value_type.rs b/src/generate/cpp/handlers/value_type.rs similarity index 55% rename from src/handlers/value_type.rs rename to src/generate/cpp/handlers/value_type.rs index 8e7b1b35b..b78968470 100644 --- a/src/handlers/value_type.rs +++ b/src/generate/cpp/handlers/value_type.rs @@ -1,24 +1,21 @@ -use std::rc::Rc; +use std::sync::Arc; -use color_eyre::Result; +use color_eyre::{eyre::ContextCompat, Result}; use crate::generate::{ - cpp_type::CppType, - cs_type::{ENUM_WRAPPER_TYPE, VALUE_WRAPPER_TYPE}, - members::CppMember, - metadata::{Il2cppFullName, Metadata}, + cpp::{ + cpp_context_collection::CppContextCollection, cpp_members::CppMember, cpp_type::CppType, + }, + cs_type_tag::CsTypeTag, + metadata::{CordlMetadata, Il2cppFullName}, }; use log::info; -pub fn register_value_type(metadata: &mut Metadata) -> Result<()> { - info!("Registering value type handler!"); - register_value_type_object_handler(metadata)?; - - Ok(()) -} - -fn register_value_type_object_handler(metadata: &mut Metadata) -> Result<()> { +pub fn register_value_type( + metadata: &CordlMetadata, + cpp_context_collection: &mut CppContextCollection, +) -> Result<()> { info!("Registering System.ValueType handler!"); info!("Registering System.Enum handler!"); @@ -31,17 +28,25 @@ fn register_value_type_object_handler(metadata: &mut Metadata) -> Result<()> { .get(&Il2cppFullName("System", "Enum")) .expect("No System.ValueType TDI found"); - metadata - .custom_type_handler - .insert(*value_type_tdi, Box::new(value_type_handler)); - metadata - .custom_type_handler - .insert(*enum_type_tdi, Box::new(enum_type_handler)); + let value_type_tag = CsTypeTag::TypeDefinitionIndex(*value_type_tdi); + let enum_type_tag = CsTypeTag::TypeDefinitionIndex(*enum_type_tdi); + + let value_cpp_type = cpp_context_collection + .get_cpp_type_mut(value_type_tag) + .wrap_err("No System.Object type found")?; + + value_type_handler(value_cpp_type); + + let enum_cpp_type = cpp_context_collection + .get_cpp_type_mut(enum_type_tag) + .wrap_err("No System.Object type found")?; + + enum_type_handler(enum_cpp_type); Ok(()) } -fn unified_type_handler(cpp_type: &mut CppType, _base_ctor: &str) { +fn unified_type_handler(cpp_type: &mut CppType) { // We don't replace parent anymore // cpp_type.inherit = vec![base_ctor.to_string()]; @@ -54,7 +59,7 @@ fn unified_type_handler(cpp_type: &mut CppType, _base_ctor: &str) { .iter_mut() .filter(|t| matches!(t.as_ref(), CppMember::ConstructorDecl(_))) .for_each(|d| { - let CppMember::ConstructorDecl(constructor) = Rc::get_mut(d).unwrap() else { + let CppMember::ConstructorDecl(constructor) = Arc::get_mut(d).unwrap() else { panic!() }; @@ -78,31 +83,9 @@ fn unified_type_handler(cpp_type: &mut CppType, _base_ctor: &str) { } fn value_type_handler(cpp_type: &mut CppType) { info!("Found System.ValueType, removing inheritance!"); - unified_type_handler( - cpp_type, - format!( - "{VALUE_WRAPPER_TYPE}<0x{:x}>", - cpp_type - .size_info - .as_ref() - .map(|s| s.calculated_instance_size) - .unwrap() - ) - .as_str(), - ); + unified_type_handler(cpp_type); } fn enum_type_handler(cpp_type: &mut CppType) { info!("Found System.Enum type, removing inheritance!"); - unified_type_handler( - cpp_type, - format!( - "{ENUM_WRAPPER_TYPE}<0x{:x}>", - cpp_type - .size_info - .as_ref() - .map(|s| s.calculated_instance_size) - .unwrap() - ) - .as_str(), - ); + unified_type_handler(cpp_type); } diff --git a/src/generate/cpp/mod.rs b/src/generate/cpp/mod.rs new file mode 100644 index 000000000..812018ca4 --- /dev/null +++ b/src/generate/cpp/mod.rs @@ -0,0 +1,11 @@ +pub mod cpp_main; + +mod config; +mod cpp_context; +mod cpp_context_collection; +mod cpp_fields; +mod cpp_members; +mod cpp_members_serialize; +mod cpp_name_resolver; +mod cpp_type; +mod handlers; diff --git a/src/generate/cpp_type.rs b/src/generate/cpp_type.rs deleted file mode 100644 index 945107c5e..000000000 --- a/src/generate/cpp_type.rs +++ /dev/null @@ -1,491 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - io::Write, - rc::Rc, -}; - -use color_eyre::eyre::Context; - -use brocolib::global_metadata::{MethodIndex, TypeIndex}; -use itertools::Itertools; - -use crate::data::name_components::NameComponents; - -use super::{ - context_collection::CppContextCollection, - cpp_type_tag::CppTypeTag, - members::{CppForwardDeclare, CppInclude, CppMember, CppNonMember, CppTemplate}, - offsets::SizeInfo, - writer::{CppWriter, Sortable, Writable}, -}; - -pub const CORDL_TYPE_MACRO: &str = "CORDL_TYPE"; -pub const __CORDL_IS_VALUE_TYPE: &str = "__IL2CPP_IS_VALUE_TYPE"; -pub const __CORDL_BACKING_ENUM_TYPE: &str = "__CORDL_BACKING_ENUM_TYPE"; - -pub const CORDL_REFERENCE_TYPE_CONSTRAINT: &str = "::il2cpp_utils::il2cpp_reference_type"; -pub const CORDL_NUM_ENUM_TYPE_CONSTRAINT: &str = "::cordl_internals::is_or_is_backed_by"; -pub const CORDL_METHOD_HELPER_NAMESPACE: &str = "::cordl_internals"; - -#[derive(Debug, Clone, Default)] -pub struct CppTypeRequirements { - pub forward_declares: HashSet<(CppForwardDeclare, CppInclude)>, - - // Only value types or classes - pub required_def_includes: HashSet, - pub required_impl_includes: HashSet, - - // Lists both types we forward declare or include - pub depending_types: HashSet, -} - -impl CppTypeRequirements { - pub fn add_forward_declare(&mut self, cpp_data: (CppForwardDeclare, CppInclude)) { - // self.depending_types.insert(cpp_type.self_tag); - self.forward_declares.insert(cpp_data); - } - - pub fn add_def_include(&mut self, cpp_type: Option<&CppType>, cpp_include: CppInclude) { - if let Some(cpp_type) = cpp_type { - self.depending_types.insert(cpp_type.self_tag); - } - self.required_def_includes.insert(cpp_include); - } - pub fn add_impl_include(&mut self, cpp_type: Option<&CppType>, cpp_include: CppInclude) { - if let Some(cpp_type) = cpp_type { - self.depending_types.insert(cpp_type.self_tag); - } - self.required_impl_includes.insert(cpp_include); - } - pub fn add_dependency(&mut self, cpp_type: &CppType) { - self.depending_types.insert(cpp_type.self_tag); - } - pub fn add_dependency_tag(&mut self, tag: CppTypeTag) { - self.depending_types.insert(tag); - } -} - -// Represents all of the information necessary for a C++ TYPE! -// A C# type will be TURNED INTO this -#[derive(Debug, Clone)] -pub struct CppType { - pub self_tag: CppTypeTag, - pub nested: bool, - - pub(crate) prefix_comments: Vec, - - pub size_info: Option, - pub packing: Option, - - // Computed by TypeDefinition.full_name() - // Then fixed for generic types in CppContextCollection::make_generic_from/fill_generic_inst - pub cpp_name_components: NameComponents, - pub cs_name_components: NameComponents, - - pub declarations: Vec>, - pub implementations: Vec>, - /// Outside of the class declaration - /// Move to CsType/CppType? - pub nonmember_implementations: Vec>, - pub nonmember_declarations: Vec>, - - pub is_value_type: bool, - pub is_enum_type: bool, - pub is_reference_type: bool, - pub requirements: CppTypeRequirements, - - pub inherit: Vec, - pub cpp_template: Option, // Names of templates e.g T, TKey etc. - - /// contains the array of generic Il2CppType indexes - pub generic_instantiations_args_types: Option>, // GenericArg -> Instantiation Arg - pub method_generic_instantiation_map: HashMap>, // MethodIndex -> Generic Args - pub is_stub: bool, - pub is_interface: bool, - pub is_hidden: bool, - - pub nested_types: HashMap, -} - -impl CppTypeRequirements { - pub fn need_wrapper(&mut self) { - self.add_def_include( - None, - CppInclude::new_exact("beatsaber-hook/shared/utils/base-wrapper-type.hpp"), - ); - } - pub fn needs_int_include(&mut self) { - self.add_def_include(None, CppInclude::new_system("cstdint")); - } - pub fn needs_byte_include(&mut self) { - self.add_def_include(None, CppInclude::new_system("cstddef")); - } - pub fn needs_math_include(&mut self) { - self.add_def_include(None, CppInclude::new_system("cmath")); - } - pub fn needs_stringw_include(&mut self) { - self.add_def_include( - None, - CppInclude::new_exact("beatsaber-hook/shared/utils/typedefs-string.hpp"), - ); - } - pub fn needs_arrayw_include(&mut self) { - self.add_def_include( - None, - CppInclude::new_exact("beatsaber-hook/shared/utils/typedefs-array.hpp"), - ); - } - - pub fn needs_byref_include(&mut self) { - self.add_def_include( - None, - CppInclude::new_exact("beatsaber-hook/shared/utils/byref.hpp"), - ); - } - - pub fn needs_enum_include(&mut self) { - self.add_def_include( - None, - CppInclude::new_exact("beatsaber-hook/shared/utils/enum-type.hpp"), - ); - } - - pub fn needs_value_include(&mut self) { - self.add_def_include( - None, - CppInclude::new_exact("beatsaber-hook/shared/utils/value-type.hpp"), - ); - } -} - -impl CppType { - pub fn namespace(&self) -> String { - self.cs_name_components - .namespace - .clone() - .unwrap_or_default() - } - - pub fn cpp_namespace(&self) -> String { - self.cpp_name_components - .namespace - .clone() - .unwrap_or_default() - } - - pub fn name(&self) -> &String { - &self.cs_name_components.name - } - - pub fn cpp_name(&self) -> &String { - &self.cpp_name_components.name - } - - pub fn nested_types_flattened(&self) -> HashMap { - self.nested_types - .iter() - .flat_map(|(_, n)| n.nested_types_flattened()) - .chain(self.nested_types.iter().map(|(tag, n)| (*tag, n))) - .collect() - } - pub fn get_nested_type_mut(&mut self, tag: CppTypeTag) -> Option<&mut CppType> { - // sadly - if self.nested_types.get_mut(&tag).is_some() { - return self.nested_types.get_mut(&tag); - } - - self.nested_types.values_mut().find_map(|n| { - // Recurse - n.get_nested_type_mut(tag) - }) - } - pub fn get_nested_type(&self, tag: CppTypeTag) -> Option<&CppType> { - self.nested_types.get(&tag).or_else(|| { - self.nested_types.iter().find_map(|(_, n)| { - // Recurse - n.get_nested_type(tag) - }) - }) - } - - pub fn borrow_nested_type_mut( - &mut self, - ty: CppTypeTag, - context: &mut CppContextCollection, - func: &F, - ) -> bool - where - F: Fn(&mut CppContextCollection, CppType) -> CppType, - { - let nested_index = self.nested_types.get(&ty); - - match nested_index { - None => { - for nested_ty in self.nested_types.values_mut() { - if nested_ty.borrow_nested_type_mut(ty, context, func) { - return true; - } - } - - false - } - Some(old_nested_cpp_type) => { - // clone to avoid breaking il2cpp - let old_nested_cpp_type_tag = old_nested_cpp_type.self_tag; - let new_cpp_type = func(context, old_nested_cpp_type.clone()); - - // Remove old type, which may have a new type tag - self.nested_types.remove(&old_nested_cpp_type_tag); - self.nested_types - .insert(new_cpp_type.self_tag, new_cpp_type); - - true - } - } - } - - pub fn write_impl(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { - self.write_impl_internal(writer) - } - - pub fn write_def(&self, writer: &mut super::writer::CppWriter) -> color_eyre::Result<()> { - self.write_def_internal(writer, Some(&self.cpp_namespace())) - } - - pub fn write_impl_internal( - &self, - writer: &mut super::writer::CppWriter, - ) -> color_eyre::Result<()> { - self.nonmember_implementations - .iter() - .try_for_each(|d| d.write(writer))?; - - // Write all declarations within the type here - self.implementations - .iter() - .sorted_by(|a, b| a.sort_level().cmp(&b.sort_level())) - .try_for_each(|d| d.write(writer))?; - - // TODO: Figure out - self.nested_types - .iter() - .try_for_each(|(_tag, n)| n.write_impl_internal(writer))?; - - Ok(()) - } - - fn write_def_internal( - &self, - writer: &mut super::writer::CppWriter, - namespace: Option<&str>, - ) -> color_eyre::Result<()> { - self.prefix_comments - .iter() - .try_for_each(|pc| writeln!(writer, "// {pc}").context("Prefix comment"))?; - - let type_kind = match self.is_value_type { - true => "struct", - false => "class", - }; - - // Just forward declare - if !self.is_stub { - if let Some(n) = &namespace { - writeln!(writer, "namespace {n} {{")?; - writer.indent(); - } - - // Write type definition - if let Some(generic_args) = &self.cpp_template { - writeln!(writer, "// cpp template")?; - generic_args.write(writer)?; - } - writeln!(writer, "// Is value type: {}", self.is_value_type)?; - // writeln!( - // writer, - // "// Dependencies: {:?}", - // self.requirements - // .depending_types - // .iter() - // .sorted() - // .collect_vec() - // )?; - // writeln!(writer, "// Self: {:?}", self.self_tag)?; - - let clazz_name = self - .cpp_name_components - .formatted_name(self.generic_instantiations_args_types.is_some()); - - writeln!( - writer, - "// CS Name: {}", - self.cs_name_components.combine_all() - )?; - - // Type definition plus inherit lines - - // add_generic_inst sets template to [] - // if self.generic_instantiation_args.is_some() { - // writeln!(writer, "template<>")?; - // } - // start writing class - let cordl_hide = match self.is_hidden { - true => CORDL_TYPE_MACRO, - false => "", - }; - - if let Some(packing) = &self.packing { - writeln!(writer, "#pragma pack(push, {packing})")?; - } - - match self.inherit.is_empty() { - true => writeln!(writer, "{type_kind} {cordl_hide} {clazz_name} {{")?, - false => writeln!( - writer, - "{type_kind} {cordl_hide} {clazz_name} : {} {{", - self.inherit - .iter() - .map(|s| format!("public {s}")) - .join(", ") - )?, - } - - writer.indent(); - - // add public access - writeln!(writer, "public:")?; - - self.nested_types - .values() - .map(|t| (t, CppForwardDeclare::from_cpp_type(t))) - .unique_by(|(_, n)| n.clone()) - .try_for_each(|(t, nested_forward_declare)| { - writeln!( - writer, - "// nested type forward declare {} is stub {} {:?} {:?}\n//{:?}", - t.cs_name_components.combine_all(), - t.is_stub, - t.cs_name_components.generics, - t.generic_instantiations_args_types, - t.self_tag - )?; - nested_forward_declare.write(writer) - })?; - - self.nested_types - .iter() - .try_for_each(|(_, n)| -> color_eyre::Result<()> { - writer.indent(); - writeln!( - writer, - "// nested type {} is stub {}", - n.cs_name_components.combine_all(), - n.is_stub - )?; - n.write_def_internal(writer, None)?; - writer.dedent(); - Ok(()) - })?; - writeln!(writer, "// Declarations")?; - // Write all declarations within the type here - self.declarations - .iter() - .sorted_by(|a, b| a.as_ref().partial_cmp(b.as_ref()).unwrap()) - .sorted_by(|a, b| { - // fields and unions need to be sorted by offset to work correctly - - let a_offset = match a.as_ref() { - CppMember::FieldDecl(f) => f.offset, - CppMember::NestedUnion(u) => u.offset, - _ => u32::MAX, - }; - - let b_offset = match b.as_ref() { - CppMember::FieldDecl(f) => f.offset, - CppMember::NestedUnion(u) => u.offset, - _ => u32::MAX, - }; - - a_offset.cmp(&b_offset) - }) - // sort by sort level after fields have been ordered correctly - .sorted_by(|a, b| a.sort_level().cmp(&b.sort_level())) - .try_for_each(|d| -> color_eyre::Result<()> { - d.write(writer)?; - writeln!(writer)?; - Ok(()) - })?; - - writeln!( - writer, - "static constexpr bool {__CORDL_IS_VALUE_TYPE} = {};", - self.is_value_type - )?; - // Type complete - writer.dedent(); - writeln!(writer, "}};")?; - - if self.packing.is_some() { - writeln!(writer, "#pragma pack(pop)")?; - } - - // NON MEMBER DECLARATIONS - writeln!(writer, "// Non member Declarations")?; - - self.nonmember_declarations - .iter() - .try_for_each(|d| -> color_eyre::Result<()> { - d.write(writer)?; - writeln!(writer)?; - Ok(()) - })?; - - // Namespace complete - if let Some(n) = namespace { - writer.dedent(); - writeln!(writer, "}} // namespace end def {n}")?; - } - } - - // TODO: Write additional meta-info here, perhaps to ensure correct conversions? - Ok(()) - } - - pub fn write_type_trait(&self, writer: &mut CppWriter) -> color_eyre::Result<()> { - if self.cpp_template.is_some() { - // generic - // macros from bs hook - let type_trait_macro = if self.is_enum_type || self.is_value_type { - "MARK_GEN_VAL_T" - } else { - "MARK_GEN_REF_PTR_T" - }; - - writeln!( - writer, - "{type_trait_macro}({});", - self.cpp_name_components - .clone() - .remove_generics() - .remove_pointer() - .combine_all() - )?; - } else { - // non-generic - // macros from bs hook - let type_trait_macro = if self.is_enum_type || self.is_value_type { - "MARK_VAL_T" - } else { - "MARK_REF_PTR_T" - }; - - writeln!( - writer, - "{type_trait_macro}({});", - self.cpp_name_components.remove_pointer().combine_all() - )?; - } - - Ok(()) - } -} diff --git a/src/generate/cs_context_collection.rs b/src/generate/cs_context_collection.rs index 63dec2977..d363d9892 100644 --- a/src/generate/cs_context_collection.rs +++ b/src/generate/cs_context_collection.rs @@ -1,51 +1,550 @@ -use brocolib::global_metadata::TypeDefinitionIndex; +use core::panic; +use std::collections::{HashMap, HashSet}; -use super::{ - context_collection::CppContextCollection, cpp_type_tag::CppTypeTag, metadata::Metadata, +use brocolib::{ + global_metadata::TypeDefinitionIndex, + runtime_metadata::{Il2CppMethodSpec, TypeData}, }; +use itertools::Itertools; +use log::{info, warn}; -pub trait CsContextCollection { - fn get_cpp_context_collection(&self) -> &CppContextCollection; - fn get_mut_cpp_context_collection(&mut self) -> &mut CppContextCollection; +use crate::{data::type_resolver::TypeResolver, generate::cs_type::CsType}; - fn alias_nested_types_il2cpp( - &mut self, - owner_ty: TypeDefinitionIndex, - root_tag: CppTypeTag, - metadata: &Metadata, - nested: bool, - ); +use super::{ + context::TypeContext, + cs_type_tag::{CsTypeTag, GenericInstantiation}, + metadata::CordlMetadata, + type_extensions::TypeDefinitionExtensions, +}; + +pub struct TypeContextCollection { + // Should always be a TypeDefinitionIndex + pub all_contexts: HashMap, + pub alias_context: HashMap, + filled_types: HashSet, + filling_types: HashSet, + borrowing_types: HashSet, } -impl CsContextCollection for CppContextCollection { - fn get_cpp_context_collection(&self) -> &CppContextCollection { - self +impl TypeContextCollection { + fn fill_cpp_type(&mut self, cpp_type: &mut CsType, metadata: &CordlMetadata) { + let tag = cpp_type.self_tag; + + if self.filled_types.contains(&tag) { + return; + } + if self.filling_types.contains(&tag) { + panic!("Currently filling type {tag:?}, cannot fill") + } + + // Move ownership to local + self.filling_types.insert(tag); + + let type_resolver = TypeResolver { + cordl_metadata: metadata, + collection: self, + }; + cpp_type.fill_from_il2cpp(&type_resolver); + + self.filled_types.insert(tag); + self.filling_types.remove(&tag.clone()); } - fn get_mut_cpp_context_collection(&mut self) -> &mut CppContextCollection { - self + pub fn fill(&mut self, type_tag: CsTypeTag, metadata: &CordlMetadata) { + let context_tag = self.get_context_root_tag(type_tag); + + if self.filled_types.contains(&type_tag) { + return; + } + + if self.borrowing_types.contains(&context_tag) { + panic!("Borrowing context {context_tag:?}"); + } + + // Move ownership to local + let cpp_type_entry = self + .all_contexts + .get_mut(&context_tag) + .expect("No cpp context") + .typedef_types + .remove_entry(&type_tag); + + // In some occasions, the CppContext can be empty + if let Some((_t, mut cpp_type)) = cpp_type_entry { + self.fill_cpp_type(&mut cpp_type, metadata); + + // Move ownership back up + self.all_contexts + .get_mut(&context_tag) + .expect("No cpp context") + .insert_cs_type(cpp_type); + } } - fn alias_nested_types_il2cpp( + /// + /// Generate the aliases for the nested types through il2cpp + /// + /// If nested is true, aliases all nested types to their root type + /// + pub fn alias_nested_types_il2cpp( &mut self, owner_tdi: TypeDefinitionIndex, - root_tag: CppTypeTag, - metadata: &Metadata, - nested: bool, + root_tag: CsTypeTag, + metadata: &CordlMetadata, ) { - let owner_tag = CppTypeTag::TypeDefinitionIndex(owner_tdi); let owner_ty = &metadata.metadata.global_metadata.type_definitions[owner_tdi]; for nested_type_tdi in owner_ty.nested_types(metadata.metadata) { // let nested_type = &metadata.metadata.global_metadata.type_definitions[*nested_type_tdi]; - let nested_tag = CppTypeTag::TypeDefinitionIndex(*nested_type_tdi); + let nested_tag = CsTypeTag::TypeDefinitionIndex(*nested_type_tdi); + + self.alias_type_to_context(nested_tag, root_tag); + + self.alias_nested_types_il2cpp(*nested_type_tdi, root_tag, metadata); + } + } + + pub fn alias_type_to_context(&mut self, src: CsTypeTag, dest: CsTypeTag) { + assert!( + !self.alias_context.contains_key(&dest), + "Aliasing an aliased type! {src:?} to {dest:?}" + ); + assert!( + !self.alias_context.contains_key(&src), + "Already aliased this key!" + ); + assert!( + self.all_contexts.contains_key(&dest), + "Aliased context {src:?} to {dest:?} doesn't have a context" + ); + + self.alias_context.insert(src, dest); + } + + pub fn get_context_root_tag(&self, ty: CsTypeTag) -> CsTypeTag { + self.alias_context + .get(&ty) + .cloned() + // .map(|t| self.get_context_root_tag(*t)) + .unwrap_or(ty) + } + + pub fn make_nested_from( + &mut self, + metadata: &CordlMetadata<'_>, + tdi: TypeDefinitionIndex, + ) -> Option<&mut TypeContext> { + let ty_tag = CsTypeTag::TypeDefinitionIndex(tdi); + let ty_def = &metadata.metadata.global_metadata.type_definitions[tdi]; + let context_root_tag = self.get_context_root_tag(ty_tag); + + if self.filling_types.contains(&context_root_tag) { + panic!("Currently filling type {context_root_tag:?}, cannot fill") + } + + // Why is the borrow checker so dumb? + // Using entries causes borrow checker to die :( + if self.filled_types.contains(&ty_tag) { + return Some(self.all_contexts.get_mut(&context_root_tag).unwrap()); + } + + if self.get_cs_type(ty_tag).is_some() { + return self.get_context_mut(ty_tag); + } + + let context_tag = self.get_context_root_tag(ty_tag); + let context_type_data: TypeDefinitionIndex = context_tag.into(); + let context_td = &metadata.metadata.global_metadata.type_definitions[context_type_data]; - self.alias_type_to_context(nested_tag, root_tag, true, false); - if nested { - self.alias_type_to_parent(nested_tag, owner_tag, true); + if metadata.blacklisted_types.contains(&tdi) { + warn!( + "Skipping nested type because it's blacklisted! {context_tag:?} {}", + context_td.full_name(metadata.metadata, true) + ); + return None; + } + + let nested_inherits_declaring = ty_def.is_assignable_to(context_td, metadata.metadata); + if nested_inherits_declaring { + warn!( + "Nested type \"{}\" inherits declaring type \"{}\"", + ty_def.full_name(metadata.metadata, true), + context_td.full_name(metadata.metadata, true) + ); + } + + match nested_inherits_declaring { + true => { + // If a nested type inherits its declaring type, move it to its own CppContext + let context = TypeContext::make(metadata, tdi, ty_tag); + + // Unnest type does not alias to another context or type + self.alias_context.remove(&ty_tag); + + self.all_contexts.insert(ty_tag, context); + self.all_contexts.get_mut(&ty_tag) + } + false => { + let new_cpp_type = CsType::make_cs_type(metadata, tdi, ty_tag) + .expect("Failed to make nested type"); + + let context = self.get_context_mut(ty_tag).unwrap(); + // self.alias_type_to_context(new_cpp_type.self_tag, context_root_tag, true); + + // context.insert_cpp_type(stub); + context.insert_cs_type(new_cpp_type); + + Some(context) } - self.alias_nested_types_il2cpp(*nested_type_tdi, root_tag, metadata, nested); } } + + /// Make a generic type + /// based of an existing type definition + /// and give it the generic args + pub fn make_generic_from( + &mut self, + method_spec: &Il2CppMethodSpec, + metadata: &mut CordlMetadata, + ) -> Option<&mut TypeContext> { + // Not a generic class, no type needed + if method_spec.class_inst_index == u32::MAX { + return None; + } + // Skip generic methods? + if method_spec.method_inst_index != u32::MAX { + return None; + } + + let method = + &metadata.metadata.global_metadata.methods[method_spec.method_definition_index]; + let ty_def = &metadata.metadata.global_metadata.type_definitions[method.declaring_type]; + + if ty_def.is_interface() { + // Skip interface + info!( + "Skipping make interface for generic instantiation {}", + ty_def.full_name(metadata.metadata, true) + ); + return None; + } + + let type_data = CsTypeTag::TypeDefinitionIndex(method.declaring_type); + let tdi = method.declaring_type; + let context_root_tag = self.get_context_root_tag(type_data); + + if metadata.blacklisted_types.contains(&tdi) { + warn!( + "Skipping generic instantiation {tdi:?} {} {}", + method_spec.class_inst_index, + ty_def.full_name(metadata.metadata, true) + ); + return None; + } + + if self.filling_types.contains(&context_root_tag) { + panic!("Currently filling type {context_root_tag:?}, cannot fill") + } + + let generic_class_ty_data = CsTypeTag::GenericInstantiation(GenericInstantiation { + tdi, + inst: method_spec.class_inst_index as usize, + }); + + let generic_inst = + &metadata.metadata_registration.generic_insts[method_spec.class_inst_index as usize]; + + // Why is the borrow checker so dumb? + // Using entries causes borrow checker to die :( + if self.filled_types.contains(&generic_class_ty_data) { + return Some(self.all_contexts.get_mut(&context_root_tag).unwrap()); + } + + if self.get_cs_type(generic_class_ty_data).is_some() { + return self.get_context_mut(generic_class_ty_data); + } + + let mut new_cpp_type = CsType::make_cs_type(metadata, tdi, generic_class_ty_data) + .expect("Failed to make generic type"); + + let type_resolver = TypeResolver { + cordl_metadata: metadata, + collection: self, + }; + new_cpp_type.add_class_generic_inst(&generic_inst.types, &type_resolver); + new_cpp_type.self_tag = generic_class_ty_data; + self.alias_type_to_context(new_cpp_type.self_tag, context_root_tag); + + // TODO: Not needed since making a cpp type will already be a stub in other passes? + // this is the generic stub + // this might cause problems, hopefully not + // since two types can coexist with the TDI though only one is nested + // let mut stub = new_cpp_type.clone(); + // stub.self_tag = type_data; + + new_cpp_type.requirements.add_dependency_tag(type_data); + + // if generic type is a nested type + // put it under the parent's `nested_types` field + // otherwise put it in the typedef's hashmap + + let context = self.get_context_mut(generic_class_ty_data).unwrap(); + + // context.insert_cpp_type(stub); + context.insert_cs_type(new_cpp_type); + + Some(context) + } + + /// + /// It's important this gets called AFTER the type is filled + /// + pub fn fill_generic_method_inst( + &mut self, + method_spec: &Il2CppMethodSpec, + metadata: &CordlMetadata, + ) -> Option<&mut TypeContext> { + if method_spec.method_inst_index == u32::MAX { + return None; + } + + let method = + &metadata.metadata.global_metadata.methods[method_spec.method_definition_index]; + + // is reference type + // only make generic spatialization + let type_data = CsTypeTag::TypeDefinitionIndex(method.declaring_type); + let tdi = method.declaring_type; + + let ty_def = &metadata.metadata.global_metadata.type_definitions[method.declaring_type]; + + if metadata.blacklisted_types.contains(&tdi) { + info!( + "Skipping {tdi:?} {} since it is blacklisted", + ty_def.full_name(metadata.metadata, true) + ); + return None; + } + + if ty_def.is_interface() { + // Skip interface + info!( + "Skipping fill generic method interface for generic instantiation {}", + ty_def.full_name(metadata.metadata, true) + ); + return None; + } + + let context_root_tag = self.get_context_root_tag(type_data); + + let generic_class_ty_data = if method_spec.class_inst_index != u32::MAX { + CsTypeTag::GenericInstantiation(GenericInstantiation { + tdi, + inst: method_spec.class_inst_index as usize, + }) + } else { + type_data + }; + + self.borrow_cs_type(generic_class_ty_data, |collection, mut cpp_type| { + let method_index = method_spec.method_definition_index; + let type_resolver = TypeResolver { + cordl_metadata: metadata, + collection, + }; + cpp_type.add_method_generic_inst(method_spec, &type_resolver); + cpp_type.create_method(method_index, &type_resolver, true); + + cpp_type + }); + + self.all_contexts.get_mut(&context_root_tag) + } + + pub fn fill_generic_class_inst( + &mut self, + method_spec: &Il2CppMethodSpec, + metadata: &CordlMetadata, + ) -> Option<&mut TypeContext> { + if method_spec.class_inst_index == u32::MAX { + return None; + } + // Skip generic methods? + if method_spec.method_inst_index != u32::MAX { + return None; + } + + let method = + &metadata.metadata.global_metadata.methods[method_spec.method_definition_index]; + + let ty_def = &metadata.metadata.global_metadata.type_definitions[method.declaring_type]; + + // only make generic spatialization + let type_data = CsTypeTag::TypeDefinitionIndex(method.declaring_type); + let tdi = method.declaring_type; + + if metadata.blacklisted_types.contains(&tdi) { + info!( + "Skipping {tdi:?} {} since it is blacklisted", + ty_def.full_name(metadata.metadata, true) + ); + return None; + } + + if ty_def.is_interface() { + // Skip interface + info!( + "Skipping fill class interface for generic instantiation {}", + ty_def.full_name(metadata.metadata, true) + ); + return None; + } + + let context_root_tag = self.get_context_root_tag(type_data); + + let generic_class_ty_data = if method_spec.class_inst_index != u32::MAX { + CsTypeTag::GenericInstantiation(GenericInstantiation { + tdi, + inst: method_spec.class_inst_index as usize, + }) + } else { + type_data + }; + + self.borrow_cs_type( + generic_class_ty_data, + |collection: &mut TypeContextCollection, mut cpp_type| { + // cpp_type.make_generics_args(metadata, collection); + collection.fill_cpp_type(&mut cpp_type, metadata); + + cpp_type + }, + ); + + self.all_contexts.get_mut(&context_root_tag) + } + + pub fn make_from( + &mut self, + metadata: &CordlMetadata, + + type_data: TypeData, + _generic_inst: Option<&Vec>, + ) -> &mut TypeContext { + let type_tag = CsType::get_tag_tdi(type_data); + assert!( + !metadata.child_to_parent_map.contains_key(&type_tag), + "Cannot create context for nested type", + ); + let context_root_tag = self.get_context_root_tag(type_tag.into()); + + if self.filling_types.contains(&context_root_tag) { + panic!("Currently filling type {context_root_tag:?}, cannot fill") + } + + if self.borrowing_types.contains(&context_root_tag) { + panic!("Currently borrowing context {context_root_tag:?}, cannot fill") + } + + // Why is the borrow checker so dumb? + // Using entries causes borrow checker to die :( + if self.all_contexts.contains_key(&context_root_tag) { + return self.all_contexts.get_mut(&context_root_tag).unwrap(); + } + + let tdi = context_root_tag.get_tdi(); + let context = TypeContext::make(metadata, tdi, context_root_tag); + // Now do children + for cpp_type in context.typedef_types.values() { + for n in &cpp_type.nested_types { + self.alias_type_to_context(*n, context_root_tag); + } + } + self.all_contexts.insert(context_root_tag, context); + self.all_contexts.get_mut(&context_root_tag).unwrap() + } + + /// + /// By default will only look for nested types of the context, ignoring other CppTypes + /// + pub fn get_cs_type(&self, ty: CsTypeTag) -> Option<&CsType> { + let context_root_tag = self.get_context_root_tag(ty); + + self.get_context(context_root_tag) + .and_then(|c| c.get_types().get(&ty)) + } + + /// + /// By default will only look for nested types of the context, ignoring other CppTypes + /// + pub fn get_cs_type_mut(&mut self, ty: CsTypeTag) -> Option<&mut CsType> { + let context_root_tag = self.get_context_root_tag(ty); + self.get_context_mut(context_root_tag) + .and_then(|c| c.get_types_mut().get_mut(&ty)) + } + + pub fn borrow_cs_type(&mut self, ty: CsTypeTag, func: F) + where + F: Fn(&mut Self, CsType) -> CsType, + { + let context_ty = self.get_context_root_tag(ty); + if self.borrowing_types.contains(&context_ty) { + panic!("Already borrowing this context!"); + } + + let context = self.all_contexts.get_mut(&context_ty).unwrap(); + + // TODO: Needed? + // self.borrowing_types.insert(context_ty); + + // search in root + // clone to avoid failing il2cpp_name + let Some(declaring_cpp_type) = context.typedef_types.get(&ty).cloned() else { + panic!("No type {ty:#?} found!") + }; + let _old_tag = declaring_cpp_type.self_tag; + let new_cpp_ty = func(self, declaring_cpp_type); + + let context = self.all_contexts.get_mut(&context_ty).unwrap(); + + context.insert_cs_type(new_cpp_ty); + + self.borrowing_types.remove(&context_ty); + } + + pub fn get_context(&self, type_tag: CsTypeTag) -> Option<&TypeContext> { + let context_tag = self.get_context_root_tag(type_tag); + if self.borrowing_types.contains(&context_tag) { + panic!("Borrowing this context! {context_tag:?}"); + } + self.all_contexts.get(&context_tag) + } + pub fn get_context_mut(&mut self, type_tag: CsTypeTag) -> Option<&mut TypeContext> { + let context_tag = self.get_context_root_tag(type_tag); + if self.borrowing_types.contains(&context_tag) { + panic!("Borrowing this context! {context_tag:?}"); + } + self.all_contexts + .get_mut(&self.get_context_root_tag(context_tag)) + } + + pub fn new() -> TypeContextCollection { + TypeContextCollection { + all_contexts: Default::default(), + filled_types: Default::default(), + filling_types: Default::default(), + alias_context: Default::default(), + borrowing_types: Default::default(), + } + } + pub fn take(self) -> HashMap { + self.all_contexts + } + pub fn get(&self) -> &HashMap { + &self.all_contexts + } + pub fn get_mut(&mut self) -> &mut HashMap { + &mut self.all_contexts + } } diff --git a/src/generate/cs_members.rs b/src/generate/cs_members.rs new file mode 100644 index 000000000..2b3bff468 --- /dev/null +++ b/src/generate/cs_members.rs @@ -0,0 +1,234 @@ +use bitflags::bitflags; +use brocolib::global_metadata::MethodIndex; +use bytes::Bytes; +use itertools::Itertools; + +use crate::data::type_resolver::ResolvedType; + +use super::writer::CppWritable; + +use std::{hash::Hash, sync::Arc}; + +#[derive(Debug, Eq, Hash, PartialEq, Clone, Default, PartialOrd, Ord)] +pub struct CsGenericTemplate { + pub names: Vec<(CsGenericTemplateType, String)>, +} + +#[derive(Debug, Eq, Hash, PartialEq, Clone, Default, PartialOrd, Ord)] +pub enum CsGenericTemplateType { + #[default] + Any, + Reference, +} + +impl CsGenericTemplate { + pub fn make_typenames(names: impl Iterator) -> Self { + CsGenericTemplate { + names: names + .into_iter() + .map(|s| (CsGenericTemplateType::Any, s)) + .collect(), + } + } + pub fn make_ref_types(names: impl Iterator) -> Self { + CsGenericTemplate { + names: names + .into_iter() + .map(|s| (CsGenericTemplateType::Reference, s)) + .collect(), + } + } + + pub fn just_names(&self) -> impl Iterator { + self.names.iter().map(|(_constraint, t)| t) + } +} + +#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd)] +pub struct CsCommentedString { + pub data: String, + pub comment: Option, +} + +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct CsUsingAlias { + pub result: String, + pub alias: String, + pub template: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum CsMember {} + +#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct CsMethodData { + pub estimated_size: Option, + pub addrs: Option, + pub slot: Option, +} + +#[derive(Clone, Debug)] +pub struct CsMethodSizeData { + pub cpp_method_name: String, + pub method_name: String, + pub declaring_type_name: String, + pub declaring_classof_call: String, + pub ret_ty: String, + pub instance: bool, + pub params: Vec, + pub method_data: CsMethodData, + + // this is so bad + pub method_info_lines: Vec, + pub method_info_var: String, + + pub template: Option, + pub generic_literals: Option>, + + pub interface_clazz_of: String, + pub is_final: bool, + pub slot: Option, +} + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub enum CsValue { + String(String), + Bool(bool), + + U8(u8), + U16(u16), + U32(u32), + U64(u64), + + I8(i8), + I16(i16), + I32(i32), + I64(i64), + + F32(f32), + F64(f64), + + Object(Bytes), + ValueType(Bytes), + Null, +} + +/// Explicit layout +/// il2cpp basically turns each field into 2 structs within a union: +/// 1 which is packed with size 1, and padded with offset to fit to the end +/// the other which has the same padding and layout, except this one is for alignment so it's just packed as the parent struct demands +/// union { +/// [[pack(1)]] +/// struct { +/// byte __field_padding = size(offset) +/// T field +/// } +/// [[pack(default)]] +/// struct { +/// byte __field_padding_forAlignment = size(offset) +/// T __field_forAlignment +/// }... per field +/// } +/// +#[derive(Clone, Debug, PartialEq)] +pub struct CsField { + pub name: String, + pub field_ty: ResolvedType, + pub instance: bool, + pub readonly: bool, + // is C# const (constant evaluated) + // could be assumed from value though + pub is_const: bool, + + pub offset: Option, + pub size: usize, + + pub value: Option, + pub brief_comment: Option, +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct CsProperty { + pub name: String, + pub prop_ty: ResolvedType, + pub instance: bool, + pub getter: Option, + pub setter: Option, + /// Whether this property is one that's indexable (accessor methods take an index argument) + pub indexable: bool, + pub brief_comment: Option, +} + +bitflags! { + #[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] + pub struct CsParamFlags: u8 { + const A = 1; + const B = 1 << 1; + const C = 0b0000_0100; + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CsParam { + pub name: String, + pub il2cpp_ty: ResolvedType, + // TODO: Use bitflags to indicate these attributes + // May hold: + // const + // May hold one of: + // * + // & + // && + pub modifiers: CsParamFlags, + pub def_value: Option, +} + +bitflags! { + #[derive(Clone, Debug, PartialEq)] + pub struct CSMethodFlags: u32 { + const STATIC = 0b00000001; + const VIRTUAL = 0b00000010; + const OPERATOR = 0b00000100; + const ABSTRACT = 0b00001000; + const OVERRIDE = 0b00010000; + const FINAL = 0b00100000; + const SPECIAL_NAME = 0b01000000; + const UNSAFE = 0b10000000; + } +} + +// TODO: Generics +#[derive(Clone, Debug, PartialEq)] +pub struct CsMethod { + pub name: String, + pub method_index: MethodIndex, + pub return_type: ResolvedType, + pub parameters: Vec, + pub instance: bool, + pub template: Option, + pub method_data: CsMethodData, + pub brief: Option, + pub method_flags: CSMethodFlags, +} + +// TODO: Generics +#[derive(Clone, Debug)] +pub struct CsConstructor { + pub cpp_name: String, + pub parameters: Vec, + pub template: Option, + + pub brief: Option, + pub body: Option>>, +} + +impl PartialEq for CsConstructor { + fn eq(&self, other: &Self) -> bool { + self.cpp_name == other.cpp_name + && self.parameters == other.parameters + && self.template == other.template + && self.brief == other.brief + // can't guarantee equality + && self.body.is_some() == other.body.is_some() + } +} diff --git a/src/generate/cs_type.rs b/src/generate/cs_type.rs index 060ec1cd4..d01dc58fd 100644 --- a/src/generate/cs_type.rs +++ b/src/generate/cs_type.rs @@ -1,155 +1,181 @@ -use core::panic; -use log::{debug, info, warn}; use std::{ - clone, - collections::HashMap, + collections::{HashMap, HashSet}, io::{Cursor, Read}, - rc::Rc, - slice::Iter, - sync::Arc, }; +use byteorder::ReadBytesExt; + use brocolib::{ global_metadata::{ FieldIndex, Il2CppFieldDefinition, Il2CppTypeDefinition, MethodIndex, ParameterIndex, - TypeDefinitionIndex, TypeIndex, + TypeDefinitionIndex, }, runtime_metadata::{Il2CppMethodSpec, Il2CppType, Il2CppTypeEnum, TypeData}, }; -use byteorder::{LittleEndian, ReadBytesExt}; - use itertools::Itertools; +use log::{debug, info, warn}; use crate::{ - data::name_components::NameComponents, - generate::{cs_fields::FieldInfo, members::CppUsingAlias, offsets}, + data::{ + name_components::NameComponents, + type_resolver::{ResolvedType, TypeResolver, TypeUsage}, + }, + generate::{ + cs_members::CsField, + type_extensions::{ParameterDefinitionExtensions, TypeExtentions}, + }, helpers::cursor::ReadBytesExtensions, + Endian, }; use super::{ - config::GenerationConfig, - context_collection::CppContextCollection, - cpp_type::{ - CppType, CppTypeRequirements, CORDL_NUM_ENUM_TYPE_CONSTRAINT, - CORDL_REFERENCE_TYPE_CONSTRAINT, __CORDL_BACKING_ENUM_TYPE, - }, - cpp_type_tag::CppTypeTag, - cs_fields::{ - handle_const_fields, handle_referencetype_fields, handle_static_fields, - handle_valuetype_fields, - }, - members::{ - CppConstructorDecl, CppConstructorImpl, CppFieldDecl, CppForwardDeclare, CppInclude, - CppLine, CppMember, CppMethodData, CppMethodDecl, CppMethodImpl, CppMethodSizeStruct, - CppNestedStruct, CppNonMember, CppParam, CppPropertyDecl, CppStaticAssert, CppTemplate, + cs_members::{ + CSMethodFlags, CsConstructor, CsGenericTemplate, CsMethod, CsMethodData, CsParam, + CsParamFlags, CsProperty, CsValue, }, - metadata::{Metadata, TypeUsage}, + cs_type_tag::CsTypeTag, + metadata::CordlMetadata, + offsets::{self, SizeInfo}, type_extensions::{ - Il2CppTypeEnumExtensions, MethodDefintionExtensions, ParameterDefinitionExtensions, - TypeDefinitionExtensions, TypeExtentions, + MethodDefintionExtensions, TypeDefinitionExtensions, TypeDefinitionIndexExtensions, }, - writer::Writable, }; -type Endian = LittleEndian; - -// negative -pub const VALUE_TYPE_SIZE_OFFSET: u32 = 0x10; - -pub const VALUE_TYPE_WRAPPER_SIZE: &str = "__IL2CPP_VALUE_TYPE_SIZE"; -pub const REFERENCE_TYPE_WRAPPER_SIZE: &str = "__IL2CPP_REFERENCE_TYPE_SIZE"; -pub const REFERENCE_TYPE_FIELD_SIZE: &str = "__fields"; -pub const REFERENCE_WRAPPER_INSTANCE_NAME: &str = "::bs_hook::Il2CppWrapperType::instance"; +#[derive(Debug, Clone, Default)] +pub struct CsTypeRequirements { + // Lists both types we forward declare or include + pub depending_types: HashSet, +} -pub const VALUE_WRAPPER_TYPE: &str = "::bs_hook::ValueType"; -pub const ENUM_WRAPPER_TYPE: &str = "::bs_hook::EnumType"; -pub const INTERFACE_WRAPPER_TYPE: &str = "::cordl_internals::InterfaceW"; -pub const IL2CPP_OBJECT_TYPE: &str = "Il2CppObject"; -pub const CORDL_NO_INCLUDE_IMPL_DEFINE: &str = "CORDL_NO_IMPL_INCLUDE"; -pub const CORDL_ACCESSOR_FIELD_PREFIX: &str = "___"; +impl CsTypeRequirements { + pub fn add_dependency(&mut self, ty: &CsType) { + self.depending_types.insert(ty.self_tag); + } + pub fn add_dependency_tag(&mut self, tag: CsTypeTag) { + self.depending_types.insert(tag); + } +} -pub const ENUM_PTR_TYPE: &str = "::bs_hook::EnumPtr"; -pub const VT_PTR_TYPE: &str = "::bs_hook::VTPtr"; +// Represents all of the information necessary for a C++ TYPE! +// A C# type will be TURNED INTO this +#[derive(Debug, Clone)] +pub struct CsType { + pub self_tag: CsTypeTag, + pub declaring_ty: Option, -const SIZEOF_IL2CPP_OBJECT: u32 = 0x10; + pub size_info: Option, + pub packing: Option, -pub trait CSType: Sized { - fn get_mut_cpp_type(&mut self) -> &mut CppType; // idk how else to do this - fn get_cpp_type(&self) -> &CppType; // idk how else to do this + // Computed by TypeDefinition.full_name() + // Then fixed for generic types in CppContextCollection::make_generic_from/fill_generic_inst + // pub cpp_name_components: NameComponents, + pub cs_name_components: NameComponents, - fn get_tag_tdi(tag: TypeData) -> TypeDefinitionIndex { - match tag { - TypeData::TypeDefinitionIndex(tdi) => tdi, - _ => panic!("Unsupported type: {tag:?}"), - } - } - fn get_cpp_tag_tdi(tag: CppTypeTag) -> TypeDefinitionIndex { - tag.into() - } + pub fields: Vec, + pub methods: Vec, + pub properties: Vec, + pub constructors: Vec, - fn parent_joined_cpp_name(metadata: &Metadata, tdi: TypeDefinitionIndex) -> String { - let ty_def = &metadata.metadata.global_metadata.type_definitions[tdi]; + pub is_value_type: bool, + pub is_enum_type: bool, + pub is_reference_type: bool, + pub requirements: CsTypeRequirements, - let name = ty_def.name(metadata.metadata); + pub parent: Option, + pub interfaces: Vec, + pub generic_template: Option, // Names of templates e.g T, TKey etc. - if ty_def.declaring_type_index != u32::MAX { - let declaring_ty = - metadata.metadata_registration.types[ty_def.declaring_type_index as usize]; + /// contains the array of generic Il2CppType indexes + /// + /// for generic instantiation e.g Foo -> Foo + pub generic_instantiations_args_types: Option>, // GenericArg idx -> Instantiation Arg + pub method_generic_instantiation_map: HashMap>, // MethodIndex -> Generic Args - if let TypeData::TypeDefinitionIndex(declaring_tdi) = declaring_ty.data { - return Self::parent_joined_cpp_name(metadata, declaring_tdi) + "/" + name; - } else { - return declaring_ty.full_name(metadata.metadata) + "/" + name; - } - } + pub is_interface: bool, + pub nested_types: HashSet, +} - ty_def.full_name(metadata.metadata, true) +impl CsType { + pub fn namespace(&self) -> String { + self.cs_name_components + .namespace + .clone() + .unwrap_or_default() } - fn fixup_into_generic_instantiation(&mut self) -> &mut CppType { - let cpp_type = self.get_mut_cpp_type(); - assert!( - cpp_type.generic_instantiations_args_types.is_some(), - "No generic instantiation args!" - ); + pub fn name(&self) -> &String { + &self.cs_name_components.name + } - cpp_type.cpp_template = Some(CppTemplate { names: vec![] }); - cpp_type.is_stub = false; - cpp_type.cpp_name_components.generics = None; + pub fn get_nested_types(&self) -> &HashSet { + &self.nested_types + } - cpp_type + pub fn get_tag_tdi(tag: TypeData) -> TypeDefinitionIndex { + match tag { + TypeData::TypeDefinitionIndex(tdi) => tdi, + _ => panic!("Unsupported type: {tag:?}"), + } } - fn add_method_generic_inst( + //// + /// + /// + pub fn add_method_generic_inst( &mut self, method_spec: &Il2CppMethodSpec, - metadata: &Metadata, - ) -> &mut CppType { + type_resolver: &TypeResolver, + ) -> &mut CsType { assert!(method_spec.method_inst_index != u32::MAX); - let cpp_type = self.get_mut_cpp_type(); - + let metadata = type_resolver.cordl_metadata; let inst = metadata .metadata_registration .generic_insts .get(method_spec.method_inst_index as usize) .unwrap(); - cpp_type.method_generic_instantiation_map.insert( - method_spec.method_definition_index, - inst.types.iter().map(|t| *t as TypeIndex).collect(), + let args = inst + .types + .iter() + .map(|t| type_resolver.resolve_type(self, *t, TypeUsage::TypeName, true)) + .collect(); + self.method_generic_instantiation_map + .insert(method_spec.method_definition_index, args); + + self + } + pub fn add_class_generic_inst( + &mut self, + generic_inst: &[usize], + type_resolver: &TypeResolver, + ) -> &mut CsType { + let metadata = type_resolver.cordl_metadata; + + let tdi = self.self_tag.get_tdi(); + let t = tdi.get_type_definition(metadata.metadata); + + // TODO: Come up with a way to avoid this extra call to layout the entire type + // We really just want to call it once for a given size and then move on + // Every type should have a valid metadata size, even if it is 0 + + self.size_info = Some(offsets::get_size_info(t, tdi, Some(generic_inst), metadata)); + + self.generic_instantiations_args_types = Some( + generic_inst + .iter() + .map(|t| type_resolver.resolve_type(self, *t, TypeUsage::TypeName, true)) + .collect(), ); - cpp_type + self } - fn make_cpp_type( - metadata: &Metadata, - config: &GenerationConfig, + pub fn make_cs_type( + metadata: &CordlMetadata, tdi: TypeDefinitionIndex, - tag: CppTypeTag, - generic_inst_types: Option<&Vec>, - ) -> Option { + tag: CsTypeTag, + ) -> Option { // let iface = metadata.interfaces.get(t.interfaces_start); // Then, handle interfaces @@ -170,7 +196,9 @@ pub trait CSType: Sized { }); let cpp_template = generics.as_ref().map(|g| { - CppTemplate::make_typenames(g.iter().map(|g| g.name(metadata.metadata).to_string())) + CsGenericTemplate::make_typenames( + g.iter().map(|g| g.name(metadata.metadata).to_string()), + ) }); let ns = t.namespace(metadata.metadata); @@ -184,102 +212,59 @@ pub trait CSType: Sized { } // all nested types are unnested - let nested = false; // t.declaring_type_index != u32::MAX; - let cs_name_components = t.get_name_components(metadata.metadata); + let declaring_ty = (t.declaring_type_index != u32::MAX).then(|| { + metadata + .metadata_registration + .types + .get(t.declaring_type_index as usize) + .unwrap() + }); + let declaring_tag = + declaring_ty.map(|t| CsTypeTag::from_type_data(t.data, metadata.metadata)); + let cs_name_components = t.get_name_components(metadata.metadata); let is_pointer = cs_name_components.is_pointer; - let cpp_name_components = NameComponents { - declaring_types: cs_name_components - .declaring_types - .as_ref() - .map(|declaring_types| { - declaring_types - .iter() - .map(|s| config.name_cpp(s)) - .collect_vec() - }), - generics: cs_name_components.generics.clone(), - name: config.name_cpp(&cs_name_components.name), - namespace: cs_name_components - .namespace - .as_ref() - .map(|s| config.namespace_cpp(s)), - is_pointer, - }; - // TODO: Come up with a way to avoid this extra call to layout the entire type // We really just want to call it once for a given size and then move on // Every type should have a valid metadata size, even if it is 0 - let size_info: offsets::SizeInfo = - offsets::get_size_info(t, tdi, generic_inst_types, metadata); + let size_info: offsets::SizeInfo = offsets::get_size_info(t, tdi, None, metadata); // best results of cordl are when specified packing is strictly what is used, but experimentation may be required let packing = size_info.specified_packing; // Modified later for nested types - let mut cpptype = CppType { + let cpptype = CsType { self_tag: tag, - nested, - prefix_comments: vec![format!("Type: {ns}::{name}"), format!("{size_info:?}")], + declaring_ty: declaring_tag, size_info: Some(size_info), packing, - cpp_name_components, cs_name_components, - declarations: Default::default(), - implementations: Default::default(), - nonmember_implementations: Default::default(), - nonmember_declarations: Default::default(), + fields: Default::default(), + methods: Default::default(), + properties: Default::default(), + constructors: Default::default(), is_value_type: t.is_value_type(), is_enum_type: t.is_enum_type(), is_reference_type: is_pointer, requirements: Default::default(), - inherit: Default::default(), + interfaces: Default::default(), + parent: Default::default(), + is_interface: t.is_interface(), - cpp_template, + generic_template: cpp_template, - generic_instantiations_args_types: generic_inst_types.cloned(), + generic_instantiations_args_types: Default::default(), method_generic_instantiation_map: Default::default(), - is_stub: false, - is_hidden: true, nested_types: Default::default(), }; - if cpptype.generic_instantiations_args_types.is_some() { - cpptype.fixup_into_generic_instantiation(); - } - - // Nested type unnesting fix - if t.declaring_type_index != u32::MAX { - let declaring_ty = &metadata - .metadata - .runtime_metadata - .metadata_registration - .types[t.declaring_type_index as usize]; - - let declaring_tag = CppTypeTag::from_type_data(declaring_ty.data, metadata.metadata); - let declaring_tdi: TypeDefinitionIndex = declaring_tag.into(); - let declaring_td = &metadata.metadata.global_metadata.type_definitions[declaring_tdi]; - let combined_name = cpptype - .cpp_name_components - .clone() - .remove_generics() - .remove_pointer() - .combine_all(); - - cpptype.cpp_name_components.namespace = - Some(config.namespace_cpp(declaring_td.namespace(metadata.metadata))); - cpptype.cpp_name_components.declaring_types = None; // remove declaring types - - cpptype.cpp_name_components.name = config.sanitize_to_cpp_name(&combined_name); - } - if t.parent_index == u32::MAX { if !t.is_interface() && t.full_name(metadata.metadata, true) != "System.Object" { info!("Skipping type: {ns}::{name} because it has parent index: {} and is not an interface!", t.parent_index); @@ -297,188 +282,24 @@ pub trait CSType: Sized { Some(cpptype) } - fn fill_from_il2cpp( - &mut self, - metadata: &Metadata, - config: &GenerationConfig, - ctx_collection: &CppContextCollection, - ) { - if self.get_cpp_type().is_stub { - // Do not fill stubs - return; - } - - let tdi: TypeDefinitionIndex = self.get_cpp_type().self_tag.into(); - - let t = &metadata.metadata.global_metadata.type_definitions[tdi]; - - self.make_generics_args(metadata, ctx_collection, tdi); - self.make_parents(metadata, ctx_collection, tdi); - self.make_interfaces(metadata, ctx_collection, config, tdi); - - // we depend on parents and generic args here - // default ctor - if t.is_value_type() || t.is_enum_type() { - self.create_valuetype_constructor(metadata, ctx_collection, config, tdi); - self.create_valuetype_field_wrapper(); - if t.is_enum_type() { - self.create_enum_wrapper(metadata, ctx_collection, tdi); - self.create_enum_backing_type_constant(metadata, ctx_collection, tdi); - } - self.add_default_ctor(false); - } else if t.is_interface() { - // self.make_interface_constructors(); - self.delete_move_ctor(); - self.delete_copy_ctor(); - // self.delete_default_ctor(); - } else { - // ref type - self.delete_move_ctor(); - self.delete_copy_ctor(); - self.add_default_ctor(true); - // self.delete_default_ctor(); - } - - if !t.is_interface() { - self.create_size_assert(); - } - - self.add_type_index_member(); - - self.make_nested_types(metadata, ctx_collection, config, tdi); - self.make_fields(metadata, ctx_collection, config, tdi); - self.make_properties(metadata, ctx_collection, config, tdi); - self.make_methods(metadata, config, ctx_collection, tdi); - - if !t.is_interface() { - self.create_size_padding(metadata, tdi); - } - - if let Some(func) = metadata.custom_type_handler.get(&tdi) { - func(self.get_mut_cpp_type()) - } - } - - // fn make_generic_constraints( - // &mut self, - // metadata: &Metadata, - // config: &GenerationConfig, - // ctx_collection: &CppContextCollection, - // tdi: TypeDefinitionIndex, - // ) { - // let t = Self::get_type_definition(metadata, tdi); - - // if !t.generic_container_index.is_valid() { - // return; - // } - - // let generic_class = metadata.metadata_registration.generic_classes.iter().find(|t| t.); - // metadata.metadata_registration.generic_insts.get(generic_class.unwrap().context.class_inst_idx.unwrap()) - - // let generics = t.generic_container(metadata.metadata); - - // let generic_constraints: Vec> = generics - // .generic_parameters(metadata.metadata) - // .iter() - // .map(|p| p.constraints(metadata.metadata)) - // .map(|c| { - // c.iter() - // .map(|ti| { - // self.cppify_name_il2cpp( - // ctx_collection, - // metadata, - // metadata - // .metadata_registration - // .types - // .get(*ti as usize) - // .unwrap(), - // true, - // ) - // }) - // .filter(|l| !l.is_empty()) - // .collect() - // }) - // .filter(|l: &Vec| !l.is_empty()) - // .collect(); - // let cpp_type = self.get_mut_cpp_type(); - // } - - fn make_generics_args( - &mut self, - metadata: &Metadata, - ctx_collection: &CppContextCollection, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); - - if cpp_type.generic_instantiations_args_types.is_none() { - return; - } - - let generic_instantiations_args_types = - cpp_type.generic_instantiations_args_types.clone().unwrap(); - - let td = &metadata.metadata.global_metadata.type_definitions[tdi]; - let _ty = &metadata.metadata_registration.types[td.byval_type_index as usize]; - let generic_container = td.generic_container(metadata.metadata); + pub fn fill_from_il2cpp(&mut self, type_resolver: &TypeResolver) { + self.make_parents(type_resolver); + self.make_interfaces(type_resolver); - let mut template_args: Vec<(String, String)> = vec![]; - - let generic_instantiation_args: Vec = generic_instantiations_args_types - .iter() - .enumerate() - .map(|(gen_param_idx, u)| { - let t = metadata.metadata_registration.types.get(*u).unwrap(); - - let gen_param = generic_container - .generic_parameters(metadata.metadata) - .iter() - .find(|p| p.num as usize == gen_param_idx) - .expect("No generic parameter found for this index!"); - - (t, gen_param) - }) - .map(|(t, gen_param)| { - let gen_name = gen_param.name(metadata.metadata).to_string(); - - parse_generic_arg( - t, - gen_name, - cpp_type, - ctx_collection, - metadata, - &mut template_args, - ) - }) - .map(|n| n.combine_all()) - .collect(); - - // Handle nested types - // Assumes these nested types exist, - // which are created in the make_generic type func - // TODO: Base off a CppType the alias path - - // Add template constraint - // get all args with constraints - if !template_args.is_empty() { - cpp_type.cpp_template = Some(CppTemplate { - names: template_args, - }); - } - - cpp_type.cpp_name_components.generics = - Some(generic_instantiation_args.into_iter().collect_vec()); + self.make_nested_types(type_resolver); + self.make_fields(type_resolver); + self.make_properties(type_resolver); + self.make_methods(type_resolver); } fn make_parameters( &mut self, method: &brocolib::global_metadata::Il2CppMethodDefinition, - method_index: MethodIndex, - is_generic_method_inst: bool, - metadata: &Metadata<'_>, - config: &GenerationConfig, - ctx_collection: &CppContextCollection, - ) -> Vec { + type_resolver: &TypeResolver, + ) -> Vec { + let metadata = type_resolver.cordl_metadata; + let _tdi = self.self_tag.get_tdi(); + method .parameters(metadata.metadata) .iter() @@ -486,15 +307,7 @@ pub trait CSType: Sized { .map(|(pi, param)| { let param_index = ParameterIndex::new(method.parameter_start.index() + pi as u32); - self.make_parameter( - param, - method_index, - param_index, - is_generic_method_inst, - metadata, - config, - ctx_collection, - ) + self.make_parameter(param, param_index, type_resolver) }) .collect() } @@ -502,16 +315,13 @@ pub trait CSType: Sized { fn make_parameter( &mut self, param: &brocolib::global_metadata::Il2CppParameterDefinition, - method_index: MethodIndex, param_index: ParameterIndex, - is_generic_method_inst: bool, - metadata: &Metadata<'_>, - config: &GenerationConfig, - ctx_collection: &CppContextCollection, - ) -> CppParam { - let cpp_type = self.get_mut_cpp_type(); + type_resolver: &TypeResolver, + ) -> CsParam { + let metadata = type_resolver.cordl_metadata; + let _tdi = self.self_tag.get_tdi(); - let param_type = metadata + let _param_type = metadata .metadata_registration .types .get(param.type_index as usize) @@ -519,92 +329,41 @@ pub trait CSType: Sized { let def_value = Self::param_default_value(metadata, param_index); - let make_param_cpp_type_name = |cpp_type: &mut CppType| -> String { - let full_name = param_type.full_name(metadata.metadata); - - match full_name.as_str() { - "System.Enum" => { - cpp_type.requirements.needs_enum_include(); - ENUM_PTR_TYPE.into() - } - "System.ValueType" => { - cpp_type.requirements.needs_value_include(); - VT_PTR_TYPE.into() - } - _ => cpp_type - .cppify_name_il2cpp( - ctx_collection, - metadata, - param_type, - 0, - TypeUsage::Parameter, - ) - .combine_all(), - } - }; - - let param_cpp_name = { - // use template name if we are a generic inst - let fixup_name = match is_generic_method_inst { - // use template name - false => cpp_type.il2cpp_mvar_use_param_name( - metadata, - method_index, - make_param_cpp_type_name, - param_type, - ), - // use instantiation name - true => make_param_cpp_type_name(cpp_type), - }; - - cpp_type.il2cpp_byref(fixup_name, param_type) - }; - - CppParam { - name: config.name_cpp(param.name(metadata.metadata)), + CsParam { + name: param.name(metadata.metadata).to_owned(), def_value, - ty: param_cpp_name, - modifiers: "".to_string(), + il2cpp_ty: type_resolver.resolve_type( + self, + param.type_index as usize, + TypeUsage::Parameter, + false, + ), + modifiers: CsParamFlags::empty(), } } - fn make_methods( - &mut self, - metadata: &Metadata, - config: &GenerationConfig, - ctx_collection: &CppContextCollection, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); + fn make_methods(&mut self, type_resolver: &TypeResolver) { + let metadata = type_resolver.cordl_metadata; + let tdi = self.self_tag.get_tdi(); let t = Self::get_type_definition(metadata, tdi); // Then, handle methods if t.method_count > 0 { // 2 because each method gets a method struct and method decl // a constructor will add an additional one for each - cpp_type - .declarations - .reserve(2 * (t.method_count as usize + 1)); - cpp_type - .implementations - .reserve(t.method_count as usize + 1); + self.methods.reserve(t.method_count as usize + 1); // Then, for each method, write it out for (i, _method) in t.methods(metadata.metadata).iter().enumerate() { let method_index = MethodIndex::new(t.method_start.index() + i as u32); - self.create_method(t, method_index, metadata, ctx_collection, config, false); + self.create_method(method_index, type_resolver, false); } } } - fn make_fields( - &mut self, - metadata: &Metadata, - ctx_collection: &CppContextCollection, - config: &GenerationConfig, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); + fn make_fields(&mut self, type_resolver: &TypeResolver) { + let metadata = type_resolver.cordl_metadata; + let tdi = self.self_tag.get_tdi(); let t = Self::get_type_definition(metadata, tdi); // if no fields, skip @@ -626,19 +385,31 @@ pub trait CSType: Sized { "Computing offsets for TDI: {:?}, as it has a size of 0", tdi ); - let _resulting_size = offsets::layout_fields( - metadata, - t, - tdi, - cpp_type.generic_instantiations_args_types.as_ref(), - Some(&mut offsets), - false, - ); } + + let generic_inst_types = self + .generic_instantiations_args_types + .as_ref() + .map(|v| v.iter().map(|t| t.ty).collect_vec()); + offsets::layout_fields( + metadata, + t, + tdi, + generic_inst_types.as_deref(), + Some(&mut offsets), + false, + ); } let mut offset_iter = offsets.iter(); - let get_offset = |field: &Il2CppFieldDefinition, i: usize, iter: &mut Iter| { + fn get_offset<'a>( + field: &Il2CppFieldDefinition, + i: usize, + mut iter: impl Iterator, + field_offsets: &[u32], + metadata: &CordlMetadata<'_>, + t: &Il2CppTypeDefinition, + ) -> Option { let f_type = metadata .metadata_registration .types @@ -676,72 +447,31 @@ pub trait CSType: Sized { } }), } - }; + } - let get_size = |field: &Il2CppFieldDefinition, gen_args: Option<&Vec>| { + fn get_size( + field: &Il2CppFieldDefinition, + gen_args: Option<&Vec>, + metadata: &CordlMetadata<'_>, + ) -> usize { let f_type = metadata .metadata_registration .types .get(field.type_index as usize) .unwrap(); - let sa = offsets::get_il2cpptype_sa(metadata, f_type, gen_args); + let generic_inst_types: Option> = + gen_args.map(|list| list.iter().map(|t| t.ty).collect_vec()); + let sa = offsets::get_il2cpptype_sa(metadata, f_type, generic_inst_types.as_deref()); sa.size - - // // TODO: is valuetype even a good way of checking whether we should be size of pointer? - // match f_type.valuetype { - // false => metadata.pointer_size as u32, - // true => { - // match f_type.data { - // TypeData::TypeDefinitionIndex(f_tdi) => { - // let f_td = Self::get_type_definition(metadata, f_tdi); - - // }, - // TypeData::GenericClassIndex(class_index) => { - - // } - // } - // let f_tag = CppTypeTag::from_type_data(f_type.data, metadata.metadata); - // let f_tdi = f_tag.get_tdi(); - // let f_td = Self::get_type_definition(metadata, f_tdi); - - // if let Some(sz) = offsets::get_size_of_type_table(metadata, f_tdi) { - // if sz.instance_size == 0 { - // // At this point we need to compute the offsets - // debug!( - // "Computing offsets for TDI: {:?}, as it has a size of 0", - // tdi - // ); - // let _resulting_size = offsets::layout_fields( - // metadata, - // f_td, - // f_tdi, - // gen_args.as_ref(), - // None, - // ); - - // // TODO: check for VT fixup? - // if f_td.is_value_type() { - // _resulting_size.size as u32 - metadata.object_size() as u32 - // } else { - // _resulting_size.size as u32 - // } - // } else { - // sz.instance_size - metadata.object_size() as u32 - // } - // } else { - // 0 - // } - // } - // } - }; + } let fields = t .fields(metadata.metadata) .iter() .enumerate() - .filter_map(|(i, field)| { + .map(|(i, field)| { let f_type = metadata .metadata_registration .types @@ -751,125 +481,41 @@ pub trait CSType: Sized { let field_index = FieldIndex::new(t.field_start.index() + i as u32); let f_name = field.name(metadata.metadata); - let f_cpp_name = config.name_cpp_plus(f_name, &[cpp_type.cpp_name().as_str()]); - - let f_offset = get_offset(field, i, &mut offset_iter); + let f_offset = get_offset(field, i, &mut offset_iter, field_offsets, metadata, t); // calculate / fetch the field size - let f_size = if let Some(generics) = &cpp_type.generic_instantiations_args_types { - get_size(field, Some(generics)) - } else { - get_size(field, None) - }; - - if let TypeData::TypeDefinitionIndex(field_tdi) = f_type.data - && metadata.blacklisted_types.contains(&field_tdi) - { - if !cpp_type.is_value_type && !cpp_type.is_enum_type { - return None; - } - warn!("Value type uses {tdi:?} which is blacklisted! TODO"); - } - - // Var types are default pointers so we need to get the name component's pointer bool - let (field_ty_cpp_name, field_is_pointer) = - if f_type.is_constant() && f_type.ty == Il2CppTypeEnum::String { - ("::ConstString".to_string(), false) - } else { - let include_depth = match f_type.valuetype { - true => usize::MAX, - false => 0, - }; - - let field_name_components = cpp_type.cppify_name_il2cpp( - ctx_collection, - metadata, - f_type, - include_depth, - TypeUsage::FieldName - ); + let f_size = get_size(field, self.generic_instantiations_args_types.as_ref(), &metadata); - ( - field_name_components.combine_all(), - field_name_components.is_pointer, - ) - }; // TODO: Check a flag to look for default values to speed this up let def_value = Self::field_default_value(metadata, field_index); assert!(def_value.is_none() || (def_value.is_some() && f_type.is_param_optional())); - let cpp_field_decl = CppFieldDecl { - cpp_name: f_cpp_name, - field_ty: field_ty_cpp_name, - offset: f_offset.unwrap_or(u32::MAX), + + CsField { + name: f_name.to_owned(), + field_ty: type_resolver.resolve_type(self, field.type_index as usize, TypeUsage::Field, true), + offset: f_offset, + size: f_size, instance: !f_type.is_static() && !f_type.is_constant(), readonly: f_type.is_constant(), brief_comment: Some(format!("Field {f_name}, offset: 0x{:x}, size: 0x{f_size:x}, def value: {def_value:?}", f_offset.unwrap_or(u32::MAX))), value: def_value, - const_expr: false, - is_private: false, - }; - - Some(FieldInfo { - cpp_field: cpp_field_decl, - field, - field_type: f_type, - is_constant: f_type.is_constant(), - is_static: f_type.is_static(), - is_pointer: field_is_pointer, - offset: f_offset, - size: f_size, - }) + is_const: false, + } }) .collect_vec(); - for field_info in fields.iter() { - let f_type = field_info.field_type; - - // only push def dependency if valuetype field & not a primitive builtin - if f_type.valuetype && !f_type.ty.is_primitive_builtin() { - let field_cpp_tag: CppTypeTag = - CppTypeTag::from_type_data(f_type.data, metadata.metadata); - let field_cpp_td_tag: CppTypeTag = field_cpp_tag.get_tdi().into(); - let field_cpp_type = ctx_collection.get_cpp_type(field_cpp_td_tag); - - if field_cpp_type.is_some() { - let field_cpp_context = ctx_collection - .get_context(field_cpp_td_tag) - .expect("No context for cpp value type"); - - cpp_type.requirements.add_def_include( - field_cpp_type, - CppInclude::new_context_typedef(field_cpp_context), - ); - - cpp_type.requirements.add_impl_include( - field_cpp_type, - CppInclude::new_context_typeimpl(field_cpp_context), - ); - } - } - } - - if t.is_value_type() || t.is_enum_type() { - handle_valuetype_fields(cpp_type, &fields, metadata, tdi); - } else { - handle_referencetype_fields(cpp_type, &fields, metadata, tdi); + for f in fields { + self.fields.push(f); } - - handle_static_fields(cpp_type, &fields, metadata, tdi); - handle_const_fields(cpp_type, &fields, ctx_collection, metadata, tdi); } - fn make_parents( - &mut self, - metadata: &Metadata, - ctx_collection: &CppContextCollection, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); + fn make_parents(&mut self, type_resolver: &TypeResolver) { + let metadata = type_resolver.cordl_metadata; + let tdi = self.self_tag.get_tdi(); + let t = &metadata.metadata.global_metadata.type_definitions[tdi]; let ns = t.namespace(metadata.metadata); @@ -880,7 +526,7 @@ pub trait CSType: Sized { match t.is_interface() { true => { // FIXME: should interfaces have a base type? I don't think they need to - // cpp_type.inherit.push(INTERFACE_WRAPPER_TYPE.to_string()); + // self.inherit.push(INTERFACE_WRAPPER_TYPE.to_string()); } false => { info!("Skipping type: {ns}::{name} because it has parent index: {} and is not an interface!", t.parent_index); @@ -895,271 +541,65 @@ pub trait CSType: Sized { .get(t.parent_index as usize) .unwrap_or_else(|| panic!("NO PARENT! But valid index found: {}", t.parent_index)); - let parent_ty: CppTypeTag = CppTypeTag::from_type_data(parent_type.data, metadata.metadata); - // handle value types and enum types specially - match t.is_value_type() || t.is_enum_type() { - // parent will be a value wrapper type - // which will have the size - // OF THE TYPE ITSELF, NOT PARENT - true => { - // let Some(size_info) = &cpp_type.size_info else { - // panic!("No size for value/enum type!") - // }; - - // if t.is_enum_type() { - // cpp_type.requirements.needs_enum_include(); - // } else if t.is_value_type() { - // cpp_type.requirements.needs_value_include(); - // } - - // let wrapper = wrapper_type_for_tdi(t); - - // cpp_type.inherit.push(wrapper.to_string()); - } - // handle as reference type - false => { - // make sure our parent is intended\ - let is_ref_type = matches!( - parent_type.ty, - Il2CppTypeEnum::Class | Il2CppTypeEnum::Genericinst | Il2CppTypeEnum::Object - ); - assert!(is_ref_type, "Not a class, object or generic inst!"); - - // We have a parent, lets do something with it - let inherit_type = cpp_type.cppify_name_il2cpp( - ctx_collection, - metadata, - parent_type, - usize::MAX, - TypeUsage::TypeName, - ); - - if is_ref_type { - // TODO: Figure out why some generic insts don't work here - let parent_tdi: TypeDefinitionIndex = parent_ty.into(); - - let base_type_context = ctx_collection - .get_context(parent_ty) - .or_else(|| ctx_collection.get_context(parent_tdi.into())) - .unwrap_or_else(|| { - panic!( - "No CppContext for base type {inherit_type:?}. Using tag {parent_ty:?}" - ) - }); - - let base_type_cpp_type = ctx_collection - .get_cpp_type(parent_ty) - .or_else(|| ctx_collection.get_cpp_type(parent_tdi.into())) - .unwrap_or_else(|| { - panic!( - "No CppType for base type {inherit_type:?}. Using tag {parent_ty:?}" - ) - }); - - cpp_type.requirements.add_impl_include( - Some(base_type_cpp_type), - CppInclude::new_context_typeimpl(base_type_context), - ) - } + if !t.is_value_type() || t.is_enum_type() { + // make sure our parent is intended\ + let is_ref_type = matches!( + parent_type.ty, + Il2CppTypeEnum::Class | Il2CppTypeEnum::Genericinst | Il2CppTypeEnum::Object + ); + assert!(is_ref_type, "Not a class, object or generic inst!"); - cpp_type - .inherit - .push(inherit_type.remove_pointer().combine_all()); - } + self.parent = Some(type_resolver.resolve_type( + self, + t.parent_index as usize, + TypeUsage::TypeName, + true, + )); } } - fn make_interfaces( - &mut self, - metadata: &Metadata<'_>, - ctx_collection: &CppContextCollection, - config: &GenerationConfig, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); + fn make_interfaces(&mut self, type_resolver: &TypeResolver) { + let metadata = type_resolver.cordl_metadata; + let tdi = self.self_tag.get_tdi(); let t = &metadata.metadata.global_metadata.type_definitions[tdi]; for &interface_index in t.interfaces(metadata.metadata) { - let int_ty = &metadata.metadata_registration.types[interface_index as usize]; + let _int_ty = &metadata.metadata_registration.types[interface_index as usize]; - // We have an interface, lets do something with it - let interface_name_il2cpp = &cpp_type.cppify_name_il2cpp( - ctx_collection, - metadata, - int_ty, - 0, + let resolved = type_resolver.resolve_type( + self, + interface_index as usize, TypeUsage::TypeName, + true, ); - let interface_cpp_name = interface_name_il2cpp.remove_pointer().combine_all(); - let interface_cpp_pointer = interface_name_il2cpp.as_pointer().combine_all(); - - let operator_method_decl = CppMethodDecl { - body: Default::default(), - brief: Some(format!("Convert operator to {interface_cpp_name:?}")), - cpp_name: interface_cpp_pointer.clone(), - return_type: "".to_string(), - instance: true, - is_const: false, - is_constexpr: true, - is_no_except: !t.is_value_type() && !t.is_enum_type(), - is_implicit_operator: true, - is_explicit_operator: false, - - is_virtual: false, - is_inline: true, - parameters: vec![], - template: None, - prefix_modifiers: vec![], - suffix_modifiers: vec![], - }; - let helper_method_decl = CppMethodDecl { - brief: Some(format!("Convert to {interface_cpp_name:?}")), - is_implicit_operator: false, - return_type: interface_cpp_pointer.clone(), - cpp_name: format!("i_{}", config.sanitize_to_cpp_name(&interface_cpp_name)), - ..operator_method_decl.clone() - }; - - let method_impl_template = cpp_type - .cpp_template - .as_ref() - .is_some_and(|c| !c.names.is_empty()) - .then(|| cpp_type.cpp_template.clone()) - .flatten(); - - let convert_line = match t.is_value_type() || t.is_enum_type() { - true => { - // box - "static_cast(::il2cpp_utils::Box(this))".to_string() - } - false => "static_cast(this)".to_string(), - }; - - let body: Vec> = vec![Arc::new(CppLine::make(format!( - "return static_cast<{interface_cpp_pointer}>({convert_line});" - )))]; - let declaring_cpp_full_name = - cpp_type.cpp_name_components.remove_pointer().combine_all(); - let operator_method_impl = CppMethodImpl { - body: body.clone(), - declaring_cpp_full_name: declaring_cpp_full_name.clone(), - template: method_impl_template.clone(), - ..operator_method_decl.clone().into() - }; - - let helper_method_impl = CppMethodImpl { - body: body.clone(), - declaring_cpp_full_name, - template: method_impl_template, - ..helper_method_decl.clone().into() - }; - - // operator - cpp_type - .declarations - .push(CppMember::MethodDecl(operator_method_decl).into()); - cpp_type - .implementations - .push(CppMember::MethodImpl(operator_method_impl).into()); - - // helper method - cpp_type - .declarations - .push(CppMember::MethodDecl(helper_method_decl).into()); - cpp_type - .implementations - .push(CppMember::MethodImpl(helper_method_impl).into()); + self.interfaces.push(resolved); } } - fn make_nested_types( - &mut self, - metadata: &Metadata, - ctx_collection: &CppContextCollection, - config: &GenerationConfig, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); + fn make_nested_types(&mut self, type_resolver: &TypeResolver) { + let metadata = type_resolver.cordl_metadata; + let tdi = self.self_tag.get_tdi(); let t = &metadata.metadata.global_metadata.type_definitions[tdi]; if t.nested_type_count == 0 { return; } - let generic_instantiation_args = cpp_type.cpp_name_components.generics.clone(); - - let aliases = t + self.nested_types = t .nested_types(metadata.metadata) .iter() - .filter(|t| !metadata.blacklisted_types.contains(t)) .map(|nested_tdi| { - let nested_td = &metadata.metadata.global_metadata.type_definitions[*nested_tdi]; - let nested_tag = CppTypeTag::TypeDefinitionIndex(*nested_tdi); - - let nested_context = ctx_collection - .get_context(nested_tag) - .expect("Unable to find CppContext"); - let nested = ctx_collection - .get_cpp_type(nested_tag) - .expect("Unable to find nested CppType"); - - let alias = CppUsingAlias::from_cpp_type( - config.name_cpp(nested_td.name(metadata.metadata)), - nested, - generic_instantiation_args.clone(), - // if no generic args are made, we can do the generic fixup - // ORDER OF PASSES MATTERS - nested.generic_instantiations_args_types.is_none(), - ); - let fd = CppForwardDeclare::from_cpp_type(nested); - let inc = CppInclude::new_context_typedef(nested_context); + let _nested_td = &metadata.metadata.global_metadata.type_definitions[*nested_tdi]; - (alias, fd, inc) + CsTypeTag::TypeDefinitionIndex(*nested_tdi) }) - .collect_vec(); - - for (alias, fd, inc) in aliases { - cpp_type - .declarations - .insert(0, CppMember::CppUsingAlias(alias).into()); - cpp_type.requirements.add_forward_declare((fd, inc)); - } - - // forward - - // old way of making nested types - - // let mut nested_types: Vec = Vec::with_capacity(t.nested_type_count as usize); - - // for &nested_type_index in t.nested_types(metadata.metadata) { - // let nt_ty = &metadata.metadata.global_metadata.type_definitions[nested_type_index]; - - // // We have a parent, lets do something with it - // let nested_type = CppType::make_cpp_type( - // metadata, - // config, - // CppTypeTag::TypeDefinitionIndex(nested_type_index), - // nested_type_index, - // ); - - // match nested_type { - // Some(unwrapped) => nested_types.push(unwrapped), - // None => info!("Failed to make nested CppType {nt_ty:?}"), - // }; - // } - - // cpp_type.nested_types = nested_types.into_iter().map(|t| (t.self_tag, t)).collect() + .collect(); } - fn make_properties( - &mut self, - metadata: &Metadata, - ctx_collection: &CppContextCollection, - config: &GenerationConfig, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); + fn make_properties(&mut self, type_resolver: &TypeResolver) { + let metadata = type_resolver.cordl_metadata; + let tdi = self.self_tag.get_tdi(); let t = Self::get_type_definition(metadata, tdi); // Then, handle properties @@ -1167,7 +607,7 @@ pub trait CSType: Sized { return; } - cpp_type.declarations.reserve(t.property_count as usize); + self.properties.reserve(t.property_count as usize); // Then, for each field, write it out for prop in t.properties(metadata.metadata) { let p_name = prop.name(metadata.metadata); @@ -1184,1236 +624,120 @@ pub trait CSType: Sized { None => p_setter.unwrap().parameters(metadata.metadata)[0].type_index as usize, }; - let p_type = metadata + let _p_type = metadata .metadata_registration .types .get(p_type_index) .unwrap(); - let p_ty_cpp_name = cpp_type - .cppify_name_il2cpp(ctx_collection, metadata, p_type, 0, TypeUsage::PropertyName) - .combine_all(); - - let _method_map = |p: MethodIndex| { - let method_calc = metadata.method_calculations.get(&p).unwrap(); - CppMethodData { - estimated_size: method_calc.estimated_size, - addrs: method_calc.addrs, - } - }; - let _abstr = p_getter.is_some_and(|p| p.is_abstract_method()) || p_setter.is_some_and(|p| p.is_abstract_method()); let index = p_getter.is_some_and(|p| p.parameter_count > 0); // Need to include this type - cpp_type.declarations.push( - CppMember::Property(CppPropertyDecl { - cpp_name: config.name_cpp(p_name), - prop_ty: p_ty_cpp_name.clone(), - // methods generated in make_methods - setter: p_setter.map(|m| config.name_cpp(m.name(metadata.metadata))), - getter: p_getter.map(|m| config.name_cpp(m.name(metadata.metadata))), - indexable: index, - brief_comment: None, - instance: true, - }) - .into(), - ); + let prop_ty = type_resolver.resolve_type(self, p_type_index, TypeUsage::Property, true); + self.properties.push(CsProperty { + name: p_name.to_owned(), + prop_ty, + // methods generated in make_methods + setter: p_setter.map(|m| m.name(metadata.metadata).to_string()), + getter: p_getter.map(|m| m.name(metadata.metadata).to_string()), + indexable: index, + brief_comment: None, + instance: true, + }); } } - fn create_size_assert(&mut self) { - let cpp_type = self.get_mut_cpp_type(); + pub fn create_method( + &mut self, + method_index: MethodIndex, + type_resolver: &TypeResolver, + is_generic_method_inst: bool, + ) { + let metadata = type_resolver.cordl_metadata; + let method = &metadata.metadata.global_metadata.methods[method_index]; - // FIXME: make this work with templated types that either: have a full template (complete instantiation), or only require a pointer (size should be stable) - // for now, skip templated types - if cpp_type.cpp_template.is_some() { + // TODO: sanitize method name for c++ + let m_name = method.name(metadata.metadata); + if m_name == ".cctor" { + // info!("Skipping {}", m_name); return; } - if let Some(size) = cpp_type.size_info.as_ref().map(|s| s.instance_size) { - let cpp_name = cpp_type.cpp_name_components.remove_pointer().combine_all(); - - assert!(!cpp_name.trim().is_empty(), "CPP Name cannot be empty!"); + let _m_ret_type = metadata + .metadata_registration + .types + .get(method.return_type as usize) + .unwrap(); - let assert = CppStaticAssert { - condition: format!("::cordl_internals::size_check_v<{cpp_name}, 0x{size:x}>"), - message: Some("Size mismatch!".to_string()), - }; + let m_params_with_def: Vec = self.make_parameters(method, type_resolver); - cpp_type - .nonmember_declarations - .push(Rc::new(CppNonMember::CppStaticAssert(assert))); - } else { - todo!("Why does this type not have a valid size??? {cpp_type:?}"); - } - } - /// - /// add missing size for type - /// - fn create_size_padding(&mut self, metadata: &Metadata, tdi: TypeDefinitionIndex) { - let cpp_type = self.get_mut_cpp_type(); + let m_params_no_def: Vec = m_params_with_def + .iter() + .cloned() + .map(|mut p| { + p.def_value = None; + p + }) + .collect_vec(); - // // get type metadata size - let Some(type_definition_sizes) = &metadata.metadata_registration.type_definition_sizes - else { - return; - }; + // TODO: Add template if a generic inst e.g + // T UnityEngine.Component::GetComponent() -> bs_hook::Il2CppWrapperType UnityEngine.Component::GetComponent() + let template = method + .generic_container_index + .is_valid() + .then(|| match is_generic_method_inst { + true => Some(CsGenericTemplate { names: vec![] }), + false => { + let generics = method + .generic_container(metadata.metadata) + .unwrap() + .generic_parameters(metadata.metadata) + .iter() + .map(|param| param.name(metadata.metadata).to_string()); - let metadata_size = &type_definition_sizes.get(tdi.index() as usize); + Some(CsGenericTemplate::make_typenames(generics)) + } + }) + .flatten(); - let Some(metadata_size) = metadata_size else { - return; - }; - - // // ignore types that aren't sized - if metadata_size.instance_size == 0 || metadata_size.instance_size == u32::MAX { - return; - } - - // // if the size matches what we calculated, we're fine - // if metadata_size.instance_size == calculated_size { - // return; - // } - // let remaining_size = metadata_size.instance_size.abs_diff(calculated_size); - - let Some(size_info) = cpp_type.size_info.as_ref() else { - return; - }; - - // for all types, the size il2cpp metadata says the type should be, for generics this is calculated though - let metadata_size_instance = size_info.instance_size; - - // align the calculated size to the next multiple of natural_alignment, similiar to what happens when clang compiles our generated code - // this comes down to adding our size, and removing any bits that make it more than the next multiple of alignment - #[cfg(feature = "il2cpp_v29")] - let aligned_calculated_size = match size_info.natural_alignment as u32 { - 0 => size_info.calculated_instance_size, - alignment => (size_info.calculated_instance_size + alignment) & !(alignment - 1), - }; - #[cfg(feature = "il2cpp_v31")] - let aligned_calculated_size = size_info.calculated_instance_size; - - // return if calculated layout size == metadata size - if aligned_calculated_size == metadata_size_instance { - return; - } - - let remaining_size = metadata_size_instance.abs_diff(size_info.calculated_instance_size); - - // pack the remaining size to fit the packing of the type - let closest_packing = |size: u32| match size { - 0 => 0, - 1 => 1, - 2 => 2, - 3 => 4, - 4 => 4, - _ => 8, - }; - - let packing = cpp_type - .packing - .unwrap_or_else(|| closest_packing(size_info.calculated_instance_size)); - let packed_remaining_size = match packing == 0 { - true => remaining_size, - false => remaining_size & !(packing as u32 - 1), - }; - - // if the packed remaining size ends up being 0, don't emit padding - if packed_remaining_size == 0 { - return; - } - - cpp_type.declarations.push( - CppMember::FieldDecl(CppFieldDecl { - cpp_name: format!("_cordl_size_padding[0x{packed_remaining_size:x}]").to_string(), - field_ty: "uint8_t".into(), - offset: size_info.instance_size, - instance: true, - readonly: false, - const_expr: false, - value: None, - brief_comment: Some(format!( - "Size padding 0x{:x} - 0x{:x} = 0x{remaining_size:x}, packed as 0x{packed_remaining_size:x}", - metadata_size_instance, size_info.calculated_instance_size - )), - is_private: false, - }) - .into(), - ); - } - - fn create_ref_size(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - if let Some(size) = cpp_type.size_info.as_ref().map(|s| s.instance_size) { - cpp_type.declarations.push( - CppMember::FieldDecl(CppFieldDecl { - cpp_name: REFERENCE_TYPE_WRAPPER_SIZE.to_string(), - field_ty: "auto".to_string(), - offset: u32::MAX, - instance: false, - readonly: false, - const_expr: true, - value: Some(format!("0x{size:x}")), - brief_comment: Some("The size of the true reference type".to_string()), - is_private: false, - }) - .into(), - ); - - // here we push an instance field like uint8_t __fields[total_size - base_size] to make sure ref types are the exact size they should be - let fixup_size = match cpp_type.inherit.first() { - Some(base_type) => format!("0x{size:x} - sizeof({base_type})"), - None => format!("0x{size:x}"), - }; - - cpp_type.declarations.push( - CppMember::FieldDecl(CppFieldDecl { - cpp_name: format!("{REFERENCE_TYPE_FIELD_SIZE}[{fixup_size}]"), - field_ty: "uint8_t".to_string(), - offset: u32::MAX, - instance: true, - readonly: false, - const_expr: false, - value: Some("".into()), - brief_comment: Some( - "The size this ref type adds onto its base type, may evaluate to 0" - .to_string(), - ), - is_private: false, - }) - .into(), - ); - } else { - todo!("Why does this type not have a valid size??? {:?}", cpp_type); - } - } - fn create_enum_backing_type_constant( - &mut self, - metadata: &Metadata, - ctx_collection: &CppContextCollection, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); - - let t = Self::get_type_definition(metadata, tdi); - - let backing_field_idx = t.element_type_index as usize; - let backing_field_ty = &metadata.metadata_registration.types[backing_field_idx]; - - let enum_base = cpp_type - .cppify_name_il2cpp( - ctx_collection, - metadata, - backing_field_ty, - 0, - TypeUsage::TypeName, - ) - .remove_pointer() - .combine_all(); - - cpp_type.declarations.push( - CppMember::CppUsingAlias(CppUsingAlias { - alias: __CORDL_BACKING_ENUM_TYPE.to_string(), - result: enum_base, - template: None, - }) - .into(), - ); - } - - fn create_enum_wrapper( - &mut self, - metadata: &Metadata, - ctx_collection: &CppContextCollection, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); - let t = Self::get_type_definition(metadata, tdi); - let unwrapped_name = format!("__{}_Unwrapped", cpp_type.cpp_name()); - let backing_field = metadata - .metadata_registration - .types - .get(t.element_type_index as usize) - .unwrap(); - - let enum_base = cpp_type - .cppify_name_il2cpp( - ctx_collection, - metadata, - backing_field, - 0, - TypeUsage::TypeName, - ) - .remove_pointer() - .combine_all(); - - let enum_entries = t - .fields(metadata.metadata) - .iter() - .enumerate() - .map(|(i, field)| { - let field_index = FieldIndex::new(t.field_start.index() + i as u32); - - (field_index, field) - }) - .filter_map(|(field_index, field)| { - let f_type = metadata - .metadata_registration - .types - .get(field.type_index as usize) - .unwrap(); - - f_type.is_static().then(|| { - // enums static fields are always the enum values - let f_name = field.name(metadata.metadata); - let value = Self::field_default_value(metadata, field_index) - .expect("Enum without value!"); - - // prepend enum name with __E_ to prevent accidentally creating enum values that are reserved for builtin macros - format!("__E_{f_name} = {value},") - }) - }) - .map(|s| -> CppMember { CppMember::CppLine(s.into()) }); - - let nested_struct = CppNestedStruct { - base_type: Some(enum_base.clone()), - declaring_name: unwrapped_name.clone(), - is_class: false, - is_enum: true, - is_private: false, - declarations: enum_entries.map(Rc::new).collect(), - brief_comment: Some(format!("Nested struct {unwrapped_name}")), - packing: None, - }; - cpp_type - .declarations - .push(CppMember::NestedStruct(nested_struct).into()); - - let operator_body = format!("return static_cast<{unwrapped_name}>(this->value__);"); - let unwrapped_operator_decl = CppMethodDecl { - cpp_name: Default::default(), - instance: true, - return_type: unwrapped_name, - - brief: Some("Conversion into unwrapped enum value".to_string()), - body: Some(vec![Arc::new(CppLine::make(operator_body))]), - is_const: true, - is_constexpr: true, - is_virtual: false, - is_explicit_operator: false, - is_implicit_operator: true, - is_no_except: true, - parameters: vec![], - prefix_modifiers: vec![], - suffix_modifiers: vec![], - template: None, - is_inline: true, - }; - // convert to proper backing type - let backing_operator_body = format!("return static_cast<{enum_base}>(this->value__);"); - let backing_operator_decl = CppMethodDecl { - brief: Some("Conversion into unwrapped enum value".to_string()), - return_type: enum_base, - body: Some(vec![Arc::new(CppLine::make(backing_operator_body))]), - is_explicit_operator: true, - ..unwrapped_operator_decl.clone() - }; - - cpp_type - .declarations - .push(CppMember::MethodDecl(unwrapped_operator_decl).into()); - cpp_type - .declarations - .push(CppMember::MethodDecl(backing_operator_decl).into()); - } - - fn create_valuetype_field_wrapper(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - if cpp_type.size_info.is_none() { - todo!("Why does this type not have a valid size??? {:?}", cpp_type); - } - - let size = cpp_type - .size_info - .as_ref() - .map(|s| s.instance_size) - .unwrap(); - - cpp_type.requirements.needs_byte_include(); - cpp_type.declarations.push( - CppMember::FieldDecl(CppFieldDecl { - cpp_name: VALUE_TYPE_WRAPPER_SIZE.to_string(), - field_ty: "auto".to_string(), - offset: u32::MAX, - instance: false, - readonly: false, - const_expr: true, - value: Some(format!("0x{size:x}")), - brief_comment: Some("The size of the true value type".to_string()), - is_private: false, - }) - .into(), - ); - - // cpp_type.declarations.push( - // CppMember::ConstructorDecl(CppConstructorDecl { - // cpp_name: cpp_type.cpp_name().clone(), - // parameters: vec![CppParam { - // name: "instance".to_string(), - // ty: format!("std::array"), - // modifiers: Default::default(), - // def_value: None, - // }], - // template: None, - // is_constexpr: true, - // is_explicit: true, - // is_default: false, - // is_no_except: true, - // is_delete: false, - // is_protected: false, - // base_ctor: Some(( - // cpp_type.inherit.first().unwrap().to_string(), - // "instance".to_string(), - // )), - // initialized_values: Default::default(), - // brief: Some( - // "Constructor that lets you initialize the internal array explicitly".into(), - // ), - // body: Some(vec![]), - // }) - // .into(), - // ); - } - - fn create_valuetype_constructor( - &mut self, - metadata: &Metadata, - ctx_collection: &CppContextCollection, - config: &GenerationConfig, - tdi: TypeDefinitionIndex, - ) { - let cpp_type = self.get_mut_cpp_type(); - - let t = &metadata.metadata.global_metadata.type_definitions[tdi]; - - let instance_fields = t - .fields(metadata.metadata) - .iter() - .filter_map(|field| { - let f_type = metadata - .metadata_registration - .types - .get(field.type_index as usize) - .unwrap(); - - // ignore statics or constants - if f_type.is_static() || f_type.is_constant() { - return None; - } - - let f_type_cpp_name = cpp_type - .cppify_name_il2cpp(ctx_collection, metadata, f_type, 0, TypeUsage::FieldName) - .combine_all(); - - // Get the inner type of a Generic Inst - // e.g ReadOnlySpan -> ReadOnlySpan - let def_value = Self::type_default_value(metadata, Some(cpp_type), f_type); - - let f_cpp_name = config.name_cpp_plus( - field.name(metadata.metadata), - &[cpp_type.cpp_name().as_str()], - ); - - Some(CppParam { - name: f_cpp_name, - ty: f_type_cpp_name, - modifiers: "".to_string(), - // no default value for first param - def_value: Some(def_value), - }) - }) - .collect_vec(); - - if instance_fields.is_empty() { - return; - } - // Maps into the first parent -> "" - // so then Parent() - let base_ctor = cpp_type - .inherit - .first() - .map(|s| (s.clone(), "".to_string())); - - let body: Vec> = instance_fields - .iter() - .map(|p| { - let name = &p.name; - CppLine::make(format!("this->{name} = {name};")) - }) - .map(Arc::new) - // Why is this needed? _sigh_ - .map(|arc| -> Arc { arc }) - .collect_vec(); - - let params_no_def = instance_fields - .iter() - .cloned() - .map(|mut c| { - c.def_value = None; - c - }) - .collect_vec(); - - let constructor_decl = CppConstructorDecl { - cpp_name: cpp_type.cpp_name().clone(), - template: None, - is_constexpr: true, - is_explicit: false, - is_default: false, - is_no_except: true, - is_delete: false, - is_protected: false, - - base_ctor, - initialized_values: HashMap::new(), - // initialize values with params - // initialized_values: instance_fields - // .iter() - // .map(|p| (p.name.to_string(), p.name.to_string())) - // .collect(), - parameters: params_no_def, - brief: None, - body: None, - }; - - let method_impl_template = if cpp_type - .cpp_template - .as_ref() - .is_some_and(|c| !c.names.is_empty()) - { - cpp_type.cpp_template.clone() - } else { - None - }; - - let constructor_impl = CppConstructorImpl { - body, - template: method_impl_template, - parameters: instance_fields, - declaring_full_name: cpp_type.cpp_name_components.remove_pointer().combine_all(), - ..constructor_decl.clone().into() - }; - - cpp_type - .declarations - .push(CppMember::ConstructorDecl(constructor_decl).into()); - cpp_type - .implementations - .push(CppMember::ConstructorImpl(constructor_impl).into()); - } - - fn create_valuetype_default_constructors(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - // create the various copy and move ctors and operators - let cpp_name = cpp_type.cpp_name(); - let wrapper = format!("{VALUE_WRAPPER_TYPE}<{VALUE_TYPE_WRAPPER_SIZE}>::instance"); - - let move_ctor = CppConstructorDecl { - cpp_name: cpp_name.clone(), - parameters: vec![CppParam { - ty: cpp_name.clone(), - name: "".to_string(), - modifiers: "&&".to_string(), - def_value: None, - }], - template: None, - is_constexpr: true, - is_explicit: false, - is_default: true, - is_no_except: false, - is_delete: false, - is_protected: false, - base_ctor: None, - initialized_values: Default::default(), - brief: None, - body: None, - }; - - let copy_ctor = CppConstructorDecl { - cpp_name: cpp_name.clone(), - parameters: vec![CppParam { - ty: cpp_name.clone(), - name: "".to_string(), - modifiers: "const &".to_string(), - def_value: None, - }], - template: None, - is_constexpr: true, - is_explicit: false, - is_default: true, - is_no_except: false, - is_delete: false, - is_protected: false, - base_ctor: None, - initialized_values: Default::default(), - brief: None, - body: None, - }; - - let move_operator_eq = CppMethodDecl { - cpp_name: "operator=".to_string(), - return_type: format!("{cpp_name}&"), - parameters: vec![CppParam { - ty: cpp_name.clone(), - name: "o".to_string(), - modifiers: "&&".to_string(), - def_value: None, - }], - instance: true, - template: None, - suffix_modifiers: vec![], - prefix_modifiers: vec![], - is_virtual: false, - is_constexpr: true, - is_const: false, - is_no_except: true, - is_implicit_operator: false, - is_explicit_operator: false, - - is_inline: false, - brief: None, - body: Some(vec![ - Arc::new(CppLine::make(format!( - "this->{wrapper} = std::move(o.{wrapper});" - ))), - Arc::new(CppLine::make("return *this;".to_string())), - ]), - }; - - let copy_operator_eq = CppMethodDecl { - cpp_name: "operator=".to_string(), - return_type: format!("{cpp_name}&"), - parameters: vec![CppParam { - ty: cpp_name.clone(), - name: "o".to_string(), - modifiers: "const &".to_string(), - def_value: None, - }], - instance: true, - template: None, - suffix_modifiers: vec![], - prefix_modifiers: vec![], - is_virtual: false, - is_constexpr: true, - is_const: false, - is_no_except: true, - is_implicit_operator: false, - is_explicit_operator: false, - - is_inline: false, - brief: None, - body: Some(vec![ - Arc::new(CppLine::make(format!("this->{wrapper} = o.{wrapper};"))), - Arc::new(CppLine::make("return *this;".to_string())), - ]), - }; - - cpp_type - .declarations - .push(CppMember::ConstructorDecl(move_ctor).into()); - cpp_type - .declarations - .push(CppMember::ConstructorDecl(copy_ctor).into()); - cpp_type - .declarations - .push(CppMember::MethodDecl(move_operator_eq).into()); - cpp_type - .declarations - .push(CppMember::MethodDecl(copy_operator_eq).into()); - } - - fn create_ref_default_constructor(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - let cpp_name = cpp_type.cpp_name().clone(); - - let cs_name = cpp_type.name().clone(); - - // Skip if System.ValueType or System.Enum - if cpp_type.namespace() == "System" && (cs_name == "ValueType" || cs_name == "Enum") { - return; - } - - let default_ctor = CppConstructorDecl { - cpp_name: cpp_name.clone(), - parameters: vec![], - template: None, - is_constexpr: true, - is_explicit: false, - is_default: true, - is_no_except: true, - is_delete: false, - is_protected: true, - - base_ctor: None, - initialized_values: HashMap::new(), - brief: Some("Default ctor for custom type constructor invoke".to_string()), - body: None, - }; - let copy_ctor = CppConstructorDecl { - cpp_name: cpp_name.clone(), - parameters: vec![CppParam { - name: "".to_string(), - modifiers: " const&".to_string(), - ty: cpp_name.clone(), - def_value: None, - }], - template: None, - is_constexpr: true, - is_explicit: false, - is_default: true, - is_no_except: true, - is_delete: false, - is_protected: false, - - base_ctor: None, - initialized_values: HashMap::new(), - brief: None, - body: None, - }; - let move_ctor = CppConstructorDecl { - cpp_name: cpp_name.clone(), - parameters: vec![CppParam { - name: "".to_string(), - modifiers: "&&".to_string(), - ty: cpp_name.clone(), - def_value: None, - }], - template: None, - is_constexpr: true, - is_explicit: false, - is_default: true, - is_no_except: true, - is_delete: false, - is_protected: false, - - base_ctor: None, - initialized_values: HashMap::new(), - brief: None, - body: None, - }; - - cpp_type - .declarations - .push(CppMember::ConstructorDecl(default_ctor).into()); - cpp_type - .declarations - .push(CppMember::ConstructorDecl(copy_ctor).into()); - cpp_type - .declarations - .push(CppMember::ConstructorDecl(move_ctor).into()); - - // // Delegates and such are reference types with no inheritance - // if cpp_type.inherit.is_empty() { - // return; - // } - - // let base_type = cpp_type - // .inherit - // .get(0) - // .expect("No parent for reference type?"); - - // cpp_type.declarations.push( - // CppMember::ConstructorDecl(CppConstructorDecl { - // cpp_name: cpp_name.clone(), - // parameters: vec![CppParam { - // name: "ptr".to_string(), - // modifiers: "".to_string(), - // ty: "void*".to_string(), - // def_value: None, - // }], - // template: None, - // is_constexpr: true, - // is_explicit: true, - // is_default: false, - // is_no_except: true, - // is_delete: false, - // is_protected: false, - - // base_ctor: Some((base_type.clone(), "ptr".to_string())), - // initialized_values: HashMap::new(), - // brief: None, - // body: Some(vec![]), - // }) - // .into(), - // ); - } - fn make_interface_constructors(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - let cpp_name = cpp_type.cpp_name().clone(); - - let base_type = cpp_type - .inherit - .first() - .expect("No parent for interface type?"); - - cpp_type.declarations.push( - CppMember::ConstructorDecl(CppConstructorDecl { - cpp_name: cpp_name.clone(), - parameters: vec![CppParam { - name: "ptr".to_string(), - modifiers: "".to_string(), - ty: "void*".to_string(), - def_value: None, - }], - template: None, - is_constexpr: true, - is_explicit: true, - is_default: false, - is_no_except: true, - is_delete: false, - is_protected: false, - - base_ctor: Some((base_type.clone(), "ptr".to_string())), - initialized_values: HashMap::new(), - brief: None, - body: Some(vec![]), - }) - .into(), - ); - } - fn create_ref_default_operators(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - let cpp_name = cpp_type.cpp_name(); - - // Skip if System.ValueType or System.Enum - if cpp_type.namespace() == "System" - && (cpp_type.cpp_name() == "ValueType" || cpp_type.cpp_name() == "Enum") - { - return; - } - - // Delegates and such are reference types with no inheritance - if cpp_type.inherit.is_empty() { - return; - } - - cpp_type.declarations.push( - CppMember::CppLine(CppLine { - line: format!( - " - constexpr {cpp_name}& operator=(std::nullptr_t) noexcept {{ - this->{REFERENCE_WRAPPER_INSTANCE_NAME} = nullptr; - return *this; - }}; - - constexpr {cpp_name}& operator=(void* o) noexcept {{ - this->{REFERENCE_WRAPPER_INSTANCE_NAME} = o; - return *this; - }}; - - constexpr {cpp_name}& operator=({cpp_name}&& o) noexcept = default; - constexpr {cpp_name}& operator=({cpp_name} const& o) noexcept = default; - " - ), - }) - .into(), - ); - } - - fn delete_move_ctor(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - let t = &cpp_type.cpp_name_components.name; - - let move_ctor = CppConstructorDecl { - cpp_name: t.clone(), - parameters: vec![CppParam { - def_value: None, - modifiers: "&&".to_string(), - name: "".to_string(), - ty: t.clone(), - }], - template: None, - is_constexpr: false, - is_explicit: false, - is_default: false, - is_no_except: false, - is_protected: false, - is_delete: true, - base_ctor: None, - initialized_values: Default::default(), - brief: Some("delete move ctor to prevent accidental deref moves".to_string()), - body: None, - }; - - cpp_type - .declarations - .push(CppMember::ConstructorDecl(move_ctor).into()); - } - - fn delete_copy_ctor(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - let t = &cpp_type.cpp_name_components.name; - - let move_ctor = CppConstructorDecl { - cpp_name: t.clone(), - parameters: vec![CppParam { - def_value: None, - modifiers: "const&".to_string(), - name: "".to_string(), - ty: t.clone(), - }], - template: None, - is_constexpr: false, - is_explicit: false, - is_default: false, - is_no_except: false, - is_delete: true, - is_protected: false, - base_ctor: None, - initialized_values: Default::default(), - brief: Some("delete copy ctor to prevent accidental deref copies".to_string()), - body: None, - }; - - cpp_type - .declarations - .push(CppMember::ConstructorDecl(move_ctor).into()); - } - - fn add_default_ctor(&mut self, protected: bool) { - let cpp_type = self.get_mut_cpp_type(); - let t = &cpp_type.cpp_name_components.name; - - let default_ctor_decl = CppConstructorDecl { - cpp_name: t.clone(), - parameters: vec![], - template: None, - is_constexpr: true, - is_explicit: false, - is_default: false, - is_no_except: false, - is_delete: false, - is_protected: protected, - base_ctor: None, - initialized_values: Default::default(), - brief: Some("default ctor".to_string()), - body: None, - }; - - let default_ctor_impl = CppConstructorImpl { - body: vec![], - declaring_full_name: cpp_type.cpp_name_components.remove_pointer().combine_all(), - template: cpp_type.cpp_template.clone(), - ..default_ctor_decl.clone().into() - }; - - cpp_type - .declarations - .push(CppMember::ConstructorDecl(default_ctor_decl).into()); - - cpp_type - .implementations - .push(CppMember::ConstructorImpl(default_ctor_impl).into()); - } - - fn add_type_index_member(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - let tdi: TypeDefinitionIndex = cpp_type.self_tag.get_tdi(); - - let il2cpp_metadata_type_index = CppFieldDecl { - cpp_name: "__IL2CPP_TYPE_DEFINITION_INDEX".into(), - field_ty: "uint32_t".into(), - offset: u32::MAX, - instance: false, - readonly: true, - const_expr: true, - value: Some(tdi.index().to_string()), - brief_comment: Some("IL2CPP Metadata Type Index".into()), - is_private: false, - }; - - cpp_type - .declarations - .push(CppMember::FieldDecl(il2cpp_metadata_type_index).into()); - } - - fn delete_default_ctor(&mut self) { - let cpp_type = self.get_mut_cpp_type(); - let t = &cpp_type.cpp_name_components.name; - - let default_ctor = CppConstructorDecl { - cpp_name: t.clone(), - parameters: vec![], - template: None, - is_constexpr: false, - is_explicit: false, - is_default: false, - is_no_except: false, - is_delete: true, - is_protected: false, - base_ctor: None, - initialized_values: Default::default(), - brief: Some( - "delete default ctor to prevent accidental value type instantiations of ref types" - .to_string(), - ), - body: None, - }; - - cpp_type - .declarations - .push(CppMember::ConstructorDecl(default_ctor).into()); - } - - fn create_ref_constructor( - cpp_type: &mut CppType, - declaring_type: &Il2CppTypeDefinition, - m_params: &[CppParam], - template: &Option, - ) { - if declaring_type.is_value_type() || declaring_type.is_enum_type() { - return; - } - - let params_no_default = m_params - .iter() - .cloned() - .map(|mut c| { - c.def_value = None; - c - }) - .collect_vec(); - - let ty_full_cpp_name = cpp_type.cpp_name_components.combine_all(); - - let decl: CppMethodDecl = CppMethodDecl { - cpp_name: "New_ctor".into(), - return_type: ty_full_cpp_name.clone(), - parameters: params_no_default, - template: template.clone(), - body: None, // TODO: - brief: None, - is_no_except: false, - is_constexpr: false, - instance: false, - is_const: false, - is_implicit_operator: false, - is_explicit_operator: false, - - is_virtual: false, - is_inline: true, - prefix_modifiers: vec![], - suffix_modifiers: vec![], - }; - - // To avoid trailing ({},) - let base_ctor_params = CppParam::params_names(&decl.parameters).join(", "); - - let allocate_call = format!( - "THROW_UNLESS(::il2cpp_utils::NewSpecific<{ty_full_cpp_name}>({base_ctor_params}))" - ); - - let declaring_template = if cpp_type - .cpp_template - .as_ref() - .is_some_and(|t| !t.names.is_empty()) - { - cpp_type.cpp_template.clone() - } else { - None - }; - - let cpp_constructor_impl = CppMethodImpl { - body: vec![Arc::new(CppLine::make(format!("return {allocate_call};")))], - - declaring_cpp_full_name: cpp_type.cpp_name_components.remove_pointer().combine_all(), - parameters: m_params.to_vec(), - template: declaring_template, - ..decl.clone().into() - }; - - cpp_type - .implementations - .push(CppMember::MethodImpl(cpp_constructor_impl).into()); - - cpp_type - .declarations - .push(CppMember::MethodDecl(decl).into()); - } - - fn create_method( - &mut self, - declaring_type: &Il2CppTypeDefinition, - method_index: MethodIndex, - - metadata: &Metadata, - ctx_collection: &CppContextCollection, - config: &GenerationConfig, - is_generic_method_inst: bool, - ) { - let method = &metadata.metadata.global_metadata.methods[method_index]; - let cpp_type = self.get_mut_cpp_type(); - - // TODO: sanitize method name for c++ - let m_name = method.name(metadata.metadata); - if m_name == ".cctor" { - // info!("Skipping {}", m_name); - return; - } - - let m_ret_type = metadata - .metadata_registration - .types - .get(method.return_type as usize) - .unwrap(); - - let m_params_with_def: Vec = cpp_type.make_parameters( - method, - method_index, - is_generic_method_inst, - metadata, - config, - ctx_collection, - ); - - let m_params_no_def: Vec = m_params_with_def - .iter() - .cloned() - .map(|mut p| { - p.def_value = None; - p - }) - .collect_vec(); - - // TODO: Add template if a generic inst e.g - // T UnityEngine.Component::GetComponent() -> bs_hook::Il2CppWrapperType UnityEngine.Component::GetComponent() - let template = if method.generic_container_index.is_valid() { - match is_generic_method_inst { - true => Some(CppTemplate { names: vec![] }), - false => { - let generics = method - .generic_container(metadata.metadata) - .unwrap() - .generic_parameters(metadata.metadata) - .iter() - .map(|param| param.name(metadata.metadata).to_string()); - - Some(CppTemplate::make_typenames(generics)) - } - } - } else { - None - }; - - let declaring_type_template = if cpp_type - .cpp_template + let _declaring_type_template = self + .generic_template .as_ref() .is_some_and(|t| !t.names.is_empty()) - { - cpp_type.cpp_template.clone() - } else { - None - }; - - let literal_types = if is_generic_method_inst { - cpp_type - .method_generic_instantiation_map - .get(&method_index) - .cloned() - } else { - None - }; - - let resolved_generic_types = literal_types.map(|literal_types| { - literal_types - .iter() - .map(|t| &metadata.metadata_registration.types[*t as usize]) - .map(|t| { - cpp_type - .cppify_name_il2cpp(ctx_collection, metadata, t, 0, TypeUsage::GenericArg) - .combine_all() - }) - .collect_vec() - }); + .then(|| self.generic_template.clone()); - // Lazy cppify - let make_ret_cpp_type_name = |cpp_type: &mut CppType| -> String { - let full_name = m_ret_type.full_name(metadata.metadata); - if full_name == "System.Enum" { - cpp_type.requirements.needs_enum_include(); - ENUM_PTR_TYPE.into() - } else if full_name == "System.ValueType" { - cpp_type.requirements.needs_value_include(); - VT_PTR_TYPE.into() - } else { - cpp_type - .cppify_name_il2cpp( - ctx_collection, - metadata, - m_ret_type, - 0, - TypeUsage::ReturnType, - ) - .combine_all() - } - }; + let _literal_types = is_generic_method_inst + .then(|| { + self.method_generic_instantiation_map + .get(&method_index) + .cloned() + }) + .flatten(); - let m_ret_cpp_type_name = { - let fixup_name = match is_generic_method_inst { - false => cpp_type.il2cpp_mvar_use_param_name( - metadata, - method_index, - make_ret_cpp_type_name, - m_ret_type, - ), - true => make_ret_cpp_type_name(cpp_type), - }; + let method_calc = metadata.method_calculations.get(&method_index); - cpp_type.il2cpp_byref(fixup_name, m_ret_type) - }; + let mut flag = CSMethodFlags::empty(); - // Reference type constructor - if m_name == ".ctor" { - Self::create_ref_constructor(cpp_type, declaring_type, &m_params_with_def, &template); + if method.is_final_method() { + flag = flag.union(CSMethodFlags::FINAL); + } + if method.is_virtual_method() { + flag = flag.union(CSMethodFlags::VIRTUAL); + } + if method.is_static_method() { + flag = flag.union(CSMethodFlags::STATIC); + } + if method.is_virtual_method() { + flag = flag.union(CSMethodFlags::VIRTUAL); + } + if method.is_special_name() { + flag = flag.union(CSMethodFlags::SPECIAL_NAME); } - let cpp_m_name = { - let cpp_m_name = config.name_cpp(m_name); - - // static functions with same name and params but - // different ret types can exist - // so we add their ret types - let fixup_name = match cpp_m_name == "op_Implicit" || cpp_m_name == "op_Explicit" { - true => { - cpp_m_name - + "_" - + &config - .sanitize_to_cpp_name(&m_ret_cpp_type_name) - .replace('*', "_") - } - false => cpp_m_name, - }; - - match &resolved_generic_types { - Some(resolved_generic_types) => { - format!("{fixup_name}<{}>", resolved_generic_types.join(", ")) - } - None => fixup_name, - } - }; - - let declaring_type = method.declaring_type(metadata.metadata); - let tag = CppTypeTag::TypeDefinitionIndex(method.declaring_type); - - let method_calc = metadata.method_calculations.get(&method_index); - - // generic methods don't have definitions if not an instantiation - let method_stub = !is_generic_method_inst && template.is_some(); - let method_decl = CppMethodDecl { - body: None, + let mut method_decl = CsMethod { brief: format!( "Method {m_name}, addr 0x{:x}, size 0x{:x}, virtual {}, abstract: {}, final {}", method_calc.map(|m| m.addrs).unwrap_or(u64::MAX), @@ -2423,265 +747,49 @@ pub trait CSType: Sized { method.is_final_method() ) .into(), - is_const: false, - is_constexpr: false, - is_no_except: false, - cpp_name: cpp_m_name.clone(), - return_type: m_ret_cpp_type_name.clone(), - parameters: m_params_no_def.clone(), - instance: !method.is_static_method(), - template: template.clone(), - suffix_modifiers: Default::default(), - prefix_modifiers: Default::default(), - is_virtual: false, - is_implicit_operator: false, - is_explicit_operator: false, - - is_inline: true, - }; - - let instance_ptr: String = if method.is_static_method() { - "nullptr".into() - } else { - "this".into() - }; - - const METHOD_INFO_VAR_NAME: &str = "___internal_method"; - - let method_invoke_params = vec![instance_ptr.as_str(), METHOD_INFO_VAR_NAME]; - let param_names = CppParam::params_names(&method_decl.parameters).map(|s| s.as_str()); - let declaring_type_cpp_full_name = - cpp_type.cpp_name_components.remove_pointer().combine_all(); - - let declaring_classof_call = format!( - "::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class<{}>::get()", - cpp_type.cpp_name_components.combine_all() - ); - - let extract_self_class = - "il2cpp_functions::object_get_class(reinterpret_cast(this))"; - - let params_types_format: String = CppParam::params_types(&method_decl.parameters) - .map(|t| format!("::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_type<{t}>::get()")) - .join(", "); - let params_types_count = method_decl.parameters.len(); - - let resolve_instance_slot_lines = if method.slot != u16::MAX { - let slot = &method.slot; - vec![format!( - "auto* {METHOD_INFO_VAR_NAME} = THROW_UNLESS((::il2cpp_utils::ResolveVtableSlot( - {extract_self_class}, - {declaring_classof_call}, - {slot} - )));" - )] - } else { - vec![] - }; - - // TODO: link the method to the interface that originally declared it - // then the resolve should look something like: - // resolve(classof(GlobalNamespace::BeatmapLevelPack*), classof(GlobalNamespace::IBeatmapLevelPack*), 0); - // that way the resolve should work correctly, but it should only happen like that for non-interfaces - - let _resolve_metadata_slot_lines = if method.slot != u16::MAX { - let self_classof_call = ""; - let declaring_classof_call = ""; - let slot = &method.slot; - - vec![format!( - "auto* {METHOD_INFO_VAR_NAME} = THROW_UNLESS((::il2cpp_utils::ResolveVtableSlot( - {self_classof_call}, - {declaring_classof_call}, - {slot} - )));" - )] - } else { - vec![] - }; - - // if no params, just empty span - // avoid allocs - let params_types_array_cpp = match params_types_count { - 0 => "::std::span()".to_string(), - _ => format!( - "::std::array{{{params_types_format}}}" + method_flags: flag, + method_index, + name: m_name.to_string(), + return_type: type_resolver.resolve_type( + self, + method.return_type as usize, + TypeUsage::ReturnType, + true, ), - }; - - let method_info_lines = match &template { - Some(template) => { - // generic - let template_names = template - .just_names() - .map(|t| { - format!( - "::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class<{t}>::get()" - ) - }) - .join(", "); - let template_count = template.names.len(); - - // if no template params, just empty span - // avoid allocs - let template_classes_array_cpp = match template_count { - 0 => "std::span()".to_string(), - _ => format!( - "std::array{{{template_names}}}" - ), - }; - - vec![ - format!("static auto* ___internal_method_base = THROW_UNLESS((::il2cpp_utils::FindMethod( - {declaring_classof_call}, - \"{m_name}\", - {template_classes_array_cpp}, - {params_types_array_cpp} - )));"), - format!("static auto* {METHOD_INFO_VAR_NAME} = THROW_UNLESS(::il2cpp_utils::MakeGenericMethod( - ___internal_method_base, - {template_classes_array_cpp} - ));"), - ] - } - None => { - vec![ - format!("static auto* {METHOD_INFO_VAR_NAME} = THROW_UNLESS((::il2cpp_utils::FindMethod( - {declaring_classof_call}, - \"{m_name}\", - std::span(), - {params_types_array_cpp} - )));"), - ] - } - }; - - let method_body_lines = [format!( - "return ::cordl_internals::RunMethodRethrow<{m_ret_cpp_type_name}, false>({});", - method_invoke_params - .into_iter() - .chain(param_names) - .join(", ") - )]; - - // instance methods should resolve slots if this is an interface, or if this is a virtual/abstract method, and not a final method - // static methods can't be virtual or interface anyway so checking for that here is irrelevant - let should_resolve_slot = cpp_type.is_interface - || ((method.is_virtual_method() || method.is_abstract_method()) - && !method.is_final_method()); - - let method_body = match should_resolve_slot { - true => resolve_instance_slot_lines - .iter() - .chain(method_body_lines.iter()) - .cloned() - .map(|l| -> Arc { Arc::new(CppLine::make(l)) }) - .collect_vec(), - false => method_info_lines - .iter() - .chain(method_body_lines.iter()) - .cloned() - .map(|l| -> Arc { Arc::new(CppLine::make(l)) }) - .collect_vec(), - }; - - let method_impl = CppMethodImpl { - body: method_body, - parameters: m_params_with_def.clone(), - brief: None, - declaring_cpp_full_name: declaring_type_cpp_full_name, + parameters: m_params_no_def.clone(), instance: !method.is_static_method(), - suffix_modifiers: Default::default(), - prefix_modifiers: Default::default(), template: template.clone(), - declaring_type_template: declaring_type_template.clone(), - - // defaults - ..method_decl.clone().into() + method_data: Default::default(), }; - // check if declaring type is the current type or the interface - // we check TDI because if we are a generic instantiation - // we just use ourselves if the declaring type is also the same TDI - let interface_declaring_cpp_type: Option<&CppType> = - if tag.get_tdi() == cpp_type.self_tag.get_tdi() { - Some(cpp_type) - } else { - ctx_collection.get_cpp_type(tag) - }; - - // don't emit method size structs for generic methods - - // don't emit method size structs for generic methods - // if type is a generic - let has_template_args = cpp_type - .cpp_template + let has_template_args = self + .generic_template .as_ref() .is_some_and(|t| !t.names.is_empty()); // don't emit method size structs for generic methods - if let Some(method_calc) = method_calc - && template.is_none() - && !has_template_args - && !is_generic_method_inst - { - cpp_type - .nonmember_implementations - .push(Rc::new(CppNonMember::SizeStruct( - CppMethodSizeStruct { - ret_ty: method_decl.return_type.clone(), - cpp_method_name: method_decl.cpp_name.clone(), - method_name: m_name.to_string(), - declaring_type_name: method_impl.declaring_cpp_full_name.clone(), - declaring_classof_call, - method_info_lines, - method_info_var: METHOD_INFO_VAR_NAME.to_string(), - instance: method_decl.instance, - params: method_decl.parameters.clone(), - template: template.clone(), - generic_literals: resolved_generic_types, - method_data: CppMethodData { - addrs: method_calc.addrs, - estimated_size: method_calc.estimated_size, - }, - interface_clazz_of: interface_declaring_cpp_type - .map(|d| d.classof_cpp_name()) - .unwrap_or_else(|| format!("Bad stuff happened {declaring_type:?}")), - is_final: method.is_final_method(), - slot: if method.slot != u16::MAX { - Some(method.slot) - } else { - None - }, - } - .into(), - ))); - } - - // TODO: Revise this - const ALLOW_GENERIC_METHOD_STUBS_IMPL: bool = true; - // If a generic instantiation or not a template - if !method_stub || ALLOW_GENERIC_METHOD_STUBS_IMPL { - cpp_type - .implementations - .push(CppMember::MethodImpl(method_impl).into()); + if let Some(method_calc) = method_calc { + let is_concrete = !method.is_abstract_method(); + method_decl.method_data = CsMethodData { + addrs: is_concrete.then_some(method_calc.addrs), + estimated_size: is_concrete.then_some(method_calc.estimated_size), + slot: (method.slot != u16::MAX).then_some(method.slot), + } } if !is_generic_method_inst { - cpp_type - .declarations - .push(CppMember::MethodDecl(method_decl).into()); + self.methods.push(method_decl); } } fn default_value_blob( - metadata: &Metadata, + metadata: &CordlMetadata, ty: &Il2CppType, data_index: usize, - string_quotes: bool, - string_as_u16: bool, - ) -> String { + _string_quotes: bool, + _string_as_u16: bool, + ) -> CsValue { let data = &metadata .metadata .global_metadata @@ -2690,100 +798,37 @@ pub trait CSType: Sized { let mut cursor = Cursor::new(data); - const UNSIGNED_SUFFIX: &str = "u"; match ty.ty { - Il2CppTypeEnum::Boolean => (if data[0] == 0 { "false" } else { "true" }).to_string(), - Il2CppTypeEnum::I1 => { - format!("static_cast(0x{:x})", cursor.read_i8().unwrap()) - } - Il2CppTypeEnum::I2 => { - format!( - "static_cast(0x{:x})", - cursor.read_i16::().unwrap() - ) - } - Il2CppTypeEnum::I4 => { - format!( - "static_cast(0x{:x})", - cursor.read_compressed_i32::().unwrap() - ) - } + Il2CppTypeEnum::Boolean => CsValue::Bool(data[0] != 0), + Il2CppTypeEnum::I1 => CsValue::I8(cursor.read_i8().unwrap()), + Il2CppTypeEnum::I2 => CsValue::I16(cursor.read_i16::().unwrap()), + Il2CppTypeEnum::I4 => CsValue::I32(cursor.read_compressed_i32::().unwrap()), // TODO: We assume 64 bit Il2CppTypeEnum::I | Il2CppTypeEnum::I8 => { - format!( - "static_cast(0x{:x})", - cursor.read_i64::().unwrap() - ) - } - Il2CppTypeEnum::U1 => { - format!( - "static_cast(0x{:x}{UNSIGNED_SUFFIX})", - cursor.read_u8().unwrap() - ) - } - Il2CppTypeEnum::U2 => { - format!( - "static_cast(0x{:x}{UNSIGNED_SUFFIX})", - cursor.read_u16::().unwrap() - ) - } - Il2CppTypeEnum::U4 => { - format!( - "static_cast(0x{:x}{UNSIGNED_SUFFIX})", - cursor.read_u32::().unwrap() - ) + CsValue::I64(cursor.read_i64::().unwrap()) } + Il2CppTypeEnum::U1 => CsValue::U8(cursor.read_u8().unwrap()), + Il2CppTypeEnum::U2 => CsValue::U16(cursor.read_u16::().unwrap()), + Il2CppTypeEnum::U4 => CsValue::U32(cursor.read_u32::().unwrap()), // TODO: We assume 64 bit Il2CppTypeEnum::U | Il2CppTypeEnum::U8 => { - format!( - "static_cast(0x{:x}{UNSIGNED_SUFFIX})", - cursor.read_u64::().unwrap() - ) + CsValue::U64(cursor.read_u64::().unwrap()) } // https://learn.microsoft.com/en-us/nimbusml/concepts/types // https://en.cppreference.com/w/cpp/types/floating-point - Il2CppTypeEnum::R4 => { - let val = format!("{}", cursor.read_f32::().unwrap()); - if !val.contains('.') - && val - .find(|c: char| !c.is_ascii_digit() && c != '-') - .is_none() - { - val + ".0" - } else { - val.replace("inf", "INFINITY").replace("NaN", "NAN") - } - } - Il2CppTypeEnum::R8 => { - let val = format!("{}", cursor.read_f64::().unwrap()); - if !val.contains('.') - && val - .find(|c: char| !c.is_ascii_digit() && c != '-') - .is_none() - { - val + ".0" - } else { - val.replace("inf", "INFINITY").replace("NaN", "NAN") - } - } + Il2CppTypeEnum::R4 => CsValue::F32(cursor.read_f32::().unwrap()), + Il2CppTypeEnum::R8 => CsValue::F64(cursor.read_f64::().unwrap()), Il2CppTypeEnum::Char => { let res = String::from_utf16_lossy(&[cursor.read_u16::().unwrap()]) .escape_default() .to_string(); - if string_quotes { - let literal_prefix = if string_as_u16 { "u" } else { "" }; - return format!("{literal_prefix}'{res}'"); - } - - res + CsValue::String(res) } Il2CppTypeEnum::String => { - // UTF-16 byte array len - // which means the len is 2x the size of the string's len let stru16_len = cursor.read_compressed_i32::().unwrap(); if stru16_len == -1 { - return "".to_string(); + return CsValue::String("".to_string()); } let mut buf = vec![0u8; stru16_len as usize]; @@ -2792,49 +837,29 @@ pub trait CSType: Sized { let res = String::from_utf8(buf).unwrap().escape_default().to_string(); - if string_quotes { - let literal_prefix = if string_as_u16 { "u" } else { "" }; - return format!("{literal_prefix}\"{res}\""); - } - - res + CsValue::String(res) } - // Il2CppTypeEnum::Genericinst => match ty.data { - // TypeData::GenericClassIndex(inst_idx) => { - // let gen_class = &metadata - // .metadata - // .runtime_metadata - // .metadata_registration - // .generic_classes[inst_idx]; - - // let inner_ty = &metadata.metadata_registration.types[gen_class.type_index]; - - // Self::default_value_blob( - // metadata, - // inner_ty, - // data_index, - // string_quotes, - // string_as_u16, - // ) - // } - // _ => todo!(), - // }, Il2CppTypeEnum::Genericinst | Il2CppTypeEnum::Byref | Il2CppTypeEnum::Ptr | Il2CppTypeEnum::Array | Il2CppTypeEnum::Object | Il2CppTypeEnum::Class + | Il2CppTypeEnum::Valuetype | Il2CppTypeEnum::Szarray => { - let def = Self::type_default_value(metadata, None, ty); - format!("/* TODO: Fix these default values */ {ty:?} */ {def}") + // let def = Self::type_default_value(metadata, None, ty); + // format!("/* TODO: Fix these default values */ {ty:?} */ {def}") + CsValue::Null } - _ => "unknown".to_string(), + _ => todo!("Unsupported blob type {:#?}", ty), } } - fn unbox_nullable_valuetype<'a>(metadata: &'a Metadata, ty: &'a Il2CppType) -> &'a Il2CppType { + fn unbox_nullable_valuetype<'a>( + metadata: &'a CordlMetadata, + ty: &'a Il2CppType, + ) -> &'a Il2CppType { if let Il2CppTypeEnum::Valuetype = ty.ty { match ty.data { TypeData::TypeDefinitionIndex(tdi) => { @@ -2858,49 +883,10 @@ pub trait CSType: Sized { ty } - fn type_default_value( - metadata: &Metadata, - cpp_type: Option<&CppType>, - ty: &Il2CppType, - ) -> String { - let matched_ty: &Il2CppType = match ty.data { - // get the generic inst - TypeData::GenericClassIndex(inst_idx) => { - let gen_class = &metadata - .metadata - .runtime_metadata - .metadata_registration - .generic_classes[inst_idx]; - - &metadata.metadata_registration.types[gen_class.type_index] - } - // get the underlying type of the generic param - TypeData::GenericParameterIndex(param) => match param.is_valid() { - true => { - let gen_param = &metadata.metadata.global_metadata.generic_parameters[param]; - - cpp_type - .and_then(|cpp_type| { - cpp_type - .generic_instantiations_args_types - .as_ref() - .and_then(|gen_args| gen_args.get(gen_param.num as usize)) - .map(|t| &metadata.metadata_registration.types[*t]) - }) - .unwrap_or(ty) - } - false => ty, - }, - _ => ty, - }; - - match matched_ty.valuetype { - true => "{}".to_string(), - false => "nullptr".to_string(), - } - } - - fn field_default_value(metadata: &Metadata, field_index: FieldIndex) -> Option { + pub fn field_default_value( + metadata: &CordlMetadata, + field_index: FieldIndex, + ) -> Option { metadata .metadata .global_metadata @@ -2917,13 +903,16 @@ pub trait CSType: Sized { // get default value for given type if !def.data_index.is_valid() { - return Self::type_default_value(metadata, None, ty); + return CsValue::Null; } Self::default_value_blob(metadata, ty, def.data_index.index() as usize, true, true) }) } - fn param_default_value(metadata: &Metadata, parameter_index: ParameterIndex) -> Option { + fn param_default_value( + metadata: &CordlMetadata, + parameter_index: ParameterIndex, + ) -> Option { metadata .metadata .global_metadata @@ -2942,7 +931,7 @@ pub trait CSType: Sized { // This occurs when the type is `null` or `default(T)` for value types if !def.data_index.is_valid() { - return Self::type_default_value(metadata, None, ty); + return CsValue::Null; } if let Il2CppTypeEnum::Valuetype = ty.ty { @@ -2969,788 +958,10 @@ pub trait CSType: Sized { }) } - fn il2cpp_byref(&mut self, cpp_name: String, typ: &Il2CppType) -> String { - let requirements = &mut self.get_mut_cpp_type().requirements; - // handle out T or - // ref T when T is a value type - - // typ.valuetype -> false when T& - // apparently even if `T` is a valuetype - if typ.is_param_out() || (typ.byref && !typ.valuetype) { - requirements.needs_byref_include(); - return format!("ByRef<{cpp_name}>"); - } - - if typ.is_param_in() { - requirements.needs_byref_include(); - - return format!("ByRefConst<{cpp_name}>"); - } - - cpp_name - } - - // Basically decides to use the template param name (if applicable) - // instead of the generic instantiation of the type - // TODO: Make this less confusing - fn il2cpp_mvar_use_param_name<'a>( - &mut self, - metadata: &'a Metadata, - method_index: MethodIndex, - // use a lambda to do this lazily - cpp_name: impl FnOnce(&mut CppType) -> String, - typ: &'a Il2CppType, - ) -> String { - let tys = self - .get_mut_cpp_type() - .method_generic_instantiation_map - .remove(&method_index); - - // fast path for generic param name - // otherwise cpp_name() will default to generic param anyways - let ret = match typ.ty { - Il2CppTypeEnum::Mvar => match typ.data { - TypeData::GenericParameterIndex(index) => { - let generic_param = - &metadata.metadata.global_metadata.generic_parameters[index]; - - let owner = generic_param.owner(metadata.metadata); - assert!(owner.is_method != u32::MAX); - - generic_param.name(metadata.metadata).to_string() - } - _ => todo!(), - }, - _ => cpp_name(self.get_mut_cpp_type()), - }; - - if let Some(tys) = tys { - self.get_mut_cpp_type() - .method_generic_instantiation_map - .insert(method_index, tys); - } - - ret - } - - fn cppify_name_il2cpp( - &mut self, - ctx_collection: &CppContextCollection, - metadata: &Metadata, - typ: &Il2CppType, - include_depth: usize, - typ_usage: TypeUsage, - ) -> NameComponents { - let cpp_type = self.get_mut_cpp_type(); - - let mut requirements = cpp_type.requirements.clone(); - - let res = cpp_type.cppify_name_il2cpp_recurse( - &mut requirements, - ctx_collection, - metadata, - typ, - include_depth, - cpp_type.generic_instantiations_args_types.as_ref(), - typ_usage, - ); - - cpp_type.requirements = requirements; - - res - } - - /// [declaring_generic_inst_types] the generic instantiation of the declaring type - fn cppify_name_il2cpp_recurse( - &self, - requirements: &mut CppTypeRequirements, - ctx_collection: &CppContextCollection, - metadata: &Metadata, - typ: &Il2CppType, - include_depth: usize, - declaring_generic_inst_types: Option<&Vec>, - typ_usage: TypeUsage, - ) -> NameComponents { - let add_include = include_depth > 0; - let next_include_depth = if add_include { include_depth - 1 } else { 0 }; - - let typ_tag = typ.data; - - let cpp_type = self.get_cpp_type(); - - match typ.ty { - Il2CppTypeEnum::I1 - | Il2CppTypeEnum::U1 - | Il2CppTypeEnum::I2 - | Il2CppTypeEnum::U2 - | Il2CppTypeEnum::I4 - | Il2CppTypeEnum::U4 - | Il2CppTypeEnum::I8 - | Il2CppTypeEnum::U8 - | Il2CppTypeEnum::I - | Il2CppTypeEnum::U => { - requirements.needs_int_include(); - } - Il2CppTypeEnum::R4 | Il2CppTypeEnum::R8 => { - requirements.needs_math_include(); - } - _ => (), - }; - - let ret = match typ.ty { - // Commented so types use System.Object - // might revert - - // Il2CppTypeEnum::Object => { - // requirements.need_wrapper(); - // OBJECT_WRAPPER_TYPE.to_string() - // } - Il2CppTypeEnum::Object - | Il2CppTypeEnum::Valuetype - | Il2CppTypeEnum::Class - | Il2CppTypeEnum::Typedbyref - // ptr types - | Il2CppTypeEnum::I - | Il2CppTypeEnum::U => { - let typ_cpp_tag: CppTypeTag = typ_tag.into(); - - // handle resolving indirection - let handle_resolving = |to_incl_cpp_ty: &CppType| -> NameComponents { - let mut res = to_incl_cpp_ty.cpp_name_components.clone(); - - for resolve_handler in metadata.custom_type_resolve_handler.iter() { - res = resolve_handler( - res, - to_incl_cpp_ty, - ctx_collection, - metadata, - typ, - typ_usage, - ); - } - - res - }; - - // Self - if typ_cpp_tag == cpp_type.self_tag { - return handle_resolving(cpp_type); - } - - if let TypeData::TypeDefinitionIndex(tdi) = typ.data { - let td = &metadata.metadata.global_metadata.type_definitions[tdi]; - - // TODO: Do we need generic inst types here? Hopefully not! - let _size = offsets::get_sizeof_type(td, tdi, None, metadata); - - if metadata.blacklisted_types.contains(&tdi) { - // classes should return Il2CppObject* - if typ.ty == Il2CppTypeEnum::Class { - return NameComponents { - name: IL2CPP_OBJECT_TYPE.to_string(), - is_pointer: true, - generics: None, - namespace: None, - declaring_types: None, - }; - } - return wrapper_type_for_tdi(td).to_string().into(); - } - } - - if add_include { - requirements.add_dependency_tag(typ_cpp_tag); - } - - // In this case, just inherit the type - // But we have to: - // - Determine where to include it from - let to_incl = ctx_collection.get_context(typ_cpp_tag).unwrap_or_else(|| { - let t = &metadata.metadata.global_metadata.type_definitions - [Self::get_tag_tdi(typ.data)]; - - panic!( - "no context for type {typ:?} {}", - t.full_name(metadata.metadata, true) - ) - }); - - let other_context_ty = ctx_collection.get_context_root_tag(typ_cpp_tag); - let own_context_ty = ctx_collection.get_context_root_tag(cpp_type.self_tag); - - let typedef_incl = CppInclude::new_context_typedef(to_incl); - let typeimpl_incl = CppInclude::new_context_typeimpl(to_incl); - let to_incl_cpp_ty = ctx_collection - .get_cpp_type(typ.data.into()) - .unwrap_or_else(|| panic!("Unable to get type to include {:?}", typ.data)); - - let own_context = other_context_ty == own_context_ty; - - // - Include it - // Skip including the context if we're already in it - if !own_context { - match add_include { - // add def include - true => { - requirements - .add_def_include(Some(to_incl_cpp_ty), typedef_incl.clone()); - requirements - .add_impl_include(Some(to_incl_cpp_ty), typeimpl_incl.clone()); - } - // TODO: Remove? - // ignore nested types - // false if to_incl_cpp_ty.nested => { - // TODO: What should we do here? - // error!("Can't forward declare nested type! Including!"); - // requirements.add_include(Some(to_incl_cpp_ty), inc); - // } - // forward declare - false => { - requirements.add_forward_declare(( - CppForwardDeclare::from_cpp_type(to_incl_cpp_ty), - typedef_incl, - )); - } - } - } - - handle_resolving(to_incl_cpp_ty) - - // match to_incl_cpp_ty.is_enum_type || to_incl_cpp_ty.is_value_type { - // true => ret, - // false => format!("{ret}*"), - // } - } - // Single dimension array - Il2CppTypeEnum::Szarray => { - requirements.needs_arrayw_include(); - - let generic = match typ.data { - TypeData::TypeIndex(e) => { - let ty = &metadata.metadata_registration.types[e]; - - cpp_type.cppify_name_il2cpp_recurse( - requirements, - ctx_collection, - metadata, - ty, - include_depth, - declaring_generic_inst_types, - typ_usage, - ) - } - - _ => panic!("Unknown type data for array {typ:?}!"), - }; - - let generic_formatted = generic.combine_all(); - - NameComponents { - name: "ArrayW".into(), - namespace: Some("".into()), - generics: Some(vec![ - generic_formatted.clone(), - format!("::Array<{generic_formatted}>*"), - ]), - is_pointer: false, - ..Default::default() - } - } - // multi dimensional array - Il2CppTypeEnum::Array => { - // FIXME: when stack further implements the TypeData::ArrayType we can actually implement this fully to be a multidimensional array, whatever that might mean - warn!("Multidimensional array was requested but this is not implemented, typ: {typ:?}, instead returning Il2CppObject!"); - NameComponents { - name: IL2CPP_OBJECT_TYPE.to_string(), - is_pointer: true, - generics: None, - namespace: None, - declaring_types: None, - } - } - Il2CppTypeEnum::Mvar => match typ.data { - TypeData::GenericParameterIndex(index) => { - let generic_param: &brocolib::global_metadata::Il2CppGenericParameter = - &metadata.metadata.global_metadata.generic_parameters[index]; - - let owner = generic_param.owner(metadata.metadata); - assert!(owner.is_method != u32::MAX); - - let (_gen_param_idx, gen_param) = owner - .generic_parameters(metadata.metadata) - .iter() - .find_position(|&p| p.name_index == generic_param.name_index) - .unwrap(); - - let method_index = MethodIndex::new(owner.owner_index); - let _method = &metadata.metadata.global_metadata.methods[method_index]; - - let method_args_opt = - cpp_type.method_generic_instantiation_map.get(&method_index); - - if method_args_opt.is_none() { - return gen_param.name(metadata.metadata).to_string().into(); - } - - let method_args = method_args_opt.unwrap(); - - let ty_idx = method_args[gen_param.num as usize]; - let ty = metadata - .metadata_registration - .types - .get(ty_idx as usize) - .unwrap(); - - cpp_type.cppify_name_il2cpp_recurse( - requirements, - ctx_collection, - metadata, - ty, - include_depth, - declaring_generic_inst_types, - TypeUsage::GenericArg, - ) - } - _ => todo!(), - }, - Il2CppTypeEnum::Var => match typ.data { - // Il2CppMetadataGenericParameterHandle - TypeData::GenericParameterIndex(index) => { - let generic_param: &brocolib::global_metadata::Il2CppGenericParameter = - &metadata.metadata.global_metadata.generic_parameters[index]; - - let owner = generic_param.owner(metadata.metadata); - let (_gen_param_idx, _gen_param) = owner - .generic_parameters(metadata.metadata) - .iter() - .find_position(|&p| p.name_index == generic_param.name_index) - .unwrap(); - - let ty_idx_opt = cpp_type - .generic_instantiations_args_types - .as_ref() - .and_then(|args| args.get(generic_param.num as usize)) - .cloned(); - - // if template arg is not found - if ty_idx_opt.is_none() { - let gen_name = generic_param.name(metadata.metadata); - - // true if the type is intentionally a generic template type and not a specialization - let has_generic_template = - cpp_type.cpp_template.as_ref().is_some_and(|template| { - template.just_names().any(|name| name == gen_name) - }); - - return match has_generic_template { - true => gen_name.to_string().into(), - false => panic!("/* TODO: FIX THIS, THIS SHOULDN'T HAPPEN! NO GENERIC INST ARGS FOUND HERE */ {gen_name}"), - }; - } - - let ty_var = &metadata.metadata_registration.types[ty_idx_opt.unwrap()]; - - let generics = &cpp_type - .cpp_name_components - .generics - .as_ref() - .expect("Generic instantiation args not made yet!"); - - let resolved_var = generics - .get(generic_param.num as usize) - .expect("No generic parameter at index found!") - .clone(); - - let is_pointer = !ty_var.valuetype - // if resolved_var exists in generic template, it can't be a pointer! - && (cpp_type.cpp_template.is_none() - || !cpp_type - .cpp_template - .as_ref() - .is_some_and(|t| t.just_names().any(|s| s == &resolved_var))); - - NameComponents { - is_pointer, - name: resolved_var, - ..Default::default() - } - - // This is for calculating on the fly - // which is slower and won't work for the reference type lookup fix - // we do in make_generic_args - - // let ty_idx = ty_idx_opt.unwrap(); - - // let ty = metadata - // .metadata_registration - // .types - // .get(ty_idx as usize) - // .unwrap(); - // self.cppify_name_il2cpp(ctx_collection, metadata, ty, add_include) - } - _ => todo!(), - }, - Il2CppTypeEnum::Genericinst => match typ.data { - TypeData::GenericClassIndex(e) => { - let mr = &metadata.metadata_registration; - let generic_class = mr.generic_classes.get(e).unwrap(); - let generic_inst = mr - .generic_insts - .get(generic_class.context.class_inst_idx.unwrap()) - .unwrap(); - - let new_generic_inst_types = &generic_inst.types; - - let generic_type_def = &mr.types[generic_class.type_index]; - let TypeData::TypeDefinitionIndex(tdi) = generic_type_def.data else { - panic!() - }; - - if add_include { - let generic_tag = CppTypeTag::from_type_data(typ.data, metadata.metadata); - - // depend on both tdi and generic instantiation - requirements.add_dependency_tag(tdi.into()); - requirements.add_dependency_tag(generic_tag); - } - - let generic_types_formatted = new_generic_inst_types - // let generic_types_formatted = new_generic_inst_types - .iter() - .map(|t| mr.types.get(*t).unwrap()) - // if t is a Var, we use the generic inst provided by the caller - // TODO: This commented code breaks generic params where we intentionally use the template name - // .map(|inst_t| match inst_t.data { - // TypeData::GenericParameterIndex(gen_param_idx) => { - // let gen_param = - // &metadata.metadata.global_metadata.generic_parameters - // [gen_param_idx]; - // declaring_generic_inst_types - // .and_then(|declaring_generic_inst_types| { - // // TODO: Figure out why we this goes out of bounds - // declaring_generic_inst_types.get(gen_param.num as usize) - // }) - // .map(|t| &mr.types[*t]) - // // fallback to T since generic typedefs can be called - // .unwrap_or(inst_t) - // } - // _ => inst_t, - // }) - .map(|gen_arg_t| { - let should_include = gen_arg_t.valuetype; - let gen_include_detch = match should_include { - true => next_include_depth, - false => 0, - }; - - cpp_type.cppify_name_il2cpp_recurse( - requirements, - ctx_collection, - metadata, - gen_arg_t, - gen_include_detch, - // use declaring generic inst since we're cppifying generic args - declaring_generic_inst_types, - TypeUsage::GenericArg, - ) - }) - .map(|n| n.combine_all()) - .collect_vec(); - - let generic_type_def = &mr.types[generic_class.type_index]; - let type_def_name_components = cpp_type.cppify_name_il2cpp_recurse( - requirements, - ctx_collection, - metadata, - generic_type_def, - include_depth, - Some(new_generic_inst_types), - typ_usage, - ); - - // add generics to type def - NameComponents { - generics: Some(generic_types_formatted), - ..type_def_name_components - } - } - - _ => panic!("Unknown type data for generic inst {typ:?}!"), - }, - Il2CppTypeEnum::I1 => "int8_t".to_string().into(), - Il2CppTypeEnum::I2 => "int16_t".to_string().into(), - Il2CppTypeEnum::I4 => "int32_t".to_string().into(), - Il2CppTypeEnum::I8 => "int64_t".to_string().into(), - Il2CppTypeEnum::U1 => "uint8_t".to_string().into(), - Il2CppTypeEnum::U2 => "uint16_t".to_string().into(), - Il2CppTypeEnum::U4 => "uint32_t".to_string().into(), - Il2CppTypeEnum::U8 => "uint64_t".to_string().into(), - - // https://learn.microsoft.com/en-us/nimbusml/concepts/types - // https://en.cppreference.com/w/cpp/types/floating-point - Il2CppTypeEnum::R4 => "float_t".to_string().into(), - Il2CppTypeEnum::R8 => "double_t".to_string().into(), - - Il2CppTypeEnum::Void => "void".to_string().into(), - Il2CppTypeEnum::Boolean => "bool".to_string().into(), - Il2CppTypeEnum::Char => "char16_t".to_string().into(), - Il2CppTypeEnum::String => { - requirements.needs_stringw_include(); - "::StringW".to_string().into() - } - Il2CppTypeEnum::Ptr => { - let generic = match typ.data { - TypeData::TypeIndex(e) => { - let ty = &metadata.metadata_registration.types[e]; - cpp_type.cppify_name_il2cpp_recurse( - requirements, - ctx_collection, - metadata, - ty, - include_depth, - declaring_generic_inst_types, - typ_usage, - ) - } - - _ => panic!("Unknown type data for array {typ:?}!"), - }; - - let generic_formatted = generic.combine_all(); - - NameComponents { - namespace: Some("cordl_internals".into()), - generics: Some(vec![generic_formatted]), - name: "Ptr".into(), - ..Default::default() - } - } - // Il2CppTypeEnum::Typedbyref => { - // // TODO: test this - // if add_include && let TypeData::TypeDefinitionIndex(tdi) = typ.data { - // cpp_type.requirements.add_dependency_tag(tdi.into()); - // } - - // "::System::TypedReference".to_string() - // // "::cordl_internals::TypedByref".to_string() - // }, - // TODO: Void and the other primitives - _ => format!("/* UNKNOWN TYPE! {typ:?} */").into(), - }; - - ret - } - - fn classof_cpp_name(&self) -> String { - format!( - "::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class<{}>::get", - self.get_cpp_type().cpp_name_components.combine_all() - ) - } - - fn type_name_byref_fixup(ty: &Il2CppType, name: &str) -> String { - match ty.valuetype { - true => name.to_string(), - false => format!("{name}*"), - } - } - - fn get_type_definition<'a>( - metadata: &'a Metadata, + pub fn get_type_definition<'a>( + metadata: &'a CordlMetadata, tdi: TypeDefinitionIndex, ) -> &'a Il2CppTypeDefinition { &metadata.metadata.global_metadata.type_definitions[tdi] } } - -fn wrapper_type_for_tdi(td: &Il2CppTypeDefinition) -> &str { - if td.is_enum_type() { - return ENUM_WRAPPER_TYPE; - } - - if td.is_value_type() { - return VALUE_WRAPPER_TYPE; - } - - if td.is_interface() { - return INTERFACE_WRAPPER_TYPE; - } - - IL2CPP_OBJECT_TYPE -} - -/// -/// This makes generic args for types such as ValueTask> work -/// by recursively checking if any generic arg is a reference or numeric type (for enums) -/// -fn parse_generic_arg( - t: &Il2CppType, - gen_name: String, - cpp_type: &mut CppType, - ctx_collection: &CppContextCollection, - metadata: &Metadata<'_>, - template_args: &mut Vec<(String, String)>, -) -> NameComponents { - // If reference type, we use a template and add a requirement - if !t.valuetype { - template_args.push(( - CORDL_REFERENCE_TYPE_CONSTRAINT.to_string(), - gen_name.clone(), - )); - return gen_name.into(); - } - - /* - mscorelib.xml - - - - - - - - - - */ - let enum_system_type_discriminator = match t.data { - TypeData::TypeDefinitionIndex(tdi) => { - let td = &metadata.metadata.global_metadata.type_definitions[tdi]; - let namespace = td.namespace(metadata.metadata); - let name = td.name(metadata.metadata); - - if namespace == "System" { - match name { - "SByteEnum" => Some(Il2CppTypeEnum::I1), - "Int16Enum" => Some(Il2CppTypeEnum::I2), - "Int32Enum" => Some(Il2CppTypeEnum::I4), - "Int64Enum" => Some(Il2CppTypeEnum::I8), - "ByteEnum" => Some(Il2CppTypeEnum::U1), - "UInt16Enum" => Some(Il2CppTypeEnum::U2), - "UInt32Enum" => Some(Il2CppTypeEnum::U4), - "UInt64Enum" => Some(Il2CppTypeEnum::U8), - _ => None, - } - } else { - None - } - } - _ => None, - }; - - let inner_enum_type = enum_system_type_discriminator.map(|e| Il2CppType { - attrs: u16::MAX, - byref: false, - data: TypeData::TypeIndex(usize::MAX), - pinned: false, - ty: e, - valuetype: true, - }); - - // if int, int64 etc. - // this allows for enums to be supported - // if matches!( - // t.ty, - // Il2CppTypeEnum::I1 - // | Il2CppTypeEnum::I2 - // | Il2CppTypeEnum::I4 - // | Il2CppTypeEnum::I8 - // | Il2CppTypeEnum::U1 - // | Il2CppTypeEnum::U2 - // | Il2CppTypeEnum::U4 - // | Il2CppTypeEnum::U8 - // ) || - if let Some(inner_enum_type) = inner_enum_type { - let inner_enum_type_cpp = cpp_type - .cppify_name_il2cpp( - ctx_collection, - metadata, - &inner_enum_type, - 0, - TypeUsage::GenericArg, - ) - .combine_all(); - - template_args.push(( - format!("{CORDL_NUM_ENUM_TYPE_CONSTRAINT}<{inner_enum_type_cpp}>",), - gen_name.clone(), - )); - - return gen_name.into(); - } - - let inner_type = - cpp_type.cppify_name_il2cpp(ctx_collection, metadata, t, 0, TypeUsage::TypeName); - - match t.data { - TypeData::GenericClassIndex(gen_class_idx) => { - let gen_class = &metadata.metadata_registration.generic_classes[gen_class_idx]; - let gen_class_ty = &metadata.metadata_registration.types[gen_class.type_index]; - let TypeData::TypeDefinitionIndex(gen_class_tdi) = gen_class_ty.data else { - todo!() - }; - let gen_class_td = &metadata.metadata.global_metadata.type_definitions[gen_class_tdi]; - - let gen_container = gen_class_td.generic_container(metadata.metadata); - - let gen_class_inst = &metadata.metadata_registration.generic_insts - [gen_class.context.class_inst_idx.unwrap()]; - - // this relies on the fact TDIs do not include their generic params - let non_generic_inner_type = cpp_type.cppify_name_il2cpp( - ctx_collection, - metadata, - gen_class_ty, - 0, - TypeUsage::GenericArg, - ); - - let inner_generic_params = gen_class_inst - .types - .iter() - .enumerate() - .map(|(param_idx, u)| { - let t = metadata.metadata_registration.types.get(*u).unwrap(); - let gen_param = gen_container - .generic_parameters(metadata.metadata) - .iter() - .find(|p| p.num as usize == param_idx) - .expect("No generic param at this num"); - - (t, gen_param) - }) - .map(|(t, gen_param)| { - let inner_gen_name = gen_param.name(metadata.metadata).to_owned(); - let mangled_gen_name = - format!("{inner_gen_name}_cordlgen_{}", template_args.len()); - parse_generic_arg( - t, - mangled_gen_name, - cpp_type, - ctx_collection, - metadata, - template_args, - ) - }) - .map(|n| n.combine_all()) - .collect_vec(); - - NameComponents { - generics: Some(inner_generic_params), - ..non_generic_inner_type - } - } - _ => inner_type, - } -} - -impl CSType for CppType { - #[inline(always)] - fn get_mut_cpp_type(&mut self) -> &mut CppType { - self - } - - #[inline(always)] - fn get_cpp_type(&self) -> &CppType { - self - } -} diff --git a/src/generate/cpp_type_tag.rs b/src/generate/cs_type_tag.rs similarity index 72% rename from src/generate/cpp_type_tag.rs rename to src/generate/cs_type_tag.rs index 8f1502816..83a5aab27 100644 --- a/src/generate/cpp_type_tag.rs +++ b/src/generate/cs_type_tag.rs @@ -18,18 +18,18 @@ pub struct GenericInstantiation { // Unique identifier for a CppType #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub enum CppTypeTag { +pub enum CsTypeTag { TypeDefinitionIndex(TypeDefinitionIndex), GenericInstantiation(GenericInstantiation), } -impl From for CppTypeTag { +impl From for CsTypeTag { fn from(value: TypeDefinitionIndex) -> Self { - CppTypeTag::TypeDefinitionIndex(value) + CsTypeTag::TypeDefinitionIndex(value) } } -impl From for CppTypeTag { +impl From for CsTypeTag { fn from(value: TypeData) -> Self { match value { TypeData::TypeDefinitionIndex(i) => i.into(), @@ -38,27 +38,27 @@ impl From for CppTypeTag { } } -impl From for TypeData { - fn from(value: CppTypeTag) -> Self { +impl From for TypeData { + fn from(value: CsTypeTag) -> Self { match value { - CppTypeTag::TypeDefinitionIndex(i) => TypeData::TypeDefinitionIndex(i), - CppTypeTag::GenericInstantiation(gen) => TypeData::GenericClassIndex(gen.inst), // TODO:? + CsTypeTag::TypeDefinitionIndex(i) => TypeData::TypeDefinitionIndex(i), + CsTypeTag::GenericInstantiation(gen) => TypeData::GenericClassIndex(gen.inst), // TODO:? _ => panic!("Can't go from {value:?} to TypeData"), } } } -impl From for TypeDefinitionIndex { - fn from(value: CppTypeTag) -> Self { +impl From for TypeDefinitionIndex { + fn from(value: CsTypeTag) -> Self { match value { - CppTypeTag::TypeDefinitionIndex(i) => i, - CppTypeTag::GenericInstantiation(generic_inst) => generic_inst.tdi, + CsTypeTag::TypeDefinitionIndex(i) => i, + CsTypeTag::GenericInstantiation(generic_inst) => generic_inst.tdi, _ => panic!("Type is not a TDI! {value:?}"), } } } -impl CppTypeTag { +impl CsTypeTag { pub fn from_generic_class_index( generic_class_idx: usize, metadata: &brocolib::Metadata, @@ -86,17 +86,18 @@ impl CppTypeTag { pub fn from_type_data(type_data: TypeData, metadata: &brocolib::Metadata) -> Self { match type_data { TypeData::TypeDefinitionIndex(tdi) => tdi.into(), + TypeData::TypeIndex(_) => panic!("Not supported on array! {type_data:#?}"), TypeData::GenericClassIndex(generic_class_idx) => { Self::from_generic_class_index(generic_class_idx, metadata) } - _ => todo!(), + _ => todo!("{type_data:#?}"), } } pub fn get_tdi(&self) -> TypeDefinitionIndex { match self { - CppTypeTag::TypeDefinitionIndex(tdi) => *tdi, - CppTypeTag::GenericInstantiation(gen_inst) => gen_inst.tdi, + CsTypeTag::TypeDefinitionIndex(tdi) => *tdi, + CsTypeTag::GenericInstantiation(gen_inst) => gen_inst.tdi, } } } diff --git a/src/generate/metadata.rs b/src/generate/metadata.rs index e8b1f5be1..7d3b90ad6 100644 --- a/src/generate/metadata.rs +++ b/src/generate/metadata.rs @@ -1,14 +1,9 @@ use std::collections::{HashMap, HashSet}; -use brocolib::{ - global_metadata::{Il2CppTypeDefinition, MethodIndex, TypeDefinitionIndex}, - runtime_metadata::Il2CppType, -}; +use brocolib::global_metadata::{Il2CppTypeDefinition, MethodIndex, TypeDefinitionIndex}; use itertools::Itertools; -use crate::data::name_components::NameComponents; - -use super::{context_collection::CppContextCollection, cpp_type::CppType}; +use super::cs_type::CsType; pub struct MethodCalculations { pub estimated_size: usize, @@ -34,39 +29,15 @@ impl<'a> TypeDefinitionPair<'a> { } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub enum TypeUsage { - // Method usage - Parameter, - ReturnType, - - // References - FieldName, - PropertyName, +pub type TypeHandlerFn = Box; - // naming the CppType itself - TypeName, - GenericArg, -} - -pub type TypeHandlerFn = Box; -pub type TypeResolveHandlerFn = Box< - dyn Fn( - NameComponents, - &CppType, - &CppContextCollection, - &Metadata, - &Il2CppType, - TypeUsage, - ) -> NameComponents, ->; pub type Il2cppNamespace<'a> = &'a str; pub type Il2cppName<'a> = &'a str; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Il2cppFullName<'a>(pub Il2cppNamespace<'a>, pub Il2cppName<'a>); -pub struct Metadata<'a> { +pub struct CordlMetadata<'a> { pub metadata: &'a brocolib::Metadata<'a, 'a>, pub metadata_registration: &'a brocolib::runtime_metadata::Il2CppMetadataRegistration, pub code_registration: &'a brocolib::runtime_metadata::Il2CppCodeRegistration<'a>, @@ -76,9 +47,8 @@ pub struct Metadata<'a> { pub parent_to_child_map: HashMap>>, pub child_to_parent_map: HashMap>, - // - pub custom_type_handler: HashMap, - pub custom_type_resolve_handler: Vec, + pub unity_object_tdi: TypeDefinitionIndex, + pub name_to_tdi: HashMap, TypeDefinitionIndex>, pub blacklisted_types: HashSet, @@ -89,7 +59,7 @@ pub struct Metadata<'a> { pub packing_is_default_offset: u8, } -impl<'a> Metadata<'a> { +impl<'a> CordlMetadata<'a> { /// Returns the size of the base object. /// To be used for boxing/unboxing and various offset computations. pub fn object_size(&self) -> u8 { diff --git a/src/generate/mod.rs b/src/generate/mod.rs index c275ab892..00c938f50 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -1,14 +1,11 @@ -pub mod config; pub mod context; -pub mod context_collection; -pub mod cpp_type; -pub mod cpp_type_tag; pub mod cs_context_collection; -pub mod cs_fields; +pub mod cs_members; pub mod cs_type; -pub mod members; -pub mod members_serialize; +pub mod cs_type_tag; pub mod metadata; pub mod offsets; pub mod type_extensions; pub mod writer; + +pub mod cpp; diff --git a/src/generate/offsets/offsets_31.rs b/src/generate/offsets/offsets_31.rs index 944c05e0e..0125c5724 100644 --- a/src/generate/offsets/offsets_31.rs +++ b/src/generate/offsets/offsets_31.rs @@ -1,6 +1,6 @@ use core::mem; -use crate::generate::metadata::Metadata; +use crate::generate::metadata::CordlMetadata; use crate::generate::metadata::PointerSize; use crate::generate::type_extensions::TypeDefinitionExtensions; use crate::TypeDefinitionIndex; @@ -32,8 +32,8 @@ pub struct SizeInfo { pub fn get_size_info<'a>( t: &'a Il2CppTypeDefinition, tdi: TypeDefinitionIndex, - generic_inst_types: Option<&Vec>, - metadata: &'a Metadata, + generic_inst_types: Option<&[usize]>, + metadata: &'a CordlMetadata, ) -> SizeInfo { let size_metadata = get_size_of_type_table(metadata, tdi).unwrap(); let mut instance_size = size_metadata.instance_size; @@ -76,8 +76,8 @@ pub fn get_size_info<'a>( pub fn get_size_and_packing<'a>( t: &'a Il2CppTypeDefinition, tdi: TypeDefinitionIndex, - generic_inst_types: Option<&Vec>, - metadata: &'a Metadata, + generic_inst_types: Option<&[usize]>, + metadata: &'a CordlMetadata, ) -> (u32, Option) { let size_metadata = get_size_of_type_table(metadata, tdi).unwrap(); let mut metadata_size = size_metadata.instance_size; @@ -99,9 +99,9 @@ pub fn get_size_and_packing<'a>( } pub fn get_il2cpptype_sa( - metadata: &Metadata<'_>, + metadata: &CordlMetadata<'_>, ty: &Il2CppType, - generic_inst_types: Option<&Vec>, + generic_inst_types: Option<&[usize]>, ) -> SizeAndAlignment { get_type_size_and_alignment(ty, generic_inst_types, metadata) } @@ -109,8 +109,8 @@ pub fn get_il2cpptype_sa( pub fn get_sizeof_type<'a>( t: &'a Il2CppTypeDefinition, tdi: TypeDefinitionIndex, - generic_inst_types: Option<&Vec>, - metadata: &'a Metadata, + generic_inst_types: Option<&[usize]>, + metadata: &'a CordlMetadata, ) -> u32 { let size_metadata = get_size_of_type_table(metadata, tdi).unwrap(); let mut metadata_size = size_metadata.instance_size; @@ -182,7 +182,7 @@ fn packing_is_default(bitfield: u32, packing_is_default_offset: u8) -> bool { } /// RuntimeType::GetPacking -fn get_packing(metadata: &Metadata<'_>, ty_def: &Il2CppTypeDefinition) -> Option { +fn get_packing(metadata: &CordlMetadata<'_>, ty_def: &Il2CppTypeDefinition) -> Option { // according to this, packing is by default n = 8 // https://learn.microsoft.com/en-us/cpp/preprocessor/pack?view=msvc-170 if packing_is_default(ty_def.bitfield, metadata.packing_is_default_offset) { @@ -196,7 +196,7 @@ fn get_packing(metadata: &Metadata<'_>, ty_def: &Il2CppTypeDefinition) -> Option } /// GlobalMetadata::FromTypeDefinition -fn get_type_def_packing(metadata: &Metadata, ty_def: &Il2CppTypeDefinition) -> Option { +fn get_type_def_packing(metadata: &CordlMetadata, ty_def: &Il2CppTypeDefinition) -> Option { let packing = packing_value(ty_def.bitfield, metadata.packing_field_offset); // packing 8 is default @@ -214,7 +214,7 @@ fn size_is_default(bitfield: u32, size_is_default_offset: u8) -> bool { } fn get_size( - metadata: &Metadata<'_>, + metadata: &CordlMetadata<'_>, tdi: TypeDefinitionIndex, ty_def: &Il2CppTypeDefinition, ) -> Option { @@ -246,10 +246,10 @@ fn is_reference(ty: &Il2CppType) -> bool { /// Inspired by libil2cpp Class::LayoutFieldsLocked pub fn layout_fields( - metadata: &Metadata<'_>, + metadata: &CordlMetadata<'_>, declaring_ty_def: &Il2CppTypeDefinition, declaring_tdi: TypeDefinitionIndex, - generic_inst_types: Option<&Vec>, + generic_inst_types: Option<&[usize]>, offsets: Option<&mut Vec>, strictly_calculated: bool, ) -> SizeAndAlignment { @@ -373,10 +373,10 @@ pub fn layout_fields( /// equivalent to libil2cpp FieldLayout::LayoutFields with the instance field filter fn layout_instance_fields( - metadata: &Metadata<'_>, + metadata: &CordlMetadata<'_>, declaring_ty_def: &Il2CppTypeDefinition, declaring_tdi: TypeDefinitionIndex, - generic_inst_types: Option<&Vec>, + generic_inst_types: Option<&[usize]>, offsets: Option<&mut Vec>, parent_sa: SizeAndAlignment, ) -> SizeAndAlignment { @@ -449,7 +449,7 @@ fn layout_instance_fields( } fn get_offset_of_type_table( - metadata: &Metadata<'_>, + metadata: &CordlMetadata<'_>, tdi: TypeDefinitionIndex, field: usize, ) -> Option { @@ -467,9 +467,9 @@ fn get_offset_of_type_table( } fn get_parent_sa( - metadata: &Metadata<'_>, + metadata: &CordlMetadata<'_>, parent_index: u32, - generic_inst_types: Option<&Vec>, + generic_inst_types: Option<&[usize]>, ) -> SizeAndAlignment { let parent_ty = &metadata.metadata_registration.types[parent_index as usize]; let (parent_tdi, parent_generics) = match parent_ty.data { @@ -526,7 +526,7 @@ fn get_parent_sa( metadata, &metadata.metadata.global_metadata.type_definitions[parent_tdi], parent_tdi, - parent_generics.as_ref(), + parent_generics.as_deref(), None, false, ) @@ -536,7 +536,7 @@ fn update_instance_size_for_generic_class( ty_def: &Il2CppTypeDefinition, tdi: TypeDefinitionIndex, instance_size: usize, - metadata: &Metadata<'_>, + metadata: &CordlMetadata<'_>, ) -> usize { // need to set this in case there are no fields in a generic instance type if !ty_def.generic_container_index.is_valid() { @@ -559,7 +559,7 @@ fn update_instance_size_for_generic_class( } pub fn get_size_of_type_table<'a>( - metadata: &'a Metadata<'a>, + metadata: &'a CordlMetadata<'a>, tdi: TypeDefinitionIndex, ) -> Option<&'a Il2CppTypeDefinitionSizes> { if let Some(size_table) = &metadata @@ -603,8 +603,8 @@ fn get_alignment_of_type(ty: OffsetType, pointer_size: PointerSize) -> u8 { fn get_type_size_and_alignment( ty: &Il2CppType, - generic_inst_types: Option<&Vec>, - metadata: &Metadata, + generic_inst_types: Option<&[usize]>, + metadata: &CordlMetadata, ) -> SizeAndAlignment { let mut sa = SizeAndAlignment { alignment: 0, diff --git a/src/generate/type_extensions.rs b/src/generate/type_extensions.rs index 749299554..7f9c8e32e 100644 --- a/src/generate/type_extensions.rs +++ b/src/generate/type_extensions.rs @@ -1,7 +1,7 @@ use core::panic; use brocolib::{ - global_metadata::{Il2CppMethodDefinition, Il2CppTypeDefinition}, + global_metadata::{Il2CppMethodDefinition, Il2CppTypeDefinition, TypeDefinitionIndex}, runtime_metadata::{Il2CppType, Il2CppTypeEnum, TypeData}, Metadata, }; @@ -9,8 +9,6 @@ use itertools::Itertools; use crate::data::name_components::NameComponents; -use super::config::GenerationConfig; - pub const PARAM_ATTRIBUTE_IN: u16 = 0x0001; pub const PARAM_ATTRIBUTE_OUT: u16 = 0x0002; pub const PARAM_ATTRIBUTE_OPTIONAL: u16 = 0x0010; @@ -178,18 +176,10 @@ pub trait TypeDefinitionExtensions { fn get_name_components(&self, metadata: &Metadata) -> NameComponents; - fn full_name_cpp( - &self, - metadata: &Metadata, - config: &GenerationConfig, - with_generics: bool, - ) -> String; - fn full_name_nested( - &self, - metadata: &Metadata, - config: &GenerationConfig, - with_generics: bool, - ) -> String; + // fn full_name_cpp(&self, metadata: &Metadata, with_generics: bool) -> String; + // fn full_name_nested(&self, metadata: &Metadata, with_generics: bool) -> String; + + fn is_reference_type(&self, metadata: &Metadata) -> bool; } impl TypeDefinitionExtensions for Il2CppTypeDefinition { @@ -304,10 +294,9 @@ impl TypeDefinitionExtensions for Il2CppTypeDefinition { false => None, }; - let ty = + let _ty = &metadata.runtime_metadata.metadata_registration.types[self.byval_type_index as usize]; - let is_pointer = - (!self.is_value_type() && !self.is_enum_type()) || ty.ty == Il2CppTypeEnum::Class; + let is_pointer = self.is_reference_type(metadata); match self.declaring_type_index != u32::MAX { true => { @@ -344,84 +333,81 @@ impl TypeDefinitionExtensions for Il2CppTypeDefinition { } // TODO: Use name components - fn full_name_cpp( - &self, - metadata: &Metadata, - config: &GenerationConfig, - with_generics: bool, - ) -> String { - let namespace = config.namespace_cpp(self.namespace(metadata)); - let name = config.name_cpp(self.name(metadata)); - - let mut full_name = String::new(); - - if self.declaring_type_index != u32::MAX { - let declaring_ty = metadata.runtime_metadata.metadata_registration.types - [self.declaring_type_index as usize]; - - let s = match declaring_ty.data { - brocolib::runtime_metadata::TypeData::TypeDefinitionIndex(tdi) => { - let declaring_td = &metadata.global_metadata.type_definitions[tdi]; - declaring_td.full_name_cpp(metadata, config, with_generics) - } - _ => declaring_ty.full_name(metadata), - }; - - full_name.push_str(&s); - full_name.push_str("::"); - } else { - // only write namespace if no declaring type - full_name.push_str(&namespace); - full_name.push_str("::"); - } - - full_name.push_str(&name); - if self.generic_container_index.is_valid() && with_generics { - let gc = self.generic_container(metadata); - full_name.push_str(&gc.to_string(metadata)); - } - full_name - } + // fn full_name_cpp(&self, metadata: &Metadata, with_generics: bool) -> String { + // let namespace = config.namespace_cpp(self.namespace(metadata)); + // let name = config.name_cpp(self.name(metadata)); + + // let mut full_name = String::new(); + + // if self.declaring_type_index != u32::MAX { + // let declaring_ty = metadata.runtime_metadata.metadata_registration.types + // [self.declaring_type_index as usize]; + + // let s = match declaring_ty.data { + // brocolib::runtime_metadata::TypeData::TypeDefinitionIndex(tdi) => { + // let declaring_td = &metadata.global_metadata.type_definitions[tdi]; + // declaring_td.full_name_cpp(metadata, config, with_generics) + // } + // _ => declaring_ty.full_name(metadata), + // }; + + // full_name.push_str(&s); + // full_name.push_str("::"); + // } else { + // // only write namespace if no declaring type + // full_name.push_str(&namespace); + // full_name.push_str("::"); + // } + + // full_name.push_str(&name); + // if self.generic_container_index.is_valid() && with_generics { + // let gc = self.generic_container(metadata); + // full_name.push_str(&gc.to_string(metadata)); + // } + // full_name + // } // separates nested types with / // TODO: Use name components - fn full_name_nested( - &self, - metadata: &Metadata, - config: &GenerationConfig, - with_generics: bool, - ) -> String { - let namespace = config.namespace_cpp(self.namespace(metadata)); - let name = config.name_cpp(self.name(metadata)); - - let mut full_name = String::new(); - - if self.declaring_type_index != u32::MAX { - let declaring_ty = metadata.runtime_metadata.metadata_registration.types - [self.declaring_type_index as usize]; - - let declaring_ty = match declaring_ty.data { - brocolib::runtime_metadata::TypeData::TypeDefinitionIndex(tdi) => { - let declaring_td = &metadata.global_metadata.type_definitions[tdi]; - declaring_td.full_name_nested(metadata, config, with_generics) - } - _ => declaring_ty.full_name(metadata), - }; - - full_name.push_str(&declaring_ty); - full_name.push('/'); - } else { - // only write namespace if no declaring type - full_name.push_str(&namespace); - full_name.push('.'); - } + // fn full_name_nested(&self, metadata: &Metadata, with_generics: bool) -> String { + // let namespace = config.namespace_cpp(self.namespace(metadata)); + // let name = config.name_cpp(self.name(metadata)); + + // let mut full_name = String::new(); + + // if self.declaring_type_index != u32::MAX { + // let declaring_ty = metadata.runtime_metadata.metadata_registration.types + // [self.declaring_type_index as usize]; + + // let declaring_ty = match declaring_ty.data { + // brocolib::runtime_metadata::TypeData::TypeDefinitionIndex(tdi) => { + // let declaring_td = &metadata.global_metadata.type_definitions[tdi]; + // declaring_td.full_name_nested(metadata, config, with_generics) + // } + // _ => declaring_ty.full_name(metadata), + // }; + + // full_name.push_str(&declaring_ty); + // full_name.push('/'); + // } else { + // // only write namespace if no declaring type + // full_name.push_str(&namespace); + // full_name.push('.'); + // } + + // full_name.push_str(&name); + // if self.generic_container_index.is_valid() && with_generics { + // let gc = self.generic_container(metadata); + // full_name.push_str(&gc.to_string(metadata)); + // } + // full_name + // } + + fn is_reference_type(&self, metadata: &Metadata) -> bool { + let ty = + &metadata.runtime_metadata.metadata_registration.types[self.byval_type_index as usize]; - full_name.push_str(&name); - if self.generic_container_index.is_valid() && with_generics { - let gc = self.generic_container(metadata); - full_name.push_str(&gc.to_string(metadata)); - } - full_name + (!self.is_value_type() && !self.is_enum_type()) || ty.ty == Il2CppTypeEnum::Class } } @@ -455,3 +441,13 @@ impl Il2CppTypeEnumExtensions for Il2CppTypeEnum { ) } } + +pub trait TypeDefinitionIndexExtensions { + fn get_type_definition<'a>(&self, metadata: &'a Metadata) -> &'a Il2CppTypeDefinition; +} + +impl TypeDefinitionIndexExtensions for TypeDefinitionIndex { + fn get_type_definition<'a>(&self, metadata: &'a Metadata) -> &'a Il2CppTypeDefinition { + &metadata.global_metadata.type_definitions[*self] + } +} diff --git a/src/generate/writer.rs b/src/generate/writer.rs index aa1c2f77b..38ef20830 100644 --- a/src/generate/writer.rs +++ b/src/generate/writer.rs @@ -37,7 +37,7 @@ impl Write for CppWriter { } } -pub trait Writable: std::fmt::Debug { +pub trait CppWritable: std::fmt::Debug { fn write(&self, writer: &mut CppWriter) -> color_eyre::Result<()>; } diff --git a/src/json/json_gen.rs b/src/json/json_gen.rs index 8f27b3f09..1be84aa0c 100644 --- a/src/json/json_gen.rs +++ b/src/json/json_gen.rs @@ -1,7 +1,7 @@ use std::{ fs::{self, File}, io::BufWriter, - path::PathBuf, + path::{Path, PathBuf}, }; use brocolib::global_metadata::{ @@ -10,12 +10,11 @@ use brocolib::global_metadata::{ }; use color_eyre::eyre::Result; use itertools::Itertools; -use rayon::vec; + use serde::{Deserialize, Serialize}; use crate::generate::{ - config::GenerationConfig, - metadata::Metadata, + metadata::CordlMetadata, offsets, type_extensions::{ParameterDefinitionExtensions, TypeDefinitionExtensions, TypeExtentions}, }; @@ -75,9 +74,9 @@ struct JsonParam { fn make_field( field: &Il2CppFieldDefinition, field_index: usize, - td: &Il2CppTypeDefinition, + _td: &Il2CppTypeDefinition, tdi: TypeDefinitionIndex, - metadata: &Metadata, + metadata: &CordlMetadata, ) -> JsonField { let ty = metadata .metadata @@ -103,8 +102,8 @@ fn make_field( fn make_property( property: &Il2CppPropertyDefinition, td: &Il2CppTypeDefinition, - tdi: TypeDefinitionIndex, - metadata: &Metadata, + _tdi: TypeDefinitionIndex, + metadata: &CordlMetadata, ) -> JsonProperty { let p_setter = (property.set != u32::MAX).then(|| property.set_method(td, metadata.metadata)); let p_getter = (property.get != u32::MAX).then(|| property.get_method(td, metadata.metadata)); @@ -131,9 +130,9 @@ fn make_property( } fn make_param( param: &Il2CppParameterDefinition, - td: &Il2CppTypeDefinition, - tdi: TypeDefinitionIndex, - metadata: &Metadata, + _td: &Il2CppTypeDefinition, + _tdi: TypeDefinitionIndex, + metadata: &CordlMetadata, ) -> JsonParam { let param_type = metadata .metadata @@ -161,7 +160,7 @@ fn make_method( method: &Il2CppMethodDefinition, td: &Il2CppTypeDefinition, tdi: TypeDefinitionIndex, - metadata: &Metadata, + metadata: &CordlMetadata, ) -> JsonMethod { let ret_ty = metadata .metadata @@ -182,7 +181,11 @@ fn make_method( } } -fn make_type(td: &Il2CppTypeDefinition, tdi: TypeDefinitionIndex, metadata: &Metadata) -> JsonType { +fn make_type( + td: &Il2CppTypeDefinition, + tdi: TypeDefinitionIndex, + metadata: &CordlMetadata, +) -> JsonType { let fields = td .fields(metadata.metadata) .iter() @@ -236,7 +239,7 @@ fn make_type(td: &Il2CppTypeDefinition, tdi: TypeDefinitionIndex, metadata: &Met /// Essentially check if the type is compiler generated or /// not useful to emit /// -pub fn is_real_declaring_type(td: &Il2CppTypeDefinition, metadata: &Metadata) -> bool { +pub fn is_real_declaring_type(td: &Il2CppTypeDefinition, metadata: &CordlMetadata) -> bool { let condition1 = !td.name(metadata.metadata).contains("<>c__") && !td.name(metadata.metadata).contains(">d__"); let condition2 = !td @@ -260,7 +263,7 @@ pub fn is_real_declaring_type(td: &Il2CppTypeDefinition, metadata: &Metadata) -> && condition4 } -pub fn make_json(metadata: &Metadata, _config: &GenerationConfig, file: PathBuf) -> Result<()> { +pub fn make_json(metadata: &CordlMetadata, file: &Path) -> Result<()> { // we could use a map here but sorting // wouldn't be guaranteed // we want sorting so diffs are more readable @@ -286,11 +289,7 @@ pub fn make_json(metadata: &Metadata, _config: &GenerationConfig, file: PathBuf) Ok(()) } -pub fn make_json_folder( - metadata: &Metadata, - config: &GenerationConfig, - folder: PathBuf, -) -> Result<()> { +pub fn make_json_folder(metadata: &CordlMetadata, folder: &Path) -> Result<()> { // we could use a map here but sorting // wouldn't be guaranteed // we want sorting so diffs are more readable @@ -307,17 +306,14 @@ pub fn make_json_folder( .map(|(tdi, td)| make_type(td, tdi, metadata)) .sorted_by(|a, b| a.full_name.cmp(&b.full_name)) .try_for_each(|t| -> Result<()> { - let mut namespace_cpp = config.sanitize_to_cpp_name(&t.namespace); - let name_cpp = config.name_cpp(&t.name); + let mut namespace = t.namespace.clone(); + let name = t.name.clone(); - if namespace_cpp.is_empty() { - namespace_cpp = "GlobalNamespace".to_string(); + if namespace.is_empty() { + namespace = "GlobalNamespace".to_string(); } - let file: PathBuf = folder - .join(namespace_cpp) - .join(name_cpp) - .with_extension("json"); + let file: PathBuf = folder.join(namespace).join(name).with_extension("json"); fs::create_dir_all(file.parent().unwrap())?; diff --git a/src/main.rs b/src/main.rs index 45c21f4ac..c97d8b638 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,36 +5,42 @@ #![feature(map_try_insert)] #![feature(lazy_cell)] #![feature(exit_status_error)] +#![feature(iterator_try_collect)] use brocolib::{global_metadata::TypeDefinitionIndex, runtime_metadata::TypeData}; -use color_eyre::{eyre::Context, Result, Section}; -use generate::{config::GenerationConfig, metadata::Metadata}; +use byteorder::LittleEndian; +use color_eyre::eyre::Context; +use generate::{cpp, metadata::CordlMetadata}; use itertools::Itertools; extern crate pretty_env_logger; -use filesize::PathExt; + use include_dir::{include_dir, Dir}; use json::json_gen::{make_json, make_json_folder}; -use log::{error, info, trace, warn}; +use log::{info, trace, warn}; use rayon::prelude::*; -use walkdir::DirEntry; -use std::{fs, path::PathBuf, process::Command, sync::LazyLock, time}; +use std::{ + fs, + path::{Path, PathBuf}, + time, +}; use clap::{Parser, Subcommand}; -use crate::{ - generate::{ - context_collection::CppContextCollection, cpp_type_tag::CppTypeTag, - cs_context_collection::CsContextCollection, members::CppMember, - }, - handlers::{comment_omit::remove_coments, object, unity, value_type}, -}; +use crate::generate::{cs_context_collection::TypeContextCollection, cs_type_tag::CsTypeTag}; mod data; mod generate; -mod handlers; +// mod handlers; mod helpers; mod json; +#[derive(Clone, Copy, Debug, clap::ValueEnum)] +enum TargetLang { + Cpp, + SingleJSON, + MultiJSON, +} + #[derive(Parser)] #[clap(author, version, about, long_about = None)] struct Cli { @@ -46,21 +52,16 @@ struct Cli { #[clap(short, long, value_parser, value_name = "FILE")] libil2cpp: PathBuf, - /// The path to generated json file - #[clap(short, long, value_parser, value_name = "FILE")] - json: Option, - - /// The path to the folder for the generated json files - #[clap(long, value_parser, value_name = "FILE")] - multi_json: Option, - - /// Whether to format with clang-format + /// Whether to format #[clap(short, long)] format: bool, - /// Whether to format with clang-format + #[clap(short, long)] remove_verbose_comments: bool, + #[clap(value_parser)] + target: TargetLang, + /// Whether to generate generic method specializations #[clap(short, long)] gen_generic_methods_specializations: bool, @@ -72,18 +73,10 @@ struct Cli { #[derive(Subcommand)] enum Commands {} -pub static STATIC_CONFIG: LazyLock = LazyLock::new(|| GenerationConfig { - header_path: PathBuf::from("./codegen/include"), - source_path: PathBuf::from("./codegen/src"), - dst_internals_path: PathBuf::from("./codegen/include/cordl_internals"), - dst_header_internals_file: PathBuf::from( - "./codegen/include/cordl_internals/cordl_internals.hpp", - ), - use_anonymous_namespace: false, -}); - static INTERNALS_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/cordl_internals"); +pub type Endian = LittleEndian; + fn main() -> color_eyre::Result<()> { color_eyre::install()?; let cli: Cli = Cli::parse(); @@ -95,35 +88,36 @@ fn main() -> color_eyre::Result<()> { info!("Add --format/-f to format with clang-format at end") } - if STATIC_CONFIG.header_path.exists() { - std::fs::remove_dir_all(&STATIC_CONFIG.header_path)?; - } - std::fs::create_dir_all(&STATIC_CONFIG.header_path)?; - - info!( - "Copying config to codegen folder {:?}", - STATIC_CONFIG.dst_internals_path + println!( + "Running on {}", + Path::new("./").canonicalize().unwrap().display() ); - - std::fs::create_dir_all(&STATIC_CONFIG.dst_internals_path)?; - - // extract contents of the cordl internals folder into destination - INTERNALS_DIR.extract(&STATIC_CONFIG.dst_internals_path)?; - - let global_metadata_data = fs::read(cli.metadata).context("il2cpp metadata")?; - let elf_data = fs::read(cli.libil2cpp).context("libil2cpp.so shared object")?; + let global_metadata_data = fs::read(&cli.metadata) + .with_context(|| format!("il2cpp metadata not found {}", cli.metadata.display()))?; + let elf_data = fs::read(&cli.libil2cpp).with_context(|| { + format!( + "libil2cpp.so shared object not found {}", + cli.metadata.display() + ) + })?; let il2cpp_metadata = brocolib::Metadata::parse(&global_metadata_data, &elf_data)?; - let mut metadata = Metadata { + let unity_object_tdi_idx = il2cpp_metadata + .global_metadata + .type_definitions + .as_vec() + .iter() + .position(|v| v.full_name(&il2cpp_metadata, false) == "UnityEngine.Object") + .unwrap(); + + let mut metadata = CordlMetadata { metadata: &il2cpp_metadata, code_registration: &il2cpp_metadata.runtime_metadata.code_registration, metadata_registration: &il2cpp_metadata.runtime_metadata.metadata_registration, method_calculations: Default::default(), parent_to_child_map: Default::default(), child_to_parent_map: Default::default(), - // TODO: These should come from args to the program? - custom_type_handler: Default::default(), - custom_type_resolve_handler: Default::default(), + unity_object_tdi: TypeDefinitionIndex::new(unity_object_tdi_idx as u32), name_to_tdi: Default::default(), blacklisted_types: Default::default(), pointer_size: generate::metadata::PointerSize::Bytes8, @@ -138,18 +132,7 @@ fn main() -> color_eyre::Result<()> { metadata.parse(); info!("Finished in {}ms", t.elapsed().as_millis()); - if let Some(json) = cli.json { - println!("Writing json file {json:?}"); - make_json(&metadata, &STATIC_CONFIG, json)?; - return Ok(()); - } - if let Some(json_folder) = cli.multi_json { - println!("Writing json file {json_folder:?}"); - make_json_folder(&metadata, &STATIC_CONFIG, json_folder)?; - return Ok(()); - } - - let mut cpp_context_collection = CppContextCollection::new(); + let mut cs_context_collection = TypeContextCollection::new(); // blacklist types { @@ -253,6 +236,7 @@ fn main() -> color_eyre::Result<()> { let ty_def = &metadata.metadata.global_metadata.type_definitions[tdi]; let _ty = &metadata.metadata_registration.types[ty_def.byval_type_index as usize]; + // only make the roots if ty_def.declaring_type_index != u32::MAX { continue; } @@ -261,17 +245,11 @@ fn main() -> color_eyre::Result<()> { "Making types {:.4}% ({tdi_u64}/{total})", (tdi_u64 as f64 / total as f64 * 100.0) ); - cpp_context_collection.make_from( - &metadata, - &STATIC_CONFIG, - TypeData::TypeDefinitionIndex(tdi), - None, - ); - cpp_context_collection.alias_nested_types_il2cpp( + cs_context_collection.make_from(&metadata, TypeData::TypeDefinitionIndex(tdi), None); + cs_context_collection.alias_nested_types_il2cpp( tdi, - CppTypeTag::TypeDefinitionIndex(tdi), + CsTypeTag::TypeDefinitionIndex(tdi), &metadata, - false, ); } } @@ -293,7 +271,7 @@ fn main() -> color_eyre::Result<()> { "Making nested types {:.4}% ({tdi_u64}/{total})", (tdi_u64 as f64 / total as f64 * 100.0) ); - cpp_context_collection.make_nested_from(&metadata, &STATIC_CONFIG, tdi, None); + cs_context_collection.make_nested_from(&metadata, tdi); } } @@ -341,7 +319,7 @@ fn main() -> color_eyre::Result<()> { // cpp_context_collection.fill_generic_class_inst( // method_spec, // &mut metadata, - // &STATIC_CONFIG, + // // ); // } // } @@ -365,19 +343,17 @@ fn main() -> color_eyre::Result<()> { .get(generic_class.generic_method_index as usize) .unwrap(); - cpp_context_collection.fill_generic_method_inst( - method_spec, - &mut metadata, - &STATIC_CONFIG, - ); + cs_context_collection.fill_generic_method_inst(method_spec, &mut metadata); } } info!("Registering handlers!"); // il2cpp_internals::register_il2cpp_types(&mut metadata)?; - unity::register_unity(&mut metadata)?; - object::register_system(&mut metadata)?; - value_type::register_value_type(&mut metadata)?; + + // TODO: uncomment + // unity::register_unity(&mut metadata)?; + // object::register_system(&mut metadata)?; + // value_type::register_value_type(&mut metadata)?; info!("Handlers registered!"); { @@ -393,399 +369,29 @@ fn main() -> color_eyre::Result<()> { (tdi_u64 as f64 / total as f64 * 100.0) ); - cpp_context_collection.fill( - &metadata, - &STATIC_CONFIG, - CppTypeTag::TypeDefinitionIndex(tdi), - ); + cs_context_collection.fill(CsTypeTag::TypeDefinitionIndex(tdi), &metadata); } } if cli.remove_verbose_comments { - remove_coments(&mut cpp_context_collection)?; + // TODO: uncomment + // remove_coments(&mut cpp_context_collection)?; } - const write_all: bool = true; - if write_all { - cpp_context_collection.write_all(&STATIC_CONFIG)?; - cpp_context_collection.write_namespace_headers()?; - } else { - // for t in &metadata.type_definitions { - // // Handle the generation for a single type - // let dest = open_writer(&metadata, &config, &t); - // write_type(&metadata, &config, &t, &dest); - // } - fn make_td_tdi(idx: u32) -> TypeData { - TypeData::TypeDefinitionIndex(TypeDefinitionIndex::new(idx)) + match cli.target { + TargetLang::Cpp => cpp::cpp_main::run_cpp(cs_context_collection, &metadata), + TargetLang::SingleJSON => { + let json = Path::new("./json"); + println!("Writing json file {json:?}"); + make_json(&metadata, json) } - // All indices require updating - // cpp_context_collection.get()[&make_td_tdi(123)].write()?; - // cpp_context_collection.get()[&make_td_tdi(342)].write()?; - // cpp_context_collection.get()[&make_td_tdi(512)].write()?; - // cpp_context_collection.get()[&make_td_tdi(1024)].write()?; - // cpp_context_collection.get()[&make_td_tdi(600)].write()?; - // cpp_context_collection.get()[&make_td_tdi(1000)].write()?; - // cpp_context_collection.get()[&make_td_tdi(420)].write()?; - // cpp_context_collection.get()[&make_td_tdi(69)].write()?; - // cpp_context_collection.get()[&make_td_tdi(531)].write()?; - // cpp_context_collection.get()[&make_td_tdi(532)].write()?; - // cpp_context_collection.get()[&make_td_tdi(533)].write()?; - // cpp_context_collection.get()[&make_td_tdi(534)].write()?; - // cpp_context_collection.get()[&make_td_tdi(535)].write()?; - // cpp_context_collection.get()[&make_td_tdi(1455)].write()?; - info!("Generic type"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| c.get_types().iter().any(|(_, t)| t.cpp_template.is_some())) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("List Generic type"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types().iter().any(|(_, t)| { - t.cpp_name_components.generics.is_some() && t.cpp_name() == "List_1" - }) - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("Value type"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types().iter().any(|(_, t)| { - t.is_value_type && t.name() == "Color" && t.namespace() == "UnityEngine" - }) - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - // info!("Nested type"); - // cpp_context_collection - // .get() - // .iter() - // .find(|(_, c)| { - // c.get_types().iter().any(|(_, t)| { - // t.nested_types - // .iter() - // .any(|(_, n)| !n.declarations.is_empty()) - // }) - // }) - // .unwrap() - // .1 - // .write()?; - // Doesn't exist anymore? - // info!("AlignmentUnion type"); - // cpp_context_collection - // .get() - // .iter() - // .find(|(_, c)| { - // c.get_types() - // .iter() - // .any(|(_, t)| t.is_value_type && &t.name()== "AlignmentUnion") - // }) - // .unwrap() - // .1 - // .write()?; - info!("Array type"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.name() == "Array" && t.namespace() == "System") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("Default param"); - cpp_context_collection - .get() - .iter() - .filter(|(_, c)| { - c.get_types().iter().any(|(_, t)| { - t.implementations.iter().any(|d| { - if let CppMember::MethodImpl(m) = d.as_ref() { - m.parameters.iter().any(|p| p.def_value.is_some()) - } else { - false - } - }) - }) - }) - .nth(2) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("Enum type"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| c.get_types().iter().any(|(_, t)| t.is_enum_type)) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("UnityEngine.Object"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.name() == "Object" && t.namespace() == "UnityEngine") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("BeatmapSaveDataHelpers"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.name() == "BeatmapSaveDataHelpers") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("HMUI.ViewController"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "HMUI" && t.name() == "ViewController") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("UnityEngine.Component"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "UnityEngine" && t.name() == "Component") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("UnityEngine.GameObject"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "UnityEngine" && t.name() == "GameObject") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("MainFlowCoordinator"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace().is_empty() && t.name() == "MainFlowCoordinator") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("OVRPlugin"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace().is_empty() && t.name() == "OVRPlugin") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("HMUI.IValueChanger"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "HMUI" && t.name() == "IValueChanger`1") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("System.ValueType"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "System" && t.name() == "ValueType") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("System.ValueTuple_2"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "System" && t.name() == "ValueTuple`2") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("System.Decimal"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "System" && t.name() == "Decimal") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("System.Enum"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "System" && t.name() == "Enum") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("System.Multicast"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "System" && t.name() == "MulticastDelegate") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("System.Delegate"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.namespace() == "System" && t.name() == "Delegate") - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - info!("BeatmapSaveDataVersion3.BeatmapSaveData.EventBoxGroup`1"); - cpp_context_collection - .get() - .iter() - .find(|(_, c)| { - c.get_types() - .iter() - .any(|(_, t)| t.name().contains("EventBoxGroup`1")) - }) - .unwrap() - .1 - .write(&STATIC_CONFIG)?; - // for (_, context) in cpp_context_collection.get() { - // context.write().unwrap(); - // } - } - - if cli.format { - format_files()?; - } - - Ok(()) -} - -fn format_files() -> Result<()> { - info!("Formatting!"); + TargetLang::MultiJSON => { + let json_folder = Path::new("./multi_json"); - use walkdir::WalkDir; - - let files: Vec = WalkDir::new(&STATIC_CONFIG.header_path) - .into_iter() - .filter(|f| f.as_ref().is_ok_and(|f| f.path().is_file())) - .try_collect()?; - - let file_count = files.len(); - - info!( - "{file_count} files across {} threads", - rayon::current_num_threads() - ); - // easily get file size for a given file - fn file_size(file: &DirEntry) -> usize { - match std::fs::metadata(file.path()) { - Ok(data) => file.path().size_on_disk_fast(&data).unwrap() as usize, - Err(_) => 0, + println!("Writing json file {json_folder:?}"); + make_json_folder(&metadata, json_folder) } - } - - // TODO: Debug - warn!("Do not run with debugger, for some reason an early abrupt exit."); - - files - .iter() - // sort on file size - .sorted_by(|a, b| file_size(a).cmp(&file_size(b))) - // reverse to go big -> small, so we can work on other files while big files are happening - .rev() - // parallelism - .enumerate() - .par_bridge() - .try_for_each(|(file_num, file)| -> Result<()> { - let path = file.path(); - info!( - "Formatting [{}/{file_count}] {}", - file_num + 1, - path.display() - ); - let mut command = Command::new("clang-format"); - command.arg("-i").arg(path); - - let spawn = command - .output() - .suggestion("You may be missing clang-format. Ensure it is on PATH")?; - - if !spawn.stderr.is_empty() { - error!( - "Error {} {}", - path.display(), - String::from_utf8(spawn.stderr)? - ); - } - - spawn.status.exit_ok()?; - - Ok(()) - })?; + }?; - info!("Done formatting!"); Ok(()) }