animated = "animated" / Struct(base, "speed" / Float32l) moving = "moving" / Struct( animated, "move_sprite" / Int16ul, "run_sprite" / Int16ul, "turn_speed" / Float32l, "size_class" / Byte, "trailing_unit" / Int16ul, "trailing_options" / Byte, "trailing_spacing" / Float32l, "move_algorithm" / Byte, "turn_radius" / Float32l, "turn_radius_speed" / Float32l, "maximum_yaw_per_second_moving" / Float32l, "stationary_yaw_per_revolution_time" / Float32l, "maximum_yaw_per_second_stationary" / Float32l) action = "action" / Struct( moving, "default_task" / Int16ul, "search_radius" / Float32l, "work_rate" / Float32l, "handicap_work_rate" / If(lambda ctx: find_save_version(ctx) >= 25.06, Float32l), "drop_site" / Int16ul, "backup_drop_site" / Int16ul, "task_by_group" / Byte, "command_sound" / Int16ul, "move_sound" / Int16ul, "run_pattern" / Byte) weapon = "weapon" / Struct("type" / Int16ul, "value" / Int16ul) base_combat = "base_combat" / Struct( action, "base_armour" / IfThenElse(lambda ctx: find_version(ctx) != Version.AOK, Int16ul, Byte), "num_weapons" / Int16ul, "weapons" / Array(this.num_weapons, weapon), "num_armours" / Int16ul, "armours" / Array(this.num_armours, weapon), "defense_terrain_bonus" / Int16ul, "weapon_range_max" / Float32l, "area_effect_range" / Float32l, "attack_speed" / Float32l, "missile_id" / Int16ul, "base_hit_chance" / Int16ul, "break_off_combat" / Byte, "frame_delay" / Int16ul, "weapon_offset" / Struct("x" / Float32l, "y" / Float32l, "z" / Float32l),
"""Lobby.""" from construct import Array, Byte, Bytes, Flag, Int32ul, Padding, Peek, Struct, If, Computed, Embedded, Int32sl from mgz.enums import GameTypeEnum, RevealMapEnum from mgz.util import Version, find_save_version # pylint: disable=invalid-name, bad-continuation # Player inputs in the lobby, and several host settings. lobby = "lobby" / Struct( If(lambda ctx: find_save_version(ctx) >= 13.34, Padding(5)), If(lambda ctx: find_save_version(ctx) >= 20.06, Padding(9)), Array(8, "teams" / Byte), # team number selected by each player If( lambda ctx: ctx._.version not in (Version.DE, Version.HD), Padding(1), ), Peek("reveal_map_id" / Int32ul), RevealMapEnum("reveal_map" / Int32ul), "fog_of_war" / Int32ul, "map_size" / Int32ul, "population_limit_encoded" / Int32ul, "population_limit" / Computed(lambda ctx: ctx.population_limit_encoded * (25 if ctx._.version in [Version.USERPATCH14, Version.USERPATCH15] else 1)), Embedded( If( lambda ctx: ctx._.version != Version.AOK, Struct(Peek("game_type_id" / Byte), GameTypeEnum(
Array( 16, "player_data" / Struct( "active" / Int32ul, "human" / Int32ul, "civilization" / Int32ul, "constant" / Int32ul, # 0x04 0x00 0x00 0x00 )), Padding(5), "elapsed_time" / Float32l, "scenario_filename" / PascalString(lengthfield="scenario_filename_length" / Int16ul), If( lambda ctx: ctx._._.version == Version.DE, Struct(Padding(64), If(lambda ctx: find_save_version(ctx) >= 13.34, Padding(64))))) # Scenarios have intro text, a bitmap, and cinematics. messages = "messages" / Struct( "instruction_id" / Int32ul, "hints_id" / Int32ul, "victory_id" / Int32ul, "defeat_id" / Int32ul, "history_id" / Int32ul, "scouts_id" / If(lambda ctx: ctx._._.version != Version.AOK, Int32ul), "instructions_length" / Int16ul, "instructions" / Bytes(lambda ctx: ctx.instructions_length), "hints" / PascalString(lengthfield="hints_length" / Int16ul), "victory" / PascalString(lengthfield="victory_length" / Int16ul), "defeat" / PascalString(lengthfield="defeat_length" / Int16ul), "history" / PascalString(lengthfield="history_length" / Int16ul),
"color_id"/Int32sl, "selected_color"/Byte, "selected_team_id"/Byte, "resolved_team_id"/Byte, "dat_crc"/Bytes(8), "mp_game_version"/Byte, "civ_id"/Int32ul, "ai_type"/de_string, "ai_civ_name_index"/Byte, "ai_name"/de_string, "name"/de_string, "type"/PlayerTypeEnum(Int32ul), "profile_id"/Int32ul, Const(b"\x00\x00\x00\x00"), "player_number"/Int32sl, "hd_rm_elo"/If(lambda ctx: find_save_version(ctx) < 25.22, Int32ul), "hd_dm_elo"/If(lambda ctx: find_save_version(ctx) < 25.22, Int32ul), "prefer_random"/Flag, "custom_ai"/Flag, If(lambda ctx: find_save_version(ctx) >= 25.06, "handicap"/Bytes(8)), ) de = "de"/Struct( "build"/If(lambda ctx: find_save_version(ctx) >= 25.22, Int32ul), "timestamp"/If(lambda ctx: find_save_version(ctx) >= 26.16, Int32ul), "version"/Float32l, "interval_version"/Int32ul, "game_options_version"/Int32ul, "dlc_count"/Int32ul, "dlc_ids"/Array(lambda ctx: ctx.dlc_count, Int32ul), "dataset_ref"/Int32ul,
If(lambda ctx: find_version(ctx) == Version.DE, Bytes(19)), "has_sprite_list" / Byte, "sprite_list" / If(lambda ctx: ctx.has_sprite_list != 0, RepeatUntil(lambda x, lst, ctx: lst[-1].type == 0, sprite_list)), "de_effect_block" / If( lambda ctx: find_version(ctx) == Version.DE, Struct( Padding(4), "has_effect" / Byte, "effect" / If( lambda ctx: ctx.has_effect == 1, Struct(Padding(3), "length" / Int16ul, "name" / Bytes(lambda ctx: ctx.length), If(lambda ctx: ctx.length > 0, Padding(33)))), If( lambda ctx: ctx.effect is None or (ctx.effect and ctx.effect.length > 0), Byte), Padding(4), If(lambda ctx: find_save_version(ctx) >= 13.15, Padding(5)), If(lambda ctx: find_save_version(ctx) >= 13.17, Bytes(2)), If(lambda ctx: find_save_version(ctx) >= 13.34, Bytes(12))))) animated = "animated" / Struct(Embedded(static), "turn_speed" / Float32l) path_data = "path_data" / Struct( "id" / Int32ul, "linked_path_type" / Int32ul, "waypoint_level" / Int32ul, "path_id" / Int32ul, "waypoint" / Int32ul, "state" / Int32sl, "range" / Float32l, "target_id" / Int32ul, "pause_time" / Float32l, "continue_counter" / Int32ul, "flags" / Int32ul) vector = Struct("x" / Float32l, "y" / Float32l, "z" / Float32l) movement_data = "movement" / Struct("velocity" / vector, "acceleration" / vector)
"trade_enabled"/Flag, "team_bonus_disabled"/Flag, "random_positions"/Flag, "all_techs"/Flag, "num_starting_units"/Byte, "lock_teams"/Flag, "lock_speed"/Flag, "multiplayer"/Flag, "cheats"/Flag, "record_game"/Flag, "animals_enabled"/Flag, "predators_enabled"/Flag, "turbo_enabled"/Flag, "shared_exploration"/Flag, "team_positions"/Flag, If(lambda ctx: find_save_version(ctx) >= 25.06, "unk"/Flag), # maybe handicap If(lambda ctx: find_save_version(ctx) >= 13.34, Bytes(8)), separator, "players"/Array(8, Struct( "dlc_id"/Int32ul, "color_id"/Int32sl, "selected_color"/Byte, "selected_team_id"/Byte, "resolved_team_id"/Byte, "dat_crc"/Bytes(8), "mp_game_version"/Byte, "civ_id"/Int32ul, "ai_type"/de_string, "ai_civ_name_index"/Byte, "ai_name"/de_string, "name"/de_string,
"trade_enabled" / Flag, "team_bonus_disabled" / Flag, "random_positions" / Flag, "all_techs" / Flag, "num_starting_units" / Byte, "lock_teams" / Flag, "lock_speed" / Flag, "multiplayer" / Flag, "cheats" / Flag, "record_game" / Flag, "animals_enabled" / Flag, "predators_enabled" / Flag, "turbo_enabled" / Flag, "shared_exploration" / Flag, "team_positions" / Flag, If(lambda ctx: find_save_version(ctx) >= 13.34, Bytes(8)), separator, "players" / Array( 8, Struct("dlc_id" / Int32ul, "color_id" / Int32sl, "selected_color" / Byte, "selected_team_id" / Byte, "resolved_team_id" / Byte, "dat_crc" / Bytes(8), "mp_game_version" / Byte, "civ_id" / Byte, Const(b"\x00\x00\x00"), "ai_type" / de_string, "ai_civ_name_index" / Byte, "ai_name" / de_string, "name" / de_string, "type" / PlayerTypeEnum(Int32ul), "profile_id" / Int32ul, Const(b"\x00\x00\x00\x00"), "player_number" / Int32sl, "hd_rm_elo" / Int32ul, "hd_dm_elo" / Int32ul, "animated_destruction_enabled" / Flag, "custom_ai" / Flag)), "fog_of_war" / Flag, "cheat_notifications" / Flag, "colored_chat" / Flag,
"p6_tribute_recvd" / Float32l, "p7_tribute_recvd" / Float32l, "p8_tribute_recvd" / Float32l, "current_unit_worth" / Float32l, "current_building_worth" / Float32l, "total_food_gathered" / Float32l, "total_wood_gathered" / Float32l, "total_stone_gathered" / Float32l, "total_gold_gathered" / Float32l, "total_kill_and_raze_worth" / Float32l, "total_tribute_recvd" / Float32l, "total_value_of_razings" / Float32l, "castle_high" / Float32l, "wonder_high" / Float32l, "total_tribute_sent" / Float32l, "convert_min_adj" / Float32l, "convert_max_adj" / Float32l, "convert_resist_min_adj" / Float32l, "convert_resist_max_adj" / Float32l, "convert_building_min" / Float32l, "convert_building_max" / Float32l, "convert_building_chance" / Float32l, "spies" / Float32l, "value_wonders_castles" / Float32l, "food_score" / Float32l, "wood_score" / Float32l, "stone_score" / Float32l, "gold_score" / Float32l, Embedded( If( lambda ctx: find_save_version(ctx) >= 11.76, Struct( "wood_bonus0" / Float32l, "food_bonus0" / Float32l, "relic_rate" / Float32l, "heresy" / Float32l, "theocracy" / Float32l, "crenellations" / Float32l, "construction_rate_mod" / Float32l, "hun_wonder_bonus" / Float32l, "spies_discount" / Float32l, ))), Embedded( If(lambda ctx: find_version(ctx) in (Version.DE, Version.HD), Struct(Array(this._._.num_header_data - 198, Float32l)))), Embedded(
"next_uid"/Int32ul, "constant"/Bytes(4), Array(16, "names"/String(256)), Array(16, "player_ids"/Int32ul), Array(16, "player_data"/Struct( "active"/Int32ul, "human"/Int32ul, "civilization"/Int32ul, "constant"/Int32ul, # 0x04 0x00 0x00 0x00 )), Padding(5), "elapsed_time"/Float32l, "scenario_filename"/PascalString(lengthfield="scenario_filename_length"/Int16ul), If(lambda ctx: ctx._._.version == Version.DE, Struct( Padding(64), If(lambda ctx: find_save_version(ctx) >= 13.34, Padding(64)) )) ) # Scenarios have intro text, a bitmap, and cinematics. messages = "messages"/Struct( "instruction_id"/Int32ul, "hints_id"/Int32ul, "victory_id"/Int32ul, "defeat_id"/Int32ul, "history_id"/Int32ul, "scouts_id"/If(lambda ctx: ctx._._.version != Version.AOK, Int32ul), "instructions_length"/Int16ul, "instructions"/Bytes(lambda ctx: ctx.instructions_length), "hints"/PascalString(lengthfield="hints_length"/Int16ul), "victory"/PascalString(lengthfield="victory_length"/Int16ul),
"sleep"/Flag, "doppleganger"/Flag, "go_to_sleep"/Flag, "object_id"/Int32ul, "facet"/Byte, "x"/Float32l, "y"/Float32l, "z"/Float32l, "screen_offset_x"/Int16ul, "screen_offset_y"/Int16ul, "shadow_offset_x"/Int16ul, "shadow_offset_y"/Int16ul, "selected_group"/If(lambda ctx: find_version(ctx) == Version.AOK, Byte), ResourceEnum("resource_type"/Int16sl), "amount"/Float32l, "worker_count"/IfThenElse(lambda ctx: find_save_version(ctx) == 12.36, Int32ul, Byte), "current_damage"/Byte, "damaged_lately_timer"/Byte, "under_attack"/Byte, "pathing_group_len"/Int32ul, "pathing_group"/Array(lambda ctx: ctx.pathing_group_len, "object_id"/Int32ul), "group_id"/Int32sl, "roo_already_called"/Byte, "de_static_unk1"/If(lambda ctx: find_version(ctx) == Version.DE, Bytes(17)), If(lambda ctx: find_save_version(ctx) >= 26.16, Byte), "de_has_object_props"/If(lambda ctx: find_version(ctx) == Version.DE, Int16ul), "de_object_props"/IfThenElse(lambda ctx: ctx.de_has_object_props == 1, Struct( Bytes(162), Find(b'`\n\x00\x00`\n\x00\x00', 1000), # Skip a large unparseable block that (likely) contains RMS-modified object data Bytes(2) ), Struct(
If(lambda ctx: find_version(ctx) == Version.DE, Bytes(19)), "has_sprite_list" / Byte, "sprite_list" / If(lambda ctx: ctx.has_sprite_list != 0, RepeatUntil(lambda x, lst, ctx: lst[-1].type == 0, sprite_list)), "de_effect_block" / If( lambda ctx: find_version(ctx) == Version.DE, Struct( Padding(4), "has_effect" / Byte, "effect" / If( lambda ctx: ctx.has_effect == 1, Struct(Padding(3), "length" / Int16ul, "name" / Bytes(lambda ctx: ctx.length), If(lambda ctx: ctx.length > 0, Padding(33)))), If( lambda ctx: ctx.effect is None or (ctx.effect and ctx.effect.length > 0), Byte), Padding(4), If(lambda ctx: find_save_version(ctx) >= 13.15, Padding(5)), If(lambda ctx: find_save_version(ctx) >= 13.17, Bytes(2))))) animated = "animated" / Struct(Embedded(static), "turn_speed" / Float32l) path_data = "path_data" / Struct( "id" / Int32ul, "linked_path_type" / Int32ul, "waypoint_level" / Int32ul, "path_id" / Int32ul, "waypoint" / Int32ul, "state" / Int32sl, "range" / Float32l, "target_id" / Int32ul, "pause_time" / Float32l, "continue_counter" / Int32ul, "flags" / Int32ul) vector = Struct("x" / Float32l, "y" / Float32l, "z" / Float32l) movement_data = "movement" / Struct("velocity" / vector, "acceleration" / vector)
"doppleganger" / Flag, "go_to_sleep" / Flag, "object_id" / Int32ul, "facet" / Byte, "x" / Float32l, "y" / Float32l, "z" / Float32l, "screen_offset_x" / Int16ul, "screen_offset_y" / Int16ul, "shadow_offset_x" / Int16ul, "shadow_offset_y" / Int16ul, "selected_group" / If(lambda ctx: find_version(ctx) == Version.AOK, Byte), ResourceEnum("resource_type" / Int16sl), "amount" / Float32l, "worker_count" / IfThenElse(lambda ctx: find_save_version(ctx) == 12.36, Int32ul, Byte), "current_damage" / Byte, "damaged_lately_timer" / Byte, "under_attack" / Byte, "pathing_group_len" / Int32ul, "pathing_group" / Array(lambda ctx: ctx.pathing_group_len, "object_id" / Int32ul), "group_id" / Int32sl, "roo_already_called" / Byte, "de_static_unk1" / If(lambda ctx: find_version(ctx) == Version.DE, Bytes(19)), "has_sprite_list" / Byte, "sprite_list" / If(lambda ctx: ctx.has_sprite_list != 0, RepeatUntil(lambda x, lst, ctx: lst[-1].type == 0, sprite_list)), "hd_extension" / If(