diff --git a/src/array_property.rs b/src/array_property.rs index 7573b25..f4a0d40 100644 --- a/src/array_property.rs +++ b/src/array_property.rs @@ -1,10 +1,27 @@ use crate::set_property::SetEntry; -use binrw::binrw; +use binrw::{binrw, BinRead, BinResult}; +use crate::map_property::{KeyType, MapEntry}; + +#[binrw::parser(reader, endian)] +fn custom_parser(size_in_bytes: u32, key_name: &str) -> BinResult> { + let mut result = Vec::::new(); + + let mut current = reader.stream_position().unwrap(); + let end = current + size_in_bytes as u64 - 4; + + while current < end { + result.push(SetEntry::read_options(reader, endian, (key_name,)).unwrap()); + current = reader.stream_position().unwrap(); + } + Ok(result) +} #[binrw] #[derive(Debug)] pub struct ArrayProperty { - pub unk: u32, + // Plus this int + pub size_in_bytes: u32, + #[br(temp)] #[bw(ignore)] #[br(pad_before = 4)] @@ -15,8 +32,8 @@ pub struct ArrayProperty { pub key_name: String, #[br(pad_before = 1)] - pub num_array_entries: u32, + pub unk: u32, - #[br(count = num_array_entries, args { inner: (&*key_name,) })] + #[br(parse_with = custom_parser, args(size_in_bytes, &key_name))] pub entries: Vec, } diff --git a/src/lib.rs b/src/lib.rs index d8845f6..44c2b86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,6 +72,8 @@ pub enum StringBasedProperty { Int(IntProperty), #[br(pre_assert("MapProperty" == magic))] Map(MapProperty), + #[br(pre_assert("SetProperty" == magic))] + Set(SetProperty), } #[binrw] @@ -79,6 +81,7 @@ pub enum StringBasedProperty { pub struct Entry { #[br(temp)] #[bw(ignore)] + #[br(dbg)] pub name_length: u32, #[br(count = name_length)] #[bw(map = |x : &String | x.as_bytes())] diff --git a/src/map_property.rs b/src/map_property.rs index b67a86c..ffefb8b 100644 --- a/src/map_property.rs +++ b/src/map_property.rs @@ -1,7 +1,8 @@ +use std::io::{Read, Seek, SeekFrom}; use crate::read_bool_from; use crate::struct_property::Struct; use crate::structs::PrimaryAssetNameProperty; -use binrw::binrw; +use binrw::{binrw, BinRead, BinResult}; use binrw::helpers::until_exclusive; // A struct without a name @@ -15,6 +16,16 @@ pub struct AnonymousStruct { #[binrw] #[derive(Debug)] pub struct MapSubStructProperty { + #[br(temp)] + #[bw(ignore)] + pub unk_name_length: u32, + #[br(args { is_map: true })] + pub r#struct: Struct, +} + +#[binrw] +#[derive(Debug)] +pub struct StructMaybeKey { #[br(temp)] #[bw(ignore)] pub unk_name_length: u32, @@ -29,11 +40,12 @@ pub struct MapSubStructProperty { #[br(count = unk_type_length)] #[bw(map = |x : &String | x.as_bytes())] #[br(map = | x: Vec | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())] + #[br(pad_after = 8)] // nothingness and then length for the struct pub unk_type: String, - #[br(temp)] - #[bw(ignore)] - #[br(pad_before = 8)] // dunno what this is - pub name_length: u32, + + #[br(dbg)] + #[br(pad_before = 4)] // type length + #[br(args { is_map: true })] pub r#struct: Struct, } @@ -100,16 +112,12 @@ pub struct GuidStruct { // Used in MapProperty exclusively, seems to be a shortened version of some Properties #[binrw] #[derive(Debug)] -#[br(import { magic: &str, unk: &UnknownType, is_value: bool })] +#[br(import { magic: &str, is_value: bool })] pub enum MabSubProperty { #[br(pre_assert("NameProperty" == magic))] Name(MapSubNameProperty), - #[br(pre_assert("StructProperty" == magic && *unk == UnknownType::RealStruct))] + #[br(pre_assert("StructProperty" == magic))] Struct(MapSubStructProperty), - #[br(pre_assert("StructProperty" == magic && *unk == UnknownType::AnonymousStruct))] - AnonymousStruct(AnonymousStruct), // assuming all value structs are anonymous for now - #[br(pre_assert("StructProperty" == magic && *unk == UnknownType::Guid || *unk == UnknownType::Guid2))] - GuidStruct(GuidStruct), // assuming all value structs are anonymous for now #[br(pre_assert("FloatProperty" == magic))] Float(MapSubFloatProperty), #[br(pre_assert("StrProperty" == magic))] @@ -124,42 +132,102 @@ pub enum MabSubProperty { #[binrw] #[derive(Debug)] -#[br(import(key_type: &str, value_type: &str, key_unk: &UnknownType, value_unk: &UnknownType))] -pub struct MapEntry { - //#[br(pad_before = 8)] - #[br(args { magic: key_type, unk: key_unk, is_value: true})] - pub key: MabSubProperty, +pub struct GuidStructThing { + pub guid: [u8; 16], +} - #[br(args {magic: value_type, unk: value_unk, is_value: true})] - //#[br(if(!key_is_none(&key)))] +#[binrw] +#[derive(Debug)] +pub struct SomeIDStruct { + pub guid: [u8; 16], +} + +#[binrw] +#[derive(Debug)] +pub struct SomeID2Struct { + pub guid: [u8; 16], +} + +#[binrw] +#[derive(Debug)] +pub struct StringMapKey { + pub enum_length: u32, + #[br(count = enum_length)] + #[bw(map = |x : &String | x.as_bytes())] + #[br(map = | x: Vec | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())] + pub r#enum: String, +} + +#[binrw] +#[derive(Debug)] +#[br(import { magic: &KeyType })] +pub enum MapKeyProperty { + #[br(pre_assert(*magic == KeyType::String))] + String(StringMapKey), + #[br(pre_assert(*magic == KeyType::StructMaybe))] + StructMaybe(StructMaybeKey), + #[br(pre_assert(*magic == KeyType::Enum))] + Enum(MapSubEnumProperty), + #[br(pre_assert(*magic == KeyType::EnumAgain))] + EnumAgain(MapSubEnumProperty), + #[br(pre_assert(*magic == KeyType::GUID))] + GUID(GuidStructThing), + #[br(pre_assert(*magic == KeyType::SomeID))] + SomeID(SomeIDStruct), + #[br(pre_assert(*magic == KeyType::SomeID2))] + SomeID2(SomeID2Struct), +} + +fn is_empty(key: &MapKeyProperty) -> bool { + false +} + +#[binrw] +#[derive(Debug)] +#[br(import(key_type: &KeyType, value_type: &str))] +pub struct MapEntry { + #[br(args { magic: key_type})] + #[br(dbg)] + pub key: MapKeyProperty, + + #[br(args {magic: value_type, is_value: true})] + #[br(dbg)] pub value: MabSubProperty, } #[binrw] -#[brw(repr = u8)] -#[derive(Debug, PartialEq)] -pub enum UnknownType { - // literally all guesswork, idk - String = 0x0, - Name = 0x31, - Int = 0xBA, - RealStruct = 0x51, - Enum = 0x21, - Guid = 0x45, - IDK = 0x44, - Guid2 = 0x5, - AnonymousStruct = 0xF, +#[brw(repr = u32)] +#[derive(Debug)] +#[derive(PartialEq, Clone)] +pub enum KeyType { + String = 0x1, + GUID = 0x3, + SomeID = 0x6, + SomeID2 = 0x10, + EnumAgain = 0x4, + Enum = 0x7, + StructMaybe = 0xC, +} + +#[binrw::parser(reader, endian)] +fn custom_parser(size_in_bytes: u32, key_type: &KeyType, value_type: &str) -> BinResult> { + let mut result = Vec::::new(); + + let mut current = reader.stream_position().unwrap(); + let end = current + size_in_bytes as u64 - 5 - 3; + + while current < end { + result.push(MapEntry::read_options(reader, endian, (&key_type, &value_type)).unwrap()); + current = reader.stream_position().unwrap(); + } + Ok(result) } #[binrw] #[derive(Debug)] pub struct MapProperty { - /* - pub key_unk: UnknownType, - #[br(pad_after = 2)] // actually might need all 4 bytes? - - pub value_unk: UnknownType,*/ // Plus this int + #[br(dbg)] pub size_in_bytes: u32, #[br(temp)] @@ -179,16 +247,12 @@ pub struct MapProperty { #[br(map = | x: Vec | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())] pub value_name: String, - //#[br(pad_before = 5)] - // - //pub num_map_entries: u32, - #[br(count = size_in_bytes + 1)] - #[br(temp)] - #[bw(ignore)] - pub dummy_data: Vec, - // TODO: uncomment when map parsing reliably works, otherwise just dummy the data out - //#[br(count = num_map_entries, args { inner: (&*key_name, &*value_name, &key_unk, &value_unk) })] - //pub entries: Vec, + #[br(pad_before = 5)] + #[br(dbg)] + pub key_type: KeyType, + + #[br(parse_with = custom_parser, args(size_in_bytes, &key_type, &value_name))] + pub entries: Vec, } #[cfg(test)] @@ -215,14 +279,14 @@ mod tests { assert_eq!(decoded.size_in_bytes, 49); assert_eq!(decoded.key_name, "StrProperty"); assert_eq!(decoded.value_name, "StrProperty"); - /*let String(key_property) = &decoded.entries.first().unwrap().key else { + let MapKeyProperty::String(key_property) = &decoded.entries.first().unwrap().key else { panic!("StrProperty!") }; let String(value_property) = &decoded.entries.first().unwrap().value else { panic!("StrProperty!") }; - assert_eq!(key_property.name, "AR0XJGFWA6HNIQ1AAUJ9UR828"); - assert_eq!(value_property.name, "NAME 1");*/ + assert_eq!(key_property.r#enum, "AR0XJGFWA6HNIQ1AAUJ9UR828"); + assert_eq!(value_property.name, "NAME 1"); } #[test] @@ -252,14 +316,14 @@ mod tests { assert_eq!(decoded.size_in_bytes, 186); assert_eq!(decoded.key_name, "NameProperty"); assert_eq!(decoded.value_name, "IntProperty"); - /*let Name(key_property) = &decoded.entries.first().unwrap().key else { + let MapKeyProperty::Enum(key_property) = &decoded.entries.first().unwrap().key else { panic!("Name!") }; let Int(value_property) = &decoded.entries.first().unwrap().value else { panic!("Int!") }; - assert_eq!(key_property.sub_property_name, "SelectedMachine"); - assert_eq!(value_property.value, 2);*/ + assert_eq!(key_property.r#enum, "SelectedMachine"); + assert_eq!(value_property.value, 2); // TODO: test the rest of the values } } diff --git a/src/struct_property.rs b/src/struct_property.rs index b649c03..9930f55 100644 --- a/src/struct_property.rs +++ b/src/struct_property.rs @@ -1,13 +1,9 @@ -use crate::structs::{ - DAAssembleIdDataStruct, DABuildDataStruct, DACharacterCommonStatusStruct, DALoadOptionStruct, - DAMachineColoringDataStruct, DAModuleColorStruct, DAModuleItemDataStruct, DateTimeStruct, - GuidStruct, LinearColorStruct, ParamsStruct, PrimaryAssetIdStruct, PrimaryAssetTypeStruct, - SaveSlotInfoStruct, -}; +use crate::structs::{CarryCountProperty, DAAssembleIdDataStruct, DABuildDataStruct, DACharacterCommonStatusStruct, DALoadOptionStruct, DAMachineColoringDataStruct, DAModuleColorStruct, DAModuleItemDataStruct, DateTimeStruct, GuidStruct, LinearColorStruct, ParamsStruct, PrimaryAssetIdStruct, PrimaryAssetNameProperty, SaveSlotInfoStruct}; use binrw::binrw; #[binrw] #[derive(Debug)] +#[br(import { is_map: bool })] pub enum Struct { #[br(magic = b"DateTime\0")] DateTime(DateTimeStruct), @@ -20,7 +16,15 @@ pub enum Struct { #[br(magic = b"Params\0")] Params(ParamsStruct), #[br(magic = b"PrimaryAssetType\0")] - PrimaryAssetType(PrimaryAssetTypeStruct), + PrimaryAssetType { + #[br(pad_before = 17)] + #[br(dbg)] + name: PrimaryAssetNameProperty, + #[br(pad_before = 9)] // "None" and it's length in bytes plus the null terminator + #[br(pad_after = 9)] // ditto + #[br(dbg)] + primary_asset_name: PrimaryAssetNameProperty, + }, #[br(magic = b"PrimaryAssetId\0")] PrimaryAssetId(PrimaryAssetIdStruct), #[br(magic = b"DAModuleItemData\0")] @@ -37,6 +41,42 @@ pub enum Struct { DAModuleColor(DAModuleColorStruct), #[br(magic = b"LinearColor\0")] LinearColor(LinearColorStruct), + #[br(magic = b"CarryCount\0")] + CarryCount { + #[br(dbg)] + carry_count: CarryCountProperty, + #[br(dbg)] + #[br(pad_before = 15)] // "StoreCount" + 4 bytes for length + 1 byte for endofstring + #[br(pad_after = 9)] // "None" + 1 byte for endofstring + 4 bytes for length + store_count: CarryCountProperty, + }, + #[br(magic = b"Map\0")] + Map { + #[br(dbg)] + #[br(pad_after = 9)] // "None" + 1 byte for endofstring + 4 bytes for length + map: CarryCountProperty, + }, + // TODO: im almost certain this isn't a struct name + #[br(magic = b"ID\0")] + ID { + unk: [u8; 149], // not sure how to parse this yet + #[br(dbg)] + #[br(pad_after = 9)] // "None" and it's length in bytes plus the null terminator + name: PrimaryAssetNameProperty, + + #[br(dbg)] + #[br(pad_after = 9)] // "None" and it's length in bytes plus the null terminator + primary_asset_name: PrimaryAssetNameProperty, + + #[br(dbg)] + data: [u8; 137], + }, + #[br(magic = b"Set\0")] + Set { + #[br(dbg)] + #[br(pad_after = 9)] // "None" + 1 byte for endofstring + 4 bytes for length + set: CarryCountProperty, + }, } #[binrw] @@ -47,5 +87,6 @@ pub struct StructProperty { #[bw(ignore)] #[br(pad_before = 4)] pub name_length: u32, + #[br(args { is_map: false })] pub r#struct: Struct, } diff --git a/src/structs.rs b/src/structs.rs index 7a8ffad..37f97bb 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -38,35 +38,49 @@ pub struct ParamsStruct { pub struct PrimaryAssetNameProperty { #[br(temp)] #[bw(ignore)] + #[br(dbg)] pub property_name_length: u32, #[br(count = property_name_length)] #[bw(map = |x : &String | x.as_bytes())] #[br(map = | x: Vec | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())] + #[br(dbg)] pub property_name: String, #[br(temp)] #[bw(ignore)] #[br(if(property_name != "None"))] + #[br(dbg)] pub type_length: u32, #[br(count = type_length)] #[bw(map = |x : &String | x.as_bytes())] #[br(map = | x: Vec | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())] #[br(if(property_name != "None"))] + #[br(dbg)] pub type_name: String, #[br(if(property_name != "None"))] #[br(args { magic: &type_name})] + //#[br(dbg)] pub key: Option>, } + #[binrw] #[derive(Debug)] -pub struct PrimaryAssetTypeStruct { - #[br(pad_before = 17)] - name: PrimaryAssetNameProperty, - #[br(pad_before = 9)] // "None" and it's length in bytes plus the null terminator - #[br(pad_after = 9)] // ditto - primary_asset_name: PrimaryAssetNameProperty, +pub struct CarryCountProperty { + #[br(temp)] + #[bw(ignore)] + #[br(dbg)] + pub property_name_length: u32, + #[br(count = property_name_length)] + #[bw(map = |x : &String | x.as_bytes())] + #[br(map = | x: Vec | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())] + #[br(dbg)] + pub property_name: String, + + #[br(args { magic: &property_name})] + //#[br(dbg)] + pub key: Option>, } #[binrw] @@ -79,7 +93,8 @@ pub struct PrimaryAssetIdStruct { #[binrw] #[derive(Debug)] pub struct DAModuleItemDataStruct { - pub unk: [u8; 17], + #[br(pad_before = 17)] + pub module_level: PrimaryAssetNameProperty } #[binrw]