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.
This commit is contained in:
Joshua Goins 2025-03-02 14:07:39 -05:00
parent 08ac990c15
commit 4801c2678a
19 changed files with 502 additions and 145 deletions

View file

@ -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()

View file

@ -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<ArrayEntry>) -> 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)]

View file

@ -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::*;

View file

@ -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() {

View file

@ -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::*;

View file

@ -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();

View file

@ -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::*;

View file

@ -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!(

View file

@ -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::*;

View file

@ -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

View file

@ -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::*;

View file

@ -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<MapEntry>) -> 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<MapEntry>,
}
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::*;

View file

@ -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)
}
}

View file

@ -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();

View file

@ -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();

View file

@ -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<Vec<PrimaryAssetNameProperty>> {
let mut result = Vec::<PrimaryAssetNameProperty>::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<PrimaryAssetNameProperty>,
},
#[br(pre_assert("StringProperty" == magic || "NameProperty" == magic))]
String(MapSubStrProperty),

View file

@ -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::*;

View file

@ -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,
}

View file

@ -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<Box<Property>>,
}
#[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<T: BinRead<Args<'static> = ()> + 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<T: PropertyBase + BinWrite<Args<'static> = ()> + 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
}