Skip to content

Commit

Permalink
Fix parsing options and items in rare edge case replays.
Browse files Browse the repository at this point in the history
  • Loading branch information
ryantaylor committed Sep 13, 2024
1 parent cf9e7f5 commit b3c2ac1
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 15 deletions.
16 changes: 14 additions & 2 deletions CoH3Rec.bt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ struct ITEM {
uint32 spacer;
};

struct CPUITEM {
char blob[8];
uint32 spacer;
};

struct PLAYER {
ubyte is_human;
uint32 name_length;
Expand Down Expand Up @@ -110,7 +115,11 @@ struct PLAYER {
uint32 cosmetics_count;
ITEM cosmetic_items[cosmetics_count] <optimize=false>;
} else {
char data[48];
uint32 battlegroup_count;
CPUITEM battlegroup_items[battlegroup_count] <optimize=false>;
uint32 spacer;
uint32 cosmetics_count;
CPUITEM cosmetic_items[cosmetics_count] <optimize=false>;
}
};

Expand Down Expand Up @@ -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] <optimize=false>;
while (ReadUInt() != 0) {
OPTIONCONFIG options;
}
uint32 zero_d;
uint32 zero_e;
uint32 zero_f;
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 9 additions & 6 deletions src/data/chunks/data_data_chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
)),
Expand Down Expand Up @@ -101,8 +101,11 @@ impl DataDataChunk {
}

#[tracable_parser]
fn parse_options_length(input: Span) -> ParserResult<u32> {
fold_many_m_n(2, 2, le_u32, || -> u32 { 1 }, |acc: u32, item| acc * item)(input)
fn parse_options(input: Span) -> ParserResult<Vec<Option>> {
map(
many_till(Option::parse_option, verify(peek(le_u32), |n| *n == 0)),
|(options, _)| options,
)(input)
}

#[tracable_parser]
Expand Down
12 changes: 11 additions & 1 deletion src/data/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@ pub struct Item {

impl Item {
#[tracable_parser]
pub fn parse_item(input: Span) -> ParserResult<Item> {
pub fn parse_player_item(input: Span) -> ParserResult<Item> {
cut(map(
tuple((take(24u32), length_data(le_u32), take(4u32))),
|(_, data, _): (Span, Span, Span)| Item {
_data: data.to_vec(),
},
))(input)
}

#[tracable_parser]
pub fn parse_cpu_item(input: Span) -> ParserResult<Item> {
cut(map(
tuple((take(8u32), take(4u32))),
|(data, _): (Span, Span)| Item {
_data: data.to_vec(),
},
))(input)
}
}
15 changes: 9 additions & 6 deletions src/data/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,21 @@ impl Player {
Ok((input, steam_id))
}

#[tracable_parser]
fn parse_items<'a>(input: Span<'a>, player: &Player) -> IResult<Span<'a>, Vec<Item>> {
fn item_parser_for(player: &Player) -> impl FnMut(Span) -> ParserResult<Item> {
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<Span<'a>, Vec<Item>> {
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);
Expand Down

0 comments on commit b3c2ac1

Please sign in to comment.