diff --git a/CoH3Rec.bt b/CoH3Rec.bt index d00e459..fb8b35e 100644 --- a/CoH3Rec.bt +++ b/CoH3Rec.bt @@ -75,6 +75,11 @@ struct ITEM { uint32 spacer; }; +struct CPUITEM { + char blob[8]; + uint32 spacer; +}; + struct PLAYER { ubyte is_human; uint32 name_length; @@ -110,7 +115,11 @@ struct PLAYER { uint32 cosmetics_count; ITEM cosmetic_items[cosmetics_count] ; } else { - char data[48]; + uint32 battlegroup_count; + CPUITEM battlegroup_items[battlegroup_count] ; + uint32 spacer; + uint32 cosmetics_count; + CPUITEM cosmetic_items[cosmetics_count] ; } }; @@ -141,7 +150,9 @@ struct DATADATACHUNK { uint32 one_or_zero; uint32 option_group_count; uint32 options_per_group; - OPTIONCONFIG options[option_group_count * options_per_group] ; + while (ReadUInt() != 0) { + OPTIONCONFIG options; + } uint32 zero_d; uint32 zero_e; uint32 zero_f; @@ -191,6 +202,7 @@ struct DATASDSCCHUNK { CHUNKHEADER header; local uint32 start = FTell(); char data[121]; + if (header.version > 3026) char more_data[8]; uint32 map_file_length; char map_file[map_file_length]; uint32 map_name_length; diff --git a/src/data/chunks/data_data_chunk.rs b/src/data/chunks/data_data_chunk.rs index 76cfc57..7088106 100644 --- a/src/data/chunks/data_data_chunk.rs +++ b/src/data/chunks/data_data_chunk.rs @@ -4,8 +4,8 @@ use crate::data::{ParserResult, Player, Span}; use byteorder::{LittleEndian, ReadBytesExt}; use nom::bytes::complete::{tag, take, take_while}; use nom::character::{is_digit, is_hex_digit}; -use nom::combinator::{cut, map, map_parser}; -use nom::multi::{fold_many_m_n, length_count, length_data, length_value}; +use nom::combinator::{cut, map, map_parser, peek, verify}; +use nom::multi::{length_count, length_data, length_value, many_till}; use nom::number::complete::{le_u32, le_u64}; use nom::sequence::{separated_pair, tuple}; use nom_tracable::tracable_parser; @@ -59,8 +59,8 @@ impl DataDataChunk { length_data(le_u32), Self::parse_skirmish_flag, le_u64, - take(16u32), - length_count(Self::parse_options_length, Option::parse_option), + take(24u32), + Self::parse_options, take(12u32), Self::parse_mod_info, )), @@ -101,8 +101,11 @@ impl DataDataChunk { } #[tracable_parser] - fn parse_options_length(input: Span) -> ParserResult { - fold_many_m_n(2, 2, le_u32, || -> u32 { 1 }, |acc: u32, item| acc * item)(input) + fn parse_options(input: Span) -> ParserResult> { + map( + many_till(Option::parse_option, verify(peek(le_u32), |n| *n == 0)), + |(options, _)| options, + )(input) } #[tracable_parser] diff --git a/src/data/item.rs b/src/data/item.rs index 602558e..cd6eddf 100644 --- a/src/data/item.rs +++ b/src/data/item.rs @@ -13,7 +13,7 @@ pub struct Item { impl Item { #[tracable_parser] - pub fn parse_item(input: Span) -> ParserResult { + pub fn parse_player_item(input: Span) -> ParserResult { cut(map( tuple((take(24u32), length_data(le_u32), take(4u32))), |(_, data, _): (Span, Span, Span)| Item { @@ -21,4 +21,14 @@ impl Item { }, ))(input) } + + #[tracable_parser] + pub fn parse_cpu_item(input: Span) -> ParserResult { + cut(map( + tuple((take(8u32), take(4u32))), + |(data, _): (Span, Span)| Item { + _data: data.to_vec(), + }, + ))(input) + } } diff --git a/src/data/player.rs b/src/data/player.rs index 7568bab..a0160bc 100644 --- a/src/data/player.rs +++ b/src/data/player.rs @@ -90,18 +90,21 @@ impl Player { Ok((input, steam_id)) } - #[tracable_parser] - fn parse_items<'a>(input: Span<'a>, player: &Player) -> IResult, Vec> { + fn item_parser_for(player: &Player) -> impl FnMut(Span) -> ParserResult { if player.human == 0 { - let (input, _) = take(48u32)(input)?; - return Ok((input, vec![])); + Item::parse_cpu_item + } else { + Item::parse_player_item } + } + #[tracable_parser] + fn parse_items<'a>(input: Span<'a>, player: &Player) -> IResult, Vec> { cut(map( tuple(( - length_count(le_u32, Item::parse_item), + length_count(le_u32, Self::item_parser_for(player)), take(4u32), - length_count(le_u32, Item::parse_item), + length_count(le_u32, Self::item_parser_for(player)), )), |(mut battlegroup_items, _, mut cosmetic_items)| { battlegroup_items.append(&mut cosmetic_items);