def load(data: ScnDataReader) -> List[Effect]: """ Read all effects from here """ data.mark("effects") num_effects = data.uint32() return [Effect.read(data) for _ in range(0, num_effects)]
def load(data: ScnDataReader) -> List[ColorTable]: """ Read in all of the colors """ data.mark(name='colors') num_color_tables = data.uint16(debug='num_color_tables') return [ColorTable.read(data) for _ in range(0, num_color_tables)]
def load(data: ScnDataReader) -> List[Technology]: """ Read all technologies from here """ data.mark('technologies') num_techs = data.int16(debug='num_techs') return [Technology.read(data) for _ in range(0, num_techs)]
def load(data: ScnDataReader) -> List[Civilization]: """ Read all civilizations from here """ data.mark("civilizations") num_civs = data.int16(debug='num_civs') for i in range(0, num_civs): data.uint8(debug='civ_type {}'.format(i)) data.string_fixed(size=20, debug='civ_name {}'.format(i)) civ_num_attributes = data.uint16( debug='civ_num_attributes {}'.format(i)) civ_effect = data.int16(debug='civ_effect {}'.format(i)) # civ_bonus_effect = data.int16(debug='civ_bonus_effect {}'.format(i)) # Might not exist in AOE1 dat. civ_attributes = [ data.float32(debug='civ_attribute {} {}'.format(i, x)) for x in range(0, civ_num_attributes) ] civ_icon_set = data.uint8(debug='civ_icon_set {}'.format(i)) civ_num_units = data.uint16(debug='civ_num_units {}'.format(i)) civ_unit_available = [ data.boolean32(debug='civ_unit_available {} {}'.format(i, x)) for x in range(0, civ_num_units) ] for j in range(0, civ_num_units): if not civ_unit_available[j]: continue read_unit(data) return []
def discard_extra_map_stuff(data: ScnDataReader): data.mark("Extra map stuff") # NB: Values from genie-rs skip way past random map data in AOE1 files. # data.int32(debug='_map_row_offset') # data.float32(debug='_map_min_x') # data.float32(debug='_map_min_y') # data.float32(debug='_map_max_x') # data.float32(debug='_map_max_y') # data.float32(debug='_map_max_x') # data.float32(debug='_map_max_y') # data.uint16(debug='_additional_terrain_count') # data.uint16(debug='_borders_used') # data.uint16(debug='_max_terrain') # data.uint16(debug='_tile_width') # data.uint16(debug='_tile_height') # data.uint16(debug='_tile_half_width') # data.uint16(debug='_tile_half_height') # data.uint16(debug='_elev_height') # data.uint16(debug='_current_row') # data.uint16(debug='_current_column') # data.uint16(debug='_block_begin_row') # data.uint16(debug='_block_end_row') # data.uint16(debug='_block_begin_column') # data.uint16(debug='_block_end_column') # data.int32(debug='_seach_map_pointer') # data.int32(debug='_seach_map_rows_pointer') # data.uint8(debug='_any_frame_change') # data.uint8(debug='_map_visible') # data.uint8(debug='_map_fog_of_war') # data.read(21 + 157 * 4) data.read(68)
def load(data: ScnDataReader): # Terrain data.mark(name='terrain') # num_terrains_override = num_terrains num_terrains_override = 32 for i in range(0, num_terrains_override): data.boolean8(debug='terrain_enabled {}'.format(i)) data.uint8(debug='terrain_random {}'.format(i)) # always 0? data.string_fixed(size=13, debug='terrain_name {}'.format(i)) data.string_fixed(size=13, debug='terrain_texture_name {}'.format( i)) # Maybe read as int and test agains -1? data.int32(debug='terrain_slp_id {}'.format(i)) data.int32(debug='terrain_slp_pointer {}'.format(i)) data.int32(debug='terrain_sound_id {}'.format(i)) data.uint8(debug='terrain_minimap_color_high {}'.format( i)) # These colors are off-by-one compared with AGE. data.uint8(debug='terrain_minimap_color_medium {}'.format(i)) data.uint8(debug='terrain_minimap_color_low {}'.format(i)) data.uint8(debug='terrain_minimap_color_cliff_lt {}'.format(i)) data.uint8(debug='terrain_minimap_color_cliff_rt {}'.format(i)) data.int8(debug='terrain_passable_id {}'.format(i)) data.int8(debug='terrain_impassable_id {}'.format(i)) # Animation data.boolean8(debug='terrain_animation_enabled {}'.format(i)) data.int16(debug='terrain_anim_num_frames {}'.format(i)) data.int16(debug='terrain_anim_num_pause_frames {}'.format(i)) data.float32(debug='terrain_anim_frame_interval {}'.format(i)) data.float32(debug='terrain_anim_replay_delay {}'.format(i)) data.int16(debug='terrain_anim_frame {}'.format(i)) data.int16(debug='terrain_anim_draw_frame {}'.format(i)) data.float32(debug='terrain_anim_animate_last {}'.format(i)) data.boolean8(debug='terrain_anim_frame_changed {}'.format(i)) data.boolean8(debug='terrain_anim_drawn {}'.format(i)) # Elevation sprites for j in range(0, 19): data.uint16(debug="terrain_elevation_sprite_frames {} {}".format( i, j)) # frames data.uint16(debug="terrain_elevation_sprite_facets {} {}".format( i, j)) # facets data.uint16(debug="terrain_elevation_sprite_frame_id {} {}".format( i, j)) # frame_id data.int16(debug="terrain_to_draw {}".format(i)) data.uint16(debug="terrain_rows {}".format(i)) data.uint16(debug="terrain_cols {}".format(i)) # Borders for j in range(0, num_terrains_override): data.uint16(debug="terrain_border {} {}".format(i, j)) # Objects for j in range(0, 30): data.uint16(debug="terrain_object_id {} {}".format(i, j)) for j in range(0, 30): data.int16(debug="terrain_density {} {}".format(i, j)) for j in range(0, 30): data.int8(debug="terrain_placement_flag {} {}".format( i, j)) # 0 for random, 1 for central data.uint16(debug="terrain_object_count {}".format(i)) data.uint16(debug="padding {}".format(i)) return []
def read(data: ScnDataReader): data.mark('random maps') num_random_maps = data.uint32(debug='num_random_maps') random_maps_pointer = data.uint32(debug='random_maps_pointer') # 39845000 in empires_up.dat random_map_meta = [RandomMapMeta.read(data) for _ in range(0, num_random_maps)] # Data from random_map_meta repeats here random_maps = [RandomMap.read(data) for _ in range(0, num_random_maps)] return RandomMapListWrapper( num_random_maps, random_maps_pointer, random_map_meta, random_maps )
def load(data: ScnDataReader) -> List[Sprite]: """ Read all the sprites """ data.mark(name='sprites') num_sprites = data.uint16(debug='num_sprites') sprites_exist = [data.uint32() for _ in range(0, num_sprites)] sprites = [] for sprite_id in range(0, num_sprites): if sprites_exist[sprite_id] == 0: continue sprite = Sprite.read(data, sprite_id) sprites.append(sprite) return sprites
def discard_first_map_stuff(data: ScnDataReader): data.mark(name='map tiles') data.uint32(debug='__vfptr') data.uint32(debug='map_pointer') data.uint32(debug='map_width') data.uint32(debug='map_height') data.uint32(debug='world_width') data.uint32(debug='world_height') # Advanced Genie Editor describes these as "19 x (Width, Height, Delta Y) 1st is flat tile, then 2x8 elevation ties, then 2 1:1 tiles" for i in range(0, 19): data.int16(debug='tile width {}'.format(i)) data.int16(debug='tile height {}'.format(i)) data.int16(debug='tile delta_y {}'.format(i)) data.uint16(debug="padding?")
def read(data: ScnDataReader): # Load header file_version = data.string_fixed(size=4) file_version_float = float(file_version) header_size = data.uint32() data.mark(name='SCN header', limit=header_size) # header size header_version = data.uint32(debug='header_version') if header_version >= 3: # DE scenario a bit different timestamp = data.uint32(debug='Timestamp maybe') string_marker = data.uint16(debug='string_marker') # always 2656 description = data.string16(debug='description') # Data sometimes here sometimes not. Need to check remaining bytes in header size to see if we can read it. if header_size - data.bytes_read_since_mark >= 8: # might not account for all possibilities for these fields has_single_player_victory_condition = data.boolean32() player_count = data.uint32() else: has_single_player_victory_condition = False player_count = -1 scenario_header = ScnHeader(file_version=file_version_float, header_version=header_version, timestamp=timestamp, description=description, has_singleplayer_victory_condition= has_single_player_victory_condition, player_count=player_count) else: timestamp = data.uint32(debug='timestamp') scenario_header = ScnHeader( file_version=file_version_float, header_version=header_version, timestamp=timestamp, description=data.string32(), has_singleplayer_victory_condition=data.boolean32(), player_count=data.uint32()) logging.debug(scenario_header) data.unmark() data.decompress() return scenario_header
def load(data: ScnDataReader) -> List[TerrainTable]: # Read the terrain tables data.mark(name='terrain tables') num_terrain_tables = data.uint16(debug='num_terrain_tables') num_terrains = data.uint16(debug='num_terrains') unknown_values = [] for terrain_table_id in range(0, num_terrain_tables): unknown_value = data.uint32( debug="terrain table {} unknown value".format(terrain_table_id)) unknown_values.append(unknown_value) terrain_tables = [] for terrain_table_id in range(0, num_terrain_tables): passable = [ data.float32("passability table={} terrain={}".format( terrain_table_id, terrain_id)) for terrain_id in range(0, num_terrains) ] terrain_tables.append( TerrainTable(unknown_values[terrain_table_id], passable)) return terrain_tables
def load(file_name: str): if not (file_name.endswith(".slp")): raise Exception("SLP file must end with .slp") with open(file_name, 'rb') as f: all_data = f.read() data = ScnDataReader(all_data) header = SlpHeader.read(data) frames = [] for i in range(0, header.frames): data.mark('frame {}'.format(i)) frame_info = SlpFrameInfo.read(data) # Sub-reader for outline data (identified by offset) outline_region_start = frame_info.outline_table_offset outline_region_end = outline_region_start + frame_info.height * 4 outline_reader = ScnDataReader( all_data[outline_region_start:outline_region_end]) frame_outline = SlpOutline.read(outline_reader, frame_info.height) # Sub-reader for pixel offsets command_offset_region_start = frame_info.command_table_offset command_offset_region_end = command_offset_region_start + frame_info.height * 4 command_offset_reader = ScnDataReader( all_data[command_offset_region_start:command_offset_region_end]) command_offset = SlpCommandOffset.read(command_offset_reader, frame_info.height) # Sub-reader for pixel data (very inefficient..) command_data_reader = ScnDataReader(all_data) command_data_reader.read( command_offset.row[0] ) # read and discard so that we don't need to do maths to find in-file offset rows = [] for y in range(0, frame_info.height): row = SlpRow.read(command_data_reader, frame_outline.row[y]) rows.append(row) frames.append(SlpFrame(frame_info, rows)) return SlpFile(header, frames)
def load(data: ScnDataReader) -> List[TerrainBorder]: """ Read all of the terrain borders """ data.mark(name='terrain borders') return [TerrainBorder.read(data) for _ in range(0, 16)]
def read_de(data: ScnDataReader): version = data.float32(debug='version') for i in range(0, 16): data.uint16(debug='some number here') # 2656 data.string16(debug='player tribe name') if version >= 3.13: # These 16 bytes are not present in some DE scenarios bundled w/ the game, labelled version 3.125. for i in range(0, 16): # Guessing its a string ref, have not checked data.int32(debug="unknown_string_ref for player {}".format(i)) for i in range(0, 16): player_base_props = ScnPlayerBaseProperties.read(data) logging.debug(player_base_props) data.boolean32(debug='conquest maybe') data.float32(debug='probable check field') data.uint8(debug='unknown field') data.uint16(debug='some number here') # 2656 data.string16(debug='scenario_name') data.uint16(debug='some number here') # 2656 data.string16(debug='scenario_instructions') data.uint16(debug='some number here') # 2656 data.string16(debug='history_string') data.uint16(debug='some number here') # 2656 data.string16(debug='victory_string') data.uint16(debug='some number here') # 2656 data.string16(debug='loss_string') data.uint16(debug='some number here') # 2656 data.string16(debug='history_string') data.int32(debug='instructions_string_reference') data.uint16(debug='some_number_here') # 2656 data.string16(debug='instructions_vox') data.int32(debug='hints_string_reference') data.uint16(debug='some_number_here') # 2656 data.string16(debug='hints_vox') data.int32(debug='victory_string_reference') data.uint16(debug='some_number_here') # 2656 data.string16(debug='victory_vox') data.int32(debug='loss_string_reference') data.uint16(debug='some_number_here') # 2656 data.string16(debug='loss_vox') data.int32(debug='history_string_reference') data.uint16(debug='some_number_here') # 2656 data.string16(debug='history_vox') # Not sure if cinematics or per-player personality, AI, city plans etc data.uint16(debug='some_number_here') # 2656 data.string16(debug='unidentified_string 1') # ' <None> ' data.uint16(debug='some_number_here') # 2656 data.string16(debug='unidentified_string 2') # ' <None> ' data.uint16(debug='some_number_here') # 2656 data.string16(debug='unidentified_string 3') # ' <None> ' data.uint16(debug='some_number_here') # 2656 data.string16(debug='unidentified_string 4') # ' <None> ' data.uint32(debug='unidentified number 1') # 0 data.uint32(debug='unidentified number 2') # 0 data.uint32(debug='unidentified number 3') # 0 data.uint16(debug='unidentified number 4') # 1 for i in range(0, 16): data.string16(debug="ai player {}".format(i)) for i in range(0, 16): data.string16(debug="city plan player {}".format(i)) for i in range(0, 16): data.string16(debug="personality player {}".format(i)) for i in range(0, 16): some_length1 = data.uint32(debug='some length maybe') some_length2 = data.uint32(debug='some length maybe') some_length3 = data.uint32(debug='some length maybe') data.string_fixed(some_length1, debug='some string 1') data.string_fixed(some_length2, debug='some string 2') data.string_fixed(some_length3, debug='some string 3') check1 = data.int32(debug='check value 1') if check1 != -99: raise Exception("Check value did not match in scenario data, giving up") if version < 3.13: data.mark('extra data observed in 3.125 version') for i in range(0, 32): data.int32(debug='unknown value 1 {}'.format(i)) # 500 for i in range(0, 32): data.int32(debug='unknown value 2 {}'.format(i)) # 0 check2 = data.int32(debug='check value 2') if check2 != -99: raise Exception("Check value did not match in scenario data, giving up") rge_scen = ScnEngineProperties( version ) logging.debug(rge_scen) return rge_scen
def load(data: ScnDataReader) -> List[Sound]: data.mark(name='sounds') num_sounds = data.uint16(debug='num_sounds') return [Sound.read(data) for _ in range(0, num_sounds)]
def read_base_unit_props(data: ScnDataReader): data.mark("base unit") unit_name_len = data.uint16(debug='unit_name_len') unit_id = data.int16(debug='unit_id') unit_name_string_id = data.int16(debug='unit_name_string_id') unit_creation_string_id = data.int16(debug='unit_creation_string_id') unit_class = data.int16(debug='unit_class') standing_sprite = data.int16( debug='standing_sprite') # no standing sprite #2 here.. ? dying_sprite = data.int16(debug='dying_sprite') undead_sprite = data.int16(debug='undead_sprite') undead_flag = data.int8(debug='undead_flag') hp = data.uint16(debug='hp') los = data.float32(debug='los') garrison_capacity = data.uint8(debug='garrison_capacity') collision_radius = (data.float32(debug='collision_radius1'), data.float32(debug='collision_radius2'), data.float32(debug='collision_radius3')) unit_train_sound = data.int16(debug='train_sound') # unit_damage_sound = data.int16(debug='damage_sound') unit_death_spawn = data.int16( debug='unit_death_spawn') # no unit_damage_sound here ?? unit_sort_number = data.uint8(debug='unit_sort_number') unit_can_be_built_on = data.uint8(debug='unit_can_be_built_on') unit_button_picture = data.int16(debug='unit_button_picture') unit_hide_in_scenario_editor = data.boolean8( debug='unit_hide_in_scenario_editor') unit_portrait_picture = data.int16(debug='unit_portrait_picture') unit_enabled = data.boolean8( debug='unit_enabled') # no unit_disabled here ?? unit_tile_req = (data.int16(debug='unit_tile_req1'), data.int16(debug='unit_tile_req2') ) # "Placement side terrains" unit_center_tile_req = (data.int16(debug='unit_center_tile_req1'), data.int16(debug='unit_center_tile_req1')) unit_construction_radius = (data.float32( debug='unit_construction_radius1'), data.float32( debug='unit_construction_radius2')) unit_elevation_flag = data.boolean8(debug='unit_elevation_flag') unit_fog_flag = data.boolean8(debug='unit_fog_flag') unit_terrain_restriction_id = data.uint16( debug='unit_terrain_restriction_id') # "terrain tables" unit_movement_type = data.uint8(debug='unit_movement_type') unit_attribute_max_amount = data.uint16( debug='unit_attribute_max_amount') # resource capacity unit_attribute_rot = data.float32(debug='unit_attribute_rot') # decay unit_area_effect_level = data.uint8(debug='unit_area_effect_level') unit_combat_level = data.uint8(debug='unit_combat_level') unit_select_level = data.uint8(debug='unit_select_level') unit_map_draw_level = data.uint8(debug='unit_map_draw_level') unit_level = data.uint8(debug='unit_level') unit_multiple_attribute_mod = data.float32( debug='unit_multiple_attribute_mod') unit_map_color = data.uint8(debug='unit_map_color') unit_help_string_id = data.uint32('unit_help_string_id') unit_help_page_id = data.uint32(debug='unit_help_page_id') unit_hotkey_id = data.uint32(debug='unit_hotkey_id') unit_recyclable = data.boolean8(debug='unit_recyclable') unit_track_as_resource = data.boolean8(debug='unit_track_as_resource') unit_create_doppleganger = data.boolean8(debug='unit_create_doppleganger') unit_resource_group = data.uint8( debug='unit_resource_group') # tree/stone/gold etc unit_occlusion_mask = data.uint8( debug='unit_occlusion_mask') # "hill mode" in AGE unit_obstruction_type = data.uint8( debug='unit_obstruction_type' ) # dropped unit_selection_shape, unit_civilization, unit_attribute_piece # # object flags go here in later versions selection_outline_radius = ( data.float32(debug='selection_outline_radius1'), data.float32(debug='selection_outline_radius2'), data.float32(debug='selection_outline_radius3')) for i in range(0, 3): attribute_res = data.int16(debug='attribute_res {}'.format(i)) attribute_amount = data.float32(debug='attribute_amount {}'.format(i)) attribute_store_mode = data.int8( debug='attribute_store_mode {}'.format(i)) num_damage_sprites = data.uint8(debug='num_damage_sprites') for i in range(0, num_damage_sprites): damage_sprite = data.uint16(debug='damage_sprite {}'.format(i)) damage_percent = data.uint8(debug='damage_percent {}'.format(i)) damage_apply_mode = data.uint8(debug='damage_apply_mode {}'.format(i)) damage_unused = data.uint8(debug='damage_unused {}'.format(i)) unit_selected_sound = data.int16(debug='unit_selected_sound') unit_death_sound = data.int16(debug='unit_death_sound') unit_attack_reaction = data.uint8(debug='unit_attack_reaction') unit_convert_terrain_flag = data.uint8(debug='unit_convert_terrain_flag') unit_name = data.string_fixed(size=unit_name_len, debug='unit_name') data.uint16(debug='unit_copy_id') # dropped unit_copy_id, unit_group return unit_name