From 4801c2678aea78b8894364e5d877413229519357 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sun, 2 Mar 2025 14:07:39 -0500 Subject: [PATCH] Add new PropertyBase trait, implement more things needed for writing The biggest change is that structs and properties now provide their own name, which is needed when we want to write everything back to a file. Also a bunch of reorganization of stuff I didn't understand before. --- paramacro/src/lib.rs | 32 ++++++++- src/array_property.rs | 56 ++++++++++++++-- src/bool_property.rs | 10 +++ src/build_data.rs | 9 +-- src/common.rs | 4 ++ src/enum_property.rs | 46 ++++++------- src/float_property.rs | 10 +++ src/guid.rs | 14 ++++ src/int_property.rs | 10 +++ src/lib.rs | 4 +- src/linear_color.rs | 14 ++++ src/map_property.rs | 81 +++++++++++++++++++++-- src/name_property.rs | 10 +++ src/primary_asset_id.rs | 47 ++++++++------ src/primary_asset_type.rs | 32 ++++----- src/set_property.rs | 31 +++++---- src/str_property.rs | 10 +++ src/struct_property.rs | 94 +++++++++++++++++++-------- src/structs.rs | 133 ++++++++++++++++++++++++++++++++------ 19 files changed, 502 insertions(+), 145 deletions(-) diff --git a/paramacro/src/lib.rs b/paramacro/src/lib.rs index 6e93a50..e6a9e91 100644 --- a/paramacro/src/lib.rs +++ b/paramacro/src/lib.rs @@ -12,8 +12,15 @@ use syn::{parse_macro_input, Expr, Fields, Item, Lit}; pub fn serialized_struct(_metadata: TokenStream, input: TokenStream) -> TokenStream { let mut input = syn::parse_macro_input!(input as syn::ItemStruct); + let Lit::Str(struct_name) = syn::parse_macro_input!(_metadata as syn::Lit) else { + panic!("This must be given a name!") + }; - for mut field in &mut input.fields { + let mut field_types = vec![]; + let mut field_idents = vec![]; + let mut field_names = vec![]; + + for field in &mut input.fields { let our_custom = &field.attrs[0]; let our_custom_name = our_custom.meta.require_name_value().unwrap(); let our_custom_name = match &our_custom_name.value { @@ -28,9 +35,13 @@ pub fn serialized_struct(_metadata: TokenStream, input: TokenStream) _ => None }.unwrap(); field.attrs.clear(); + field_types.push(field.ty.clone()); + field_idents.push(field.ident.clone()); + field_names.push(our_custom_name.clone()); let field_tokens = field.to_token_stream(); let new_field_token_stream = quote! { #[br(parse_with = crate::structs::read_struct_field, args(#our_custom_name))] + #[bw(write_with = crate::structs::write_struct_field, args(#our_custom_name))] #field_tokens }; let buffer = ::syn::parse::Parser::parse2( @@ -43,7 +54,7 @@ pub fn serialized_struct(_metadata: TokenStream, input: TokenStream) // Add "None" field let none_field_stream = quote! { #[br(temp)] - #[bw(ignore)] + #[bw(calc = crate::structs::PrimaryAssetNameProperty { property_name: "None".to_string(), type_name: "".to_string(), key: None } )] none_field: crate::structs::PrimaryAssetNameProperty }; let buffer = ::syn::parse::Parser::parse2( @@ -57,9 +68,26 @@ pub fn serialized_struct(_metadata: TokenStream, input: TokenStream) _ => {} } + let id = &input.ident; + let output = quote! { #[binrw] #input + + #[automatically_derived] + impl crate::structs::PropertyBase for #id { + fn type_name() -> &'static str { + return "StructProperty"; + } + + fn struct_name() -> Option<&'static str> { + return Some(#struct_name); + } + + fn size_in_bytes(&self) -> u32 { + #( #field_types::size_in_bytes(&self.#field_idents) )+* + #( (crate::structs::calc_struct_field_prelude_byte_size(stringify!(#field_types), #field_names, #field_types::struct_name()) ) )+* + 9 // for "none" field + } + } }; output.into_token_stream().into() diff --git a/src/array_property.rs b/src/array_property.rs index a8f0d70..f767875 100644 --- a/src/array_property.rs +++ b/src/array_property.rs @@ -19,6 +19,20 @@ fn calculate_header_size(key_name: &str, array_key_data: &ArrayKeyData) -> u64 { } } +impl crate::structs::PropertyBase for ArrayProperty { + fn type_name() -> &'static str { + return "ArrayProperty"; + } + + fn size_in_bytes(&self) -> u32 { + // todo + 4 + 8 + + crate::common::size_of_string_with_length(&self.key_name) + + calc_key_data_size_in_bytes(&self.key_data) + + calc_size_in_bytes(&self) + } +} + #[binrw::parser(reader, endian)] fn custom_parser( size_in_bytes: u32, @@ -63,9 +77,22 @@ pub struct ArrayEntry { pub key: ArrayValue, } -fn calc_size_in_bytes(entries: &Vec) -> u32 { - // TODO: stub - 18 +fn calc_size_in_bytes(prop: &ArrayProperty) -> u32 { + // TODO: complete guesswork still + let mut size = 4; + + for entry in &prop.entries { + size += match &entry.key { + ArrayValue::Struct { r#struct } => { + crate::struct_property::calc_size_in_bytes(&r#struct) + } + ArrayValue::String(string_map_key) => { + crate::common::size_of_string_with_length(&string_map_key.value) + } + }; + } + + size } #[binrw] @@ -86,16 +113,33 @@ pub enum ArrayKeyData { #[br(parse_with = read_string_with_length)] #[bw(write_with = write_string_with_length)] - #[br(pad_before = 8)] // idk - #[br(pad_after = 17)] // super idk + #[brw(pad_before = 8)] // idk + #[brw(pad_after = 17)] // super idk struct_name: String, }, } +fn calc_key_data_size_in_bytes(key_data: &ArrayKeyData) -> u32 { + return match key_data { + ArrayKeyData::String() => 0, + ArrayKeyData::Struct { + name, + type_name, + struct_name, + } => { + crate::common::size_of_string_with_length(&name) + + crate::common::size_of_string_with_length(&type_name) + + crate::common::size_of_string_with_length(&struct_name) + + 8 + + 17 + } + }; +} + #[binrw] #[derive(Debug)] pub struct ArrayProperty { - #[bw(calc = calc_size_in_bytes(entries))] + #[bw(calc = calc_size_in_bytes(self))] pub size_in_bytes: u32, #[brw(pad_before = 4)] diff --git a/src/bool_property.rs b/src/bool_property.rs index c8c450d..7f62668 100644 --- a/src/bool_property.rs +++ b/src/bool_property.rs @@ -10,6 +10,16 @@ pub struct BoolProperty { pub value: bool, } +impl crate::structs::PropertyBase for BoolProperty { + fn type_name() -> &'static str { + return "IntProperty"; + } + + fn size_in_bytes(&self) -> u32 { + 10 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/build_data.rs b/src/build_data.rs index 6a57fe9..3338055 100644 --- a/src/build_data.rs +++ b/src/build_data.rs @@ -1,12 +1,13 @@ use crate::common::{read_string_with_length, write_string_with_length}; use crate::str_property::StrProperty; +use crate::structs::PropertyBase; use crate::structs::{ DAAssembleIdDataStruct, DACustomizeAssetIdDataStruct, DATriggerDataStruct, DATuningDataStruct, }; -use binrw::{binrw}; +use binrw::binrw; use std::fmt::Debug; -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DABuildData")] #[derive(Debug)] pub struct DABuildDataStruct { #[paramacro::serialized_field = "Name"] @@ -28,8 +29,8 @@ pub struct DABuildDataStruct { #[cfg(test)] mod tests { use super::*; - use binrw::BinRead; - use std::io::Cursor; + use binrw::{BinRead, BinWrite}; + use std::{fs::write, io::Cursor}; #[test] fn read_build_data() { diff --git a/src/common.rs b/src/common.rs index d3ab2a7..df62998 100644 --- a/src/common.rs +++ b/src/common.rs @@ -48,6 +48,10 @@ pub(crate) fn write_string_with_length(string: &String) -> BinResult<()> { Ok(()) } +pub(crate) fn size_of_string_with_length(string: &str) -> u32 { + 4 + string.len() as u32 + 1 +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/enum_property.rs b/src/enum_property.rs index 0a779b6..b669ee4 100644 --- a/src/enum_property.rs +++ b/src/enum_property.rs @@ -1,5 +1,5 @@ -use binrw::binrw; use crate::common::{read_string_with_length, write_string_with_length}; +use binrw::binrw; #[binrw] #[derive(Debug)] @@ -15,6 +15,18 @@ pub struct EnumProperty { pub value: String, } +impl crate::structs::PropertyBase for EnumProperty { + fn type_name() -> &'static str { + return "EnumProperty"; + } + + fn size_in_bytes(&self) -> u32 { + 8 + crate::common::size_of_string_with_length(&self.enum_type) + + 1 + + crate::common::size_of_string_with_length(&self.value) + } +} + #[cfg(test)] mod tests { use super::*; @@ -25,32 +37,12 @@ mod tests { fn read_enum() { // Persistent.sav let data = [ - 0x29, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, - 0x00, 0x00, 0x00, - 0x45, 0x44, 0x41, - 0x57, 0x65, 0x61, - 0x70, 0x6f, 0x6e, - 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, - 0x50, 0x6f, 0x73, - 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x00, - 0x00, 0x25, 0x00, - 0x00, 0x00, 0x45, - 0x44, 0x41, 0x57, - 0x65, 0x61, 0x70, - 0x6f, 0x6e, 0x4d, - 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x50, - 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, - 0x6e, 0x3a, 0x3a, - 0x46, 0x72, 0x6f, - 0x6e, 0x74, 0x57, - 0x65, 0x61, 0x70, - 0x6f, 0x6e, 0x00 + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x45, 0x44, + 0x41, 0x57, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x45, + 0x44, 0x41, 0x57, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x46, 0x72, 0x6f, 0x6e, + 0x74, 0x57, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x00, ]; let mut cursor = Cursor::new(data); let decoded = EnumProperty::read_le(&mut cursor).unwrap(); diff --git a/src/float_property.rs b/src/float_property.rs index 7b93bc4..4e844fb 100644 --- a/src/float_property.rs +++ b/src/float_property.rs @@ -9,6 +9,16 @@ pub struct FloatProperty { pub value: f32, } +impl crate::structs::PropertyBase for FloatProperty { + fn type_name() -> &'static str { + return "FloatProperty"; + } + + fn size_in_bytes(&self) -> u32 { + 4 + 5 + 4 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/guid.rs b/src/guid.rs index 411a6be..bf18c06 100644 --- a/src/guid.rs +++ b/src/guid.rs @@ -10,6 +10,20 @@ pub struct Guid { pub d: u32, } +impl crate::structs::PropertyBase for Guid { + fn type_name() -> &'static str { + return "StructProperty"; + } + + fn struct_name() -> Option<&'static str> { + return Some("Guid"); + } + + fn size_in_bytes(&self) -> u32 { + 16 + } +} + impl fmt::Debug for Guid { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&format!( diff --git a/src/int_property.rs b/src/int_property.rs index abff50d..b2c32b1 100644 --- a/src/int_property.rs +++ b/src/int_property.rs @@ -9,6 +9,16 @@ pub struct IntProperty { pub value: u32, } +impl crate::structs::PropertyBase for IntProperty { + fn type_name() -> &'static str { + return "IntProperty"; + } + + fn size_in_bytes(&self) -> u32 { + 4 + 5 + 4 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/lib.rs b/src/lib.rs index d389bc2..7d17ad3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod array_property; pub mod bool_property; mod build_data; mod common; +mod enum_property; pub mod float_property; mod guid; pub mod int_property; @@ -14,7 +15,6 @@ pub mod set_property; pub mod str_property; pub mod struct_property; mod structs; -mod enum_property; use binrw::helpers::until_eof; use std::fs::write; @@ -30,7 +30,7 @@ use crate::name_property::NameProperty; use crate::set_property::SetProperty; use crate::str_property::StrProperty; use crate::struct_property::StructProperty; -use binrw::{BinRead, BinResult, binrw, BinWrite}; +use binrw::{BinRead, BinResult, BinWrite, binrw}; use flate2::bufread::ZlibDecoder; // Used in ArrayProperty exclusively, but could be used instead of magic above diff --git a/src/linear_color.rs b/src/linear_color.rs index 9f4e278..01ebcda 100644 --- a/src/linear_color.rs +++ b/src/linear_color.rs @@ -9,6 +9,20 @@ pub struct LinearColorStruct { pub a: f32, } +impl crate::structs::PropertyBase for LinearColorStruct { + fn type_name() -> &'static str { + return "StructProperty"; + } + + fn struct_name() -> Option<&'static str> { + return Some("LinearColor"); + } + + fn size_in_bytes(&self) -> u32 { + 16 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/map_property.rs b/src/map_property.rs index bc41783..4ed90bc 100644 --- a/src/map_property.rs +++ b/src/map_property.rs @@ -2,8 +2,8 @@ use crate::common::{ read_bool_from, read_string_with_length, write_bool_as, write_string_with_length, }; use crate::guid::Guid; -use crate::struct_property::Struct; -use crate::structs::PrimaryAssetNameProperty; +use crate::struct_property::{Struct, calc_size_in_bytes}; +use crate::structs::{PrimaryAssetNameProperty, PropertyBase}; use binrw::{BinRead, BinResult, binrw}; // parse until we can't parse no more. kind of a hack for how we run into the end of Persistent.Sav @@ -203,15 +203,69 @@ fn custom_parser( Ok(result) } -fn calc_size_in_bytes(entries: &Vec) -> u32 { - // TODO: stub - 49 +fn calc_entry_size_in_bytes(prop: &MapProperty) -> u32 { + // TODO: complete guesswork still + let mut size = 4; + + for entry in &prop.entries { + size += match &entry.key { + MapKeyProperty::String(string_map_key) => { + crate::common::size_of_string_with_length(&string_map_key.value) + } + MapKeyProperty::StructMaybe(struct_maybe_key) => { + crate::common::size_of_string_with_length(&struct_maybe_key.unk_name) + + 8 + + crate::common::size_of_string_with_length(&struct_maybe_key.unk_type) + + crate::common::size_of_string_with_length(&struct_maybe_key.struct_name) + + 17 + + crate::struct_property::calc_size_in_bytes(&struct_maybe_key.r#struct) + } + MapKeyProperty::Enum(map_sub_enum_property) => { + crate::common::size_of_string_with_length(&map_sub_enum_property.value) + } + MapKeyProperty::EnumAgain(map_sub_enum_property) => { + crate::common::size_of_string_with_length(&map_sub_enum_property.value) + } + MapKeyProperty::GUID(guid) => guid.size_in_bytes(), + MapKeyProperty::SomeID(guid) => guid.size_in_bytes(), + MapKeyProperty::SomeID2(guid) => guid.size_in_bytes(), + MapKeyProperty::ArrayStruct(struct_maybe_key) => { + crate::common::size_of_string_with_length(&struct_maybe_key.unk_name) + + 8 + + crate::common::size_of_string_with_length(&struct_maybe_key.unk_type) + + crate::common::size_of_string_with_length(&struct_maybe_key.struct_name) + + 17 + + crate::struct_property::calc_size_in_bytes(&struct_maybe_key.r#struct) + } + MapKeyProperty::SomeID3(guid) => guid.size_in_bytes(), + MapKeyProperty::SoftObjectProperty(string_map_key) => { + crate::common::size_of_string_with_length(&string_map_key.value) + } + }; + size += match &entry.value { + MabSubProperty::Name(map_sub_name_property) => { + crate::common::size_of_string_with_length(&map_sub_name_property.value) + } + MabSubProperty::Struct(map_sub_struct_property) => 0, + MabSubProperty::Float(map_sub_float_property) => 4, + MabSubProperty::String(map_sub_str_property) => { + crate::common::size_of_string_with_length(&map_sub_str_property.value) + } + MabSubProperty::Bool(map_sub_bool_property) => 1, + MabSubProperty::Int(map_sub_int_property) => 4, + MabSubProperty::Enum(map_sub_enum_property) => { + crate::common::size_of_string_with_length(&map_sub_enum_property.value) + } + } + } + + size } #[binrw] #[derive(Debug)] pub struct MapProperty { - #[bw(calc = calc_size_in_bytes(entries))] + #[bw(calc = calc_entry_size_in_bytes(&self))] pub size_in_bytes: u32, #[brw(pad_before = 4)] @@ -231,6 +285,21 @@ pub struct MapProperty { pub entries: Vec, } +impl crate::structs::PropertyBase for MapProperty { + fn type_name() -> &'static str { + return "MapProperty"; + } + + fn size_in_bytes(&self) -> u32 { + 4 + 4 + + crate::common::size_of_string_with_length(&self.key_name) + + crate::common::size_of_string_with_length(&self.value_name) + + 5 + + 4 + + calc_entry_size_in_bytes(&self) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/name_property.rs b/src/name_property.rs index 9a21a31..a0845ed 100644 --- a/src/name_property.rs +++ b/src/name_property.rs @@ -13,3 +13,13 @@ pub struct NameProperty { #[bw(write_with = write_string_with_length)] pub value: String, } + +impl crate::structs::PropertyBase for NameProperty { + fn type_name() -> &'static str { + return "NameProperty"; + } + + fn size_in_bytes(&self) -> u32 { + 5 + 4 + crate::common::size_of_string_with_length(&self.value) + } +} diff --git a/src/primary_asset_id.rs b/src/primary_asset_id.rs index 45623c8..950cb46 100644 --- a/src/primary_asset_id.rs +++ b/src/primary_asset_id.rs @@ -1,13 +1,16 @@ -use crate::structs::StructField; +use crate::{ + name_property::NameProperty, primary_asset_type::PrimaryAssetTypeStruct, structs::StructField, +}; use binrw::binrw; -#[binrw] +#[paramacro::serialized_struct("PrimaryAssetId")] #[derive(Debug)] pub struct PrimaryAssetIdStruct { - //#[brw(pad_before = 17)] - //#[br(pad_after = 9)] // "None" - #[br(args {name: "PrimaryAssetType", r#type: "StructProperty" })] - primary_asset_type: StructField, + #[paramacro::serialized_field = "PrimaryAssetType"] + pub primary_asset_type: PrimaryAssetTypeStruct, + + #[paramacro::serialized_field = "PrimaryAssetName"] + pub primary_asset_name: NameProperty, } #[cfg(test)] @@ -19,21 +22,23 @@ mod tests { #[test] fn read_id() { let data = [ - 0x11, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x50, - 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x6f, 0x74, - 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x11, 0x00, 0x00, - 0x00, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, - 0x74, 0x79, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x49, 0x74, 0x65, 0x6d, 0x5f, 0x50, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x48, 0x65, 0x61, 0x6c, - 0x74, 0x68, 0x50, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, - 0x65, 0x00 + 0x11, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x00, 0x37, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, + 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, 0x65, + 0x00, 0x11, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4e, 0x61, + 0x6d, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x00, 0x1d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x49, 0x74, 0x65, 0x6d, + 0x5f, 0x50, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x50, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, + 0x65, 0x00, ]; let mut cursor = Cursor::new(data); let decoded = PrimaryAssetIdStruct::read_le(&mut cursor).unwrap(); diff --git a/src/primary_asset_type.rs b/src/primary_asset_type.rs index 07540a2..0221571 100644 --- a/src/primary_asset_type.rs +++ b/src/primary_asset_type.rs @@ -1,16 +1,11 @@ -use crate::structs::StructField; +use crate::{name_property::NameProperty, structs::StructField}; use binrw::binrw; -// TODO: im pretty sure everything about this + PrimaryAssetId is wrong. i think primary_asset_name needs to belong in PrimaryAssetId -#[binrw] +#[paramacro::serialized_struct("PrimaryAssetType")] #[derive(Debug)] pub struct PrimaryAssetTypeStruct { - #[brw(pad_after = 9)] - #[br(args { name: "Name", r#type: "NameProperty" })] - name: StructField, - #[brw(pad_after = 9)] - #[br(args { name: "PrimaryAssetName", r#type: "NameProperty" })] - primary_asset_name: StructField, + #[paramacro::serialized_field = "Name"] + pub name: NameProperty, } #[cfg(test)] @@ -22,15 +17,16 @@ mod tests { #[test] fn read_id() { let data = [ - 0x05, 0x00, 0x00, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4e, 0x61, 0x6d, - 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x11, 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x00, 0x1d, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x49, 0x74, 0x65, 0x6d, 0x5f, 0x50, - 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x6f, 0x74, 0x69, - 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00 + 0x05, 0x00, 0x00, 0x00, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4e, + 0x61, 0x6d, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x6f, 0x74, + 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4e, 0x61, 0x6d, 0x65, + 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x49, 0x74, 0x65, 0x6d, 0x5f, 0x50, + 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x6f, + 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, ]; let mut cursor = Cursor::new(data); let decoded = PrimaryAssetTypeStruct::read_le(&mut cursor).unwrap(); diff --git a/src/set_property.rs b/src/set_property.rs index 18f7fcc..585c5fa 100644 --- a/src/set_property.rs +++ b/src/set_property.rs @@ -1,8 +1,24 @@ use crate::common::{read_string_with_length, write_string_with_length}; use crate::map_property::{KeyType, MapSubStrProperty}; use crate::struct_property::Struct; +use crate::structs::PrimaryAssetNameProperty; use binrw::{BinRead, BinResult, binrw}; +// hack, we should be checking for "none" instead +#[binrw::parser(reader, endian)] +fn cc() -> BinResult> { + let mut result = Vec::::new(); + + loop { + if let Ok(str) = PrimaryAssetNameProperty::read_options(reader, endian, ()) { + result.push(str); + } else { + break; + } + } + Ok(result) +} + #[binrw] #[derive(Debug)] #[br(import { magic: &str, name: &str })] @@ -10,19 +26,8 @@ pub enum SetValue { // NOTE: the name checking is just a temporary workaround #[br(pre_assert("StructProperty" == magic && name != "OpenedStrongBoxIds" && name != "AcquiredItemBoxIds"))] Struct { - #[br(parse_with = read_string_with_length)] - #[bw(write_with = write_string_with_length)] - struct_type: String, - #[br(parse_with = read_string_with_length)] - #[bw(write_with = write_string_with_length)] - r#type: String, - #[br(parse_with = read_string_with_length)] - #[bw(write_with = write_string_with_length)] - #[br(pad_before = 8)] - struct_type_for_some_reason_again: String, - #[br(args { magic: &struct_type_for_some_reason_again })] - #[brw(pad_before = 17)] - r#struct: Struct, + #[br(parse_with = cc)] + fields: Vec, }, #[br(pre_assert("StringProperty" == magic || "NameProperty" == magic))] String(MapSubStrProperty), diff --git a/src/str_property.rs b/src/str_property.rs index 528f7ba..269fe75 100644 --- a/src/str_property.rs +++ b/src/str_property.rs @@ -14,6 +14,16 @@ pub struct StrProperty { pub value: String, } +impl crate::structs::PropertyBase for StrProperty { + fn type_name() -> &'static str { + return "StrProperty"; + } + + fn size_in_bytes(&self) -> u32 { + 5 + 4 + crate::common::size_of_string_with_length(&self.value) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/struct_property.rs b/src/struct_property.rs index fab75ef..85d7564 100644 --- a/src/struct_property.rs +++ b/src/struct_property.rs @@ -5,11 +5,10 @@ use crate::linear_color::LinearColorStruct; use crate::primary_asset_id::PrimaryAssetIdStruct; use crate::primary_asset_type::PrimaryAssetTypeStruct; use crate::structs::{ - DAAssembleIdDataStruct, DACustomizeAssetIdDataStruct, - DAHumanoidColoringDataStruct, DAHumanoidFigureData, DALoadOptionStruct, - DAMachineColoringDataStruct, DAModuleColorStruct, DAModuleItemDataStruct, DATriggerDataStruct, - DATuningDataStruct, DATuningPointData, DateTimeStruct, QuatStruct, SaveSlotInfoStruct, - TransformStruct, VectorStruct, + DAAssembleIdDataStruct, DACustomizeAssetIdDataStruct, DAHumanoidColoringDataStruct, + DAHumanoidFigureData, DALoadOptionStruct, DAMachineColoringDataStruct, DAModuleColorStruct, + DAModuleItemDataStruct, DATriggerDataStruct, DATuningDataStruct, DATuningPointData, + DateTimeStruct, PropertyBase, QuatStruct, SaveSlotInfoStruct, TransformStruct, VectorStruct, }; use binrw::binrw; @@ -17,50 +16,91 @@ use binrw::binrw; #[derive(Debug)] #[br(import { magic: &str })] pub enum Struct { - #[br(pre_assert(magic == "DateTime"))] + #[br(pre_assert(magic == DateTimeStruct::struct_name().unwrap()))] DateTime(DateTimeStruct), - #[br(pre_assert(magic == "DALoadOption"))] + #[br(pre_assert(magic == DALoadOptionStruct::struct_name().unwrap()))] DALoadOption(DALoadOptionStruct), - #[br(pre_assert(magic == "SaveSlotInfo"))] + #[br(pre_assert(magic == SaveSlotInfoStruct::struct_name().unwrap()))] SaveSlotInfo(SaveSlotInfoStruct), - #[br(pre_assert(magic == "PrimaryAssetType"))] + #[br(pre_assert(magic == PrimaryAssetTypeStruct::struct_name().unwrap()))] PrimaryAssetType(PrimaryAssetTypeStruct), - #[br(pre_assert(magic == "PrimaryAssetId"))] + #[br(pre_assert(magic == PrimaryAssetIdStruct::struct_name().unwrap()))] PrimaryAssetId(PrimaryAssetIdStruct), - #[br(pre_assert(magic == "DAModuleItemData"))] + #[br(pre_assert(magic == DAModuleItemDataStruct::struct_name().unwrap()))] DAModuleItemData(DAModuleItemDataStruct), - #[br(pre_assert(magic == "DABuildData"))] + #[br(pre_assert(magic == DABuildDataStruct::struct_name().unwrap()))] DABuildData(DABuildDataStruct), - #[br(pre_assert(magic == "DAAssembleIdData"))] + #[br(pre_assert(magic == DAAssembleIdDataStruct::struct_name().unwrap()))] DAAssembleIdData(DAAssembleIdDataStruct), - #[br(pre_assert(magic == "Guid"))] + #[br(pre_assert(magic == Guid::struct_name().unwrap()))] Guid(Guid), - #[br(pre_assert(magic == "DAMachineColoringData"))] + #[br(pre_assert(magic == DAMachineColoringDataStruct::struct_name().unwrap()))] DAMachineColoringData(DAMachineColoringDataStruct), - #[br(pre_assert(magic == "DAModuleColor"))] + #[br(pre_assert(magic == DAModuleColorStruct::struct_name().unwrap()))] DAModuleColor(DAModuleColorStruct), - #[br(pre_assert(magic == "LinearColor"))] + #[br(pre_assert(magic == LinearColorStruct::struct_name().unwrap()))] LinearColor(LinearColorStruct), - #[br(pre_assert(magic == "DATriggerData"))] + #[br(pre_assert(magic == DATriggerDataStruct::struct_name().unwrap()))] DATriggerData(DATriggerDataStruct), - #[br(pre_assert(magic == "DACustomizeAssetIdData"))] + #[br(pre_assert(magic == DACustomizeAssetIdDataStruct::struct_name().unwrap()))] DACustomizeAssetIdData(DACustomizeAssetIdDataStruct), - #[br(pre_assert(magic == "DATuningData"))] + #[br(pre_assert(magic == DATuningDataStruct::struct_name().unwrap()))] DATuningData(DATuningDataStruct), - #[br(pre_assert(magic == "DAHumanoidColoringData"))] + #[br(pre_assert(magic == DAHumanoidColoringDataStruct::struct_name().unwrap()))] DAHumanoidColoringData(DAHumanoidColoringDataStruct), - #[br(pre_assert(magic == "DAHumanoidFigureData"))] + #[br(pre_assert(magic == DAHumanoidFigureData::struct_name().unwrap()))] DAHumanoidFigureData(DAHumanoidFigureData), - #[br(pre_assert(magic == "DATuningPointData"))] + #[br(pre_assert(magic == DATuningPointData::struct_name().unwrap()))] DATuningPointData(DATuningPointData), - #[br(pre_assert(magic == "Transform"))] + #[br(pre_assert(magic == TransformStruct::struct_name().unwrap()))] Transform(TransformStruct), - #[br(pre_assert(magic == "Quat"))] + #[br(pre_assert(magic == QuatStruct::struct_name().unwrap()))] Quat(QuatStruct), - #[br(pre_assert(magic == "Vector"))] + #[br(pre_assert(magic == VectorStruct::struct_name().unwrap()))] Vector(VectorStruct), } +pub(crate) fn calc_size_in_bytes(r#struct: &Struct) -> u32 { + // todo + return match r#struct { + Struct::DateTime(date_time_struct) => date_time_struct.size_in_bytes(), + Struct::DALoadOption(daload_option_struct) => daload_option_struct.size_in_bytes(), + Struct::SaveSlotInfo(save_slot_info_struct) => save_slot_info_struct.size_in_bytes(), + Struct::PrimaryAssetType(primary_asset_type_struct) => { + primary_asset_type_struct.size_in_bytes() + } + Struct::PrimaryAssetId(primary_asset_id_struct) => primary_asset_id_struct.size_in_bytes(), + Struct::DAModuleItemData(damodule_item_data_struct) => { + damodule_item_data_struct.size_in_bytes() + } + Struct::DABuildData(dabuild_data_struct) => dabuild_data_struct.size_in_bytes(), + Struct::DAAssembleIdData(daassemble_id_data_struct) => { + daassemble_id_data_struct.size_in_bytes() + } + Struct::Guid(guid) => guid.size_in_bytes(), + Struct::DAMachineColoringData(damachine_coloring_data_struct) => { + damachine_coloring_data_struct.size_in_bytes() + } + Struct::DAModuleColor(damodule_color_struct) => damodule_color_struct.size_in_bytes(), + Struct::LinearColor(linear_color_struct) => linear_color_struct.size_in_bytes(), + Struct::DATriggerData(datrigger_data_struct) => datrigger_data_struct.size_in_bytes(), + Struct::DACustomizeAssetIdData(dacustomize_asset_id_data_struct) => { + dacustomize_asset_id_data_struct.size_in_bytes() + } + Struct::DATuningData(datuning_data_struct) => datuning_data_struct.size_in_bytes(), + Struct::DAHumanoidColoringData(dahumanoid_coloring_data_struct) => { + dahumanoid_coloring_data_struct.size_in_bytes() + } + Struct::DAHumanoidFigureData(dahumanoid_figure_data) => { + dahumanoid_figure_data.size_in_bytes() + } + Struct::DATuningPointData(datuning_point_data) => datuning_point_data.size_in_bytes(), + Struct::Transform(transform_struct) => transform_struct.size_in_bytes(), + Struct::Quat(quat_struct) => quat_struct.size_in_bytes(), + Struct::Vector(vector_struct) => vector_struct.size_in_bytes(), + }; +} + #[binrw] #[derive(Debug)] pub struct StructProperty { @@ -70,6 +110,6 @@ pub struct StructProperty { #[bw(write_with = write_string_with_length)] pub struct_name: String, #[br(args { magic: &struct_name })] - #[br(pad_before = 17)] + #[brw(pad_before = 17)] pub r#struct: Struct, } diff --git a/src/structs.rs b/src/structs.rs index 54af307..fcdebb5 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -3,6 +3,7 @@ use crate::array_property::ArrayProperty; use crate::bool_property::BoolProperty; use crate::build_data::DABuildDataStruct; use crate::common::{read_string_with_length, write_string_with_length}; +use crate::enum_property::EnumProperty; use crate::float_property::FloatProperty; use crate::guid::Guid; use crate::int_property::IntProperty; @@ -12,9 +13,8 @@ use crate::name_property::NameProperty; use crate::primary_asset_id::PrimaryAssetIdStruct; use crate::primary_asset_type::PrimaryAssetTypeStruct; use crate::str_property::StrProperty; -use binrw::{BinRead, BinResult, binrw}; +use binrw::{BinRead, BinResult, BinWrite, binrw}; use std::fmt::Debug; -use crate::enum_property::EnumProperty; #[binrw] #[derive(Debug)] @@ -22,14 +22,14 @@ pub struct DateTimeStruct { pub unk: [u8; 8], } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DALoadOption")] #[derive(Debug)] pub struct DALoadOptionStruct { #[paramacro::serialized_field = "LoadTypes"] pub load_types: IntProperty, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("SaveSlotInfo")] #[derive(Debug)] pub struct SaveSlotInfoStruct { #[paramacro::serialized_field = "Name"] @@ -83,14 +83,14 @@ pub struct StructField { pub key: Option>, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DAModuleItemData")] #[derive(Debug)] pub struct DAModuleItemDataStruct { #[paramacro::serialized_field = "ModuleLevel"] pub module_level: IntProperty, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DAAssembleIdData")] #[derive(Debug)] pub struct DAAssembleIdDataStruct { #[paramacro::serialized_field = "Hanger"] @@ -124,7 +124,7 @@ pub struct DAAssembleIdDataStruct { pub coloring_data: DAMachineColoringDataStruct, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DAMachineColoringData")] #[derive(Debug)] pub struct DAMachineColoringDataStruct { #[paramacro::serialized_field = "Hanger"] @@ -155,7 +155,7 @@ pub struct DAMachineColoringDataStruct { pub right_rear_weapon: DAModuleColorStruct, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DAHumanoidColoringData")] #[derive(Debug)] pub struct DAHumanoidColoringDataStruct { #[paramacro::serialized_field = "Skin"] @@ -192,7 +192,7 @@ pub struct DAHumanoidColoringDataStruct { pub body_sub3: LinearColorStruct, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DAModuleColor")] #[derive(Debug)] pub struct DAModuleColorStruct { #[paramacro::serialized_field = "Main"] @@ -208,7 +208,7 @@ pub struct DAModuleColorStruct { pub glow: LinearColorStruct, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DATriggerData")] #[derive(Debug)] pub struct DATriggerDataStruct { #[paramacro::serialized_field = "A"] @@ -221,7 +221,7 @@ pub struct DATriggerDataStruct { pub c: EnumProperty, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DACustomizeAssetIdData")] #[derive(Debug)] pub struct DACustomizeAssetIdDataStruct { #[paramacro::serialized_field = "Body"] @@ -252,7 +252,7 @@ pub struct DACustomizeAssetIdDataStruct { pub inverse_back_hair_mesh: BoolProperty, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DAHumanoidFigureData")] #[derive(Debug)] pub struct DAHumanoidFigureData { #[paramacro::serialized_field = "BustUp"] @@ -271,7 +271,7 @@ pub struct DAHumanoidFigureData { pub waist_up: FloatProperty, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DATuningData")] #[derive(Debug)] pub struct DATuningDataStruct { #[paramacro::serialized_field = "GrantedTuningPointList"] @@ -284,7 +284,7 @@ pub struct SavedBuildData { pub build_data: DABuildDataStruct, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("DATuningPointData")] #[derive(Debug)] pub struct DATuningPointData { #[paramacro::serialized_field = "TuningPoint"] @@ -294,7 +294,7 @@ pub struct DATuningPointData { max_tuning_point: IntProperty, } -#[paramacro::serialized_struct] +#[paramacro::serialized_struct("Transform")] #[derive(Debug)] pub struct TransformStruct { #[paramacro::serialized_field = "Rotation"] @@ -340,11 +340,11 @@ pub struct StructFieldPrelude { #[binrw] #[derive(Debug)] pub struct StructPrelude { - pub unk: u32, + pub size_in_bytes: u32, #[brw(pad_before = 4)] #[br(parse_with = read_string_with_length)] #[bw(write_with = write_string_with_length)] - #[br(pad_after = 17)] + #[brw(pad_after = 17)] pub struct_name: String, } @@ -359,10 +359,105 @@ pub(crate) fn read_struct_field = ()> + Debug>( name, prelude.property_name ); } - println!("{:#?}", prelude); + // TODO: type check with type_name() if prelude.type_name == "StructProperty" { - println!("{:#?}", StructPrelude::read_le(reader)?); + StructPrelude::read_le(reader)?; } let val = T::read_options(reader, endian, ())?; Ok(val) } + +pub(crate) trait PropertyBase { + fn type_name() -> &'static str; + fn size_in_bytes(&self) -> u32; + + // these are only relevant for structs: + // FIXME: this isn't great' + fn struct_name() -> Option<&'static str> { + None + } +} + +impl PropertyBase for DateTimeStruct { + fn type_name() -> &'static str { + return "StructProperty"; + } + + fn struct_name() -> Option<&'static str> { + return Some("DateTime"); + } + + fn size_in_bytes(&self) -> u32 { + 8 + } +} + +impl PropertyBase for VectorStruct { + fn type_name() -> &'static str { + return "StructProperty"; + } + + fn struct_name() -> Option<&'static str> { + return Some("Vector"); + } + + fn size_in_bytes(&self) -> u32 { + 16 + } +} + +impl PropertyBase for QuatStruct { + fn type_name() -> &'static str { + return "StructProperty"; + } + + fn struct_name() -> Option<&'static str> { + return Some("Quat"); + } + + fn size_in_bytes(&self) -> u32 { + 16 + } +} + +#[binrw::writer(writer, endian)] +pub(crate) fn write_struct_field = ()> + Debug>( + structure: &T, + name: &str, +) -> BinResult<()> { + let prelude = StructFieldPrelude { + property_name: name.to_string(), + type_name: T::type_name().to_string(), + }; + prelude.write_le(writer)?; + if T::type_name() == "StructProperty" { + let struct_prelude = StructPrelude { + size_in_bytes: T::size_in_bytes(structure), + struct_name: T::struct_name().unwrap().to_string(), + }; + struct_prelude.write_le(writer)?; + } + structure.write_options(writer, endian, ())?; + Ok(()) +} + +pub(crate) fn calc_struct_field_prelude_byte_size( + type_name: &str, + field_name: &str, + struct_name: Option<&str>, +) -> u32 { + let mut base_size = crate::common::size_of_string_with_length(field_name); + + // This is an easy way to detect properties that are actually structs + if struct_name.is_some() { + base_size += crate::common::size_of_string_with_length("StructProperty") + + crate::common::size_of_string_with_length(struct_name.unwrap()) + + 4 + + 17 + + 4; // see struct prelude + } else { + base_size += crate::common::size_of_string_with_length(type_name); + } + + base_size +}