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 +}