def __init__(self, header_file, filename=None, **options): super(MapHeader, self).__init__(header_file, filename, **options) self.data = BitPackedDecoder(header_file).read_struct() # Name self.name = self.data[0][1] # Blizzard self.blizzard = (self.data[0][11] == 'BLIZ') # Parse image hash parsed_hash = utils.parse_hash(self.data[0][1]) self.image_hash = parsed_hash['hash'] self.image_url = self.image_url_template.format( parsed_hash['server'], parsed_hash['hash']) # Parse map hash parsed_hash = utils.parse_hash(self.data[0][2]) self.map_hash = parsed_hash['hash'] self.map_url = self.base_url_template.format(parsed_hash['server'], parsed_hash['hash'], parsed_hash['type']) # Parse localization hashes l18n_struct = self.data[0][4][8] for l in l18n_struct: parsed_hash = utils.parse_hash(l[1][0]) self.localization_urls[l[0]] = self.base_url_template.format( parsed_hash['server'], parsed_hash['hash'], parsed_hash['type'])
def __init__(self, info_file, filename=None, **options): super(MapInfo, self).__init__(info_file, filename, **options) self.data = BitPackedDecoder(info_file).read_struct() self.map_name = self.data[0][7] self.language = self.data[0][13] parsed_hash = utils.parse_hash(self.data[0][1]) self.s2mh_hash = parsed_hash['hash'] self.s2mh_url = MapHeader.url_template.format(parsed_hash['server'], self.s2mh_hash)
def read_file(filename): with open(filename, 'rb') as f: ext = os.path.splitext(filename)[1][1:] data = BitPackedDecoder(f.read()).read_struct() if ext == 's2mh': return read_s2mh(data) elif ext == 's2mi': return read_s2mi(data) else: raise Exception(ext) return data
def __call__(self, data, replay): # The replay.message.events file is a single long list containing three # different element types (minimap pings, player messages, and some sort # of network packets); each differentiated by flags. data = BitPackedDecoder(data) pings = list() messages = list() packets = list() frame = 0 while not data.done(): # All the element types share the same time, pid, flags header. frame += data.read_frames() pid = data.read_bits(5) t = data.read_bits(3) flags = data.read_uint8() if flags in (0x83,0x89): # We need some tests for this, probably not right x = data.read_uint32() y = data.read_uint32() pings.append(PingEvent(frame, pid, flags, x, y)) elif flags == 0x80: info = data.read_bytes(4) packets.append(PacketEvent(frame, pid, flags, info)) elif flags & 0x80 == 0: lo_mask = 2**self.TARGET_BITS-1 hi_mask = 0xFF ^ lo_mask target = flags & lo_mask extension = (flags & hi_mask) << 3 length = data.read_uint8() text = data.read_bytes(length + extension) messages.append(ChatEvent(frame, pid, flags, target, text, (flags, lo_mask, hi_mask, length, extension))) return AttributeDict(pings=pings, messages=messages, packets=packets)
def __init__(self, summary_file, filename=None, lang='enUS', **options): super(GameSummary, self).__init__(summary_file, filename, lang=lang, **options) #: A dict of team# -> teams self.team = dict() #: A list of teams self.teams = list() #: Players, a dict of :class`PlayerSummary` from the game self.players = list() self.observers = list() #: Game start and end times self.start_time = None self.end_time = None self.winners = list() self.player = dict() self.settings = dict() self.player_stats = dict() self.player_settings = defaultdict(dict) self.build_orders = defaultdict(list) self.image_urls = list() self.localization_urls = dict() self.lobby_properties = dict() self.lobby_player_properties = dict() self.game_type = str() self.real_type = str() # The first 16 bytes appear to be some sort of compression header buffer = BitPackedDecoder(zlib.decompress(summary_file.read()[16:])) # TODO: Is there a fixed number of entries? # TODO: Maybe the # of parts is recorded somewhere? self.parts = list() while not buffer.done(): self.parts.append(buffer.read_struct()) self.end_time = datetime.utcfromtimestamp(self.parts[0][8]) self.game_speed = LOBBY_PROPERTIES[0xBB8][1][self.parts[0][0] [1].decode('utf8')] self.game_length = utils.Length(seconds=self.parts[0][7]) self.real_length = utils.Length( seconds=int(self.parts[0][7] / GAME_SPEED_FACTOR[self.game_speed])) self.start_time = datetime.utcfromtimestamp(self.parts[0][8] - self.real_length.seconds) self.load_translations() self.load_map_info() self.load_settings() self.load_player_stats() self.load_players() dependencies = [sheet[1] for sheet in self.lang_sheets['enUS']] if 'Swarm (Mod)' in dependencies: self.expansion = 'HotS' elif 'Liberty (Mod)' in dependencies: self.expansion = 'WoL' else: self.expansion = '' self.game_type = self.settings['Teams'].replace(" ", "") self.real_type = utils.get_real_type(self.teams) # The s2gs file also keeps reference to a series of s2mv files # Some of these appear to be encoded bytes and others appear to be # the preview images that authors may bundle with their maps. self.s2mv_urls = [ str(utils.DepotFile(file_hash)) for file_hash in self.parts[0][6][7] ]
def __init__(self, replay_file, filename=None, load_level=4, engine=sc2reader.engine, **options): super(Replay, self).__init__(replay_file, filename, **options) self.datapack = None self.raw_data = dict() # The current load level of the replay self.load_level = None #default values, filled in during file read self.player_names = list() self.other_people = set() self.speed = "" self.type = "" self.game_type = "" self.real_type = "" self.category = "" self.is_ladder = False self.is_private = False self.map = None self.map_hash = "" self.gateway = "" self.events = list() self.events_by_type = defaultdict(list) self.teams, self.team = list(), dict() self.player = utils.PersonDict() self.observer = utils.PersonDict() self.human = utils.PersonDict() self.computer = utils.PersonDict() self.entity = utils.PersonDict() self.players = list() self.observers = list() # Unordered list of Observer self.humans = list() self.computers = list() self.entities = list() self.attributes = defaultdict(dict) self.messages = list() self.recorder = None # Player object self.packets = list() self.objects = {} self.active_units = {} self.game_fps = 16.0 self.tracker_events = list() self.game_events = list() # Bootstrap the readers. self.registered_readers = defaultdict(list) self.register_default_readers() # Bootstrap the datapacks. self.registered_datapacks = list() self.register_default_datapacks() # Unpack the MPQ and read header data if requested # Since the underlying traceback isn't important to most people, don't expose it in python2 anymore if load_level >= 0: self.load_level = 0 try: self.archive = mpyq.MPQArchive(replay_file, listfile=False) except Exception as e: raise exceptions.MPQError("Unable to construct the MPQArchive", e) header_content = self.archive.header['user_data_header']['content'] header_data = BitPackedDecoder(header_content).read_struct() self.versions = list(header_data[1].values()) self.frames = header_data[3] self.build = self.versions[4] self.base_build = self.versions[5] self.release_string = "{0}.{1}.{2}.{3}".format(*self.versions[1:5]) self.game_length = utils.Length(seconds=self.frames / 16) self.length = self.real_length = utils.Length( seconds=int(self.frames / self.game_fps)) # Load basic details if requested if load_level >= 1: self.load_level = 1 for data_file in [ 'replay.initData', 'replay.details', 'replay.attributes.events' ]: self._read_data(data_file, self._get_reader(data_file)) self.load_details() self.datapack = self._get_datapack() # Can only be effective if map data has been loaded if options.get('load_map', False): self.load_map() # Load players if requested if load_level >= 2: self.load_level = 2 for data_file in ['replay.message.events']: self._read_data(data_file, self._get_reader(data_file)) self.load_message_events() self.load_players() # Load tracker events if requested if load_level >= 3: self.load_level = 3 for data_file in ['replay.tracker.events']: self._read_data(data_file, self._get_reader(data_file)) self.load_tracker_events() # Load events if requested if load_level >= 4: self.load_level = 4 for data_file in ['replay.game.events']: self._read_data(data_file, self._get_reader(data_file)) self.load_game_events() # Run this replay through the engine as indicated if engine: engine.run(self)
def __init__(self, summary_file, filename=None, lang='enUS', **options): super(GameSummary, self).__init__(summary_file, filename, lang=lang, **options) #: A dict of team# -> teams self.team = dict() #: A list of teams self.teams = list() #: Players, a dict of :class`PlayerSummary` from the game self.players = list() self.observers = list() #: Game start and end times self.start_time = None self.end_time = None self.winners = list() self.player = dict() self.settings = dict() self.player_stats = dict() self.player_settings = defaultdict(dict) self.build_orders = defaultdict(list) self.image_urls = list() self.localization_urls = dict() self.lobby_properties = dict() self.lobby_player_properties = dict() self.game_type = str() self.real_type = str() # The first 16 bytes appear to be some sort of compression header buffer = BitPackedDecoder(zlib.decompress(summary_file.read()[16:])) # TODO: Is there a fixed number of entries? # TODO: Maybe the # of parts is recorded somewhere? self.parts = list() while not buffer.done(): self.parts.append(buffer.read_struct()) self.end_time = datetime.utcfromtimestamp(self.parts[0][8]) self.game_speed = LOBBY_PROPERTIES[0xBB8][1][self.parts[0][0][1].decode('utf8')] self.game_length = utils.Length(seconds=self.parts[0][7]) self.real_length = utils.Length(seconds=int(self.parts[0][7]/GAME_SPEED_FACTOR[self.game_speed])) self.start_time = datetime.utcfromtimestamp(self.parts[0][8] - self.real_length.seconds) self.load_translations() self.load_map_info() self.load_settings() self.load_player_stats() self.load_players() dependencies = [sheet[1] for sheet in self.lang_sheets['enUS']] if 'Swarm (Mod)' in dependencies: self.expansion = 'HotS' elif 'Liberty (Mod)' in dependencies: self.expansion = 'WoL' else: self.expansion = '' self.game_type = self.settings['Teams'].replace(" ", "") self.real_type = utils.get_real_type(self.teams) # The s2gs file also keeps reference to a series of s2mv files # Some of these appear to be encoded bytes and others appear to be # the preview images that authors may bundle with their maps. self.s2mv_urls = [str(utils.DepotFile(file_hash)) for file_hash in self.parts[0][6][7]]
def __call__(self, data, replay): data = BitPackedDecoder(data) game_events = list() fstamp = 0 debug = replay.opt.debug data_length = data.length read_frames = data.read_frames read_bits = data.read_bits read_uint8 = data.read_uint8 tell = data.tell read_bytes = data.read_bytes byte_align = data.byte_align append = game_events.append event_start = 0 try: while event_start != data_length: fstamp += read_frames() pid = read_bits(5) event_type = read_bits(7) # Check for a lookup if event_type in self.EVENT_DISPATCH: event = self.EVENT_DISPATCH[event_type](data, fstamp, pid, event_type) if debug: event.bytes = data.read_range(event_start, tell()) append(event) # Otherwise maybe it is an unknown chunk elif event_type == 0x26: read_bytes(8) elif event_type == 0x27: read_bytes(4) elif event_type == 0x37: read_bytes(8) elif event_type == 0x38: arr1 = [read_bits(32) for i in range(read_bits(8))] arr2 = [read_bits(32) for i in range(read_bits(8))] elif event_type == 0x3c: read_bytes(2) elif event_type == 0x47: read_bytes(4) elif event_type == 0x48: read_bytes(4) elif event_type == 0x4C: read_bits(4) elif event_type == 0x59: read_bytes(4) elif event_type == 0x00: read_bytes(1) # Otherwise throw a read error else: raise ReadError("Event type {0} unknown at position {1}.".format(hex(event_type),hex(event_start)), event_type, event_start, replay, game_events, data) byte_align() event_start = tell() return game_events except ParseError as e: raise ReadError("Parse error '{0}' unknown at position {1}.".format(e.msg, hex(event_start)), event_type, event_start, replay, game_events, data) except EOFError as e: raise ReadError("EOFError error '{0}' unknown at position {1}.".format(e.msg, hex(event_start)), event_type, event_start, replay, game_events, data)
def __call__(self, data, replay): data = BitPackedDecoder(data) init_data = dict( #58 player_init_data = [dict( #38 name = data.read_aligned_bytes(data.read_uint8()), clan_tag = None, highest_league = None, combined_race_levels = None, random_seed = data.read_uint32(), race_preference = data.read_uint8() if data.read_bool() else None, #38 team_preference = data.read_uint8() if data.read_bool() else None, #39 test_map = data.read_bool(), test_auto = data.read_bool(), examine = data.read_bool(), custom_interface = None, observe = data.read_bits(2), ) for i in range(data.read_bits(5)) ], game_description = dict( # 48 random_value = None, game_cache_name = None, game_options = dict( #40 lock_teams = None, teams_together = None, advanced_shared_control = None, random_races = None, battle_net = None, amm = None, competitive = None, no_victory_or_defeat = None, fog = None, observers = None, user_difficulty = None, client_debug_flags = None, ), game_speed = None, game_type = None, max_users = None, max_observers = None, max_players = None, max_teams = None, max_colors = None, max_races = None, max_controls = None, map_size_x = None, map_size_y = None, map_file_sync_checksum = None, map_file_name = None, map_author_name = None, mod_file_sync_checksum = None, slot_descriptions = [dict( #47 allowed_colors = None, allowed_races = None, allowedDifficulty = None, allowedControls = None, allowed_observe_types = None, allowed_ai_builds = None, ) for i in range(0)], default_difficulty = None, default_AI_build = None, cache_handles = [], is_blizzardMap = None, is_premade_ffa = None, is_coop_mode = None, ), lobby_state = dict( #56 phase = None, max_users = None, max_observers = None, slots = [dict( #54 control = None, user_id = None, team_id = None, colorPref = None, race_pref = None, difficulty = None, ai_build = None, handicap = None, observe = None, working_set_slot_id = None, rewards = [], toon_handle = None, licenses = [], ) for i in range(0)], # 58 random_seed = None, host_user_id = None, is_single_player = None, game_duration = None, default_difficulty = None, default_ai_build = 0, ), ) distance = data.read_range(data.tell(), data.length).find('s2ma') data.read_aligned_bytes(distance) map_data = list() while data.peek(4) == 's2ma': depot_file = DepotFile(data.read_aligned_bytes(40)) init_data['game_description']['cache_handles'].append(depot_file) init_data.setdefault('game_description',dict())['cache_handles'] = map_data return init_data
def __call__(self, data, replay): data = BitPackedDecoder(data) init_data = dict( player_init_data = [dict( name = data.read_aligned_bytes(data.read_uint8()), clan_tag = data.read_aligned_bytes(data.read_uint8()) if data.read_bool() else "", # 36 highest_league = data.read_uint8() if data.read_bool() else None, #20 combined_race_levels = data.read_uint32() if data.read_bool() else None, #37 random_seed = data.read_uint32(), race_preference = data.read_uint8() if data.read_bool() else None, #38 team_preference = data.read_uint8() if data.read_bool() else None, #39 test_map = data.read_bool(), test_auto = data.read_bool(), examine = data.read_bool(), custom_interface = data.read_bool(), observe = data.read_bits(2), ) for i in range(data.read_bits(5)) ], game_description = dict( random_value = data.read_uint32(), # 4 game_cache_name = data.read_aligned_bytes(data.read_bits(10)), # 24 game_options = dict( lock_teams = data.read_bool(), #27 teams_together = data.read_bool(), advanced_shared_control = data.read_bool(), random_races = data.read_bool(), battle_net = data.read_bool(), amm = data.read_bool(), competitive = data.read_bool(), no_victory_or_defeat = data.read_bool(), fog = data.read_bits(2), #19 observers = data.read_bits(2), #19 user_difficulty = data.read_bits(2), #19 client_debug_flags = data.read_uint64(), #15 ), game_speed = data.read_bits(3), game_type = data.read_bits(3), max_users = data.read_bits(5), max_observers = data.read_bits(5), max_players = data.read_bits(5), max_teams = data.read_bits(4)+1, max_colors = data.read_bits(6), max_races = data.read_uint8()+1, max_controls = data.read_uint8()+1, map_size_x = data.read_uint8(), map_size_y = data.read_uint8(), map_file_sync_checksum = data.read_uint32(), map_file_name = data.read_aligned_bytes(data.read_bits(11)), map_author_name = data.read_aligned_bytes(data.read_uint8()), mod_file_sync_checksum = data.read_uint32(), slot_descriptions = [dict( #50 allowed_colors = data.read_bits(data.read_bits(6)), allowed_races = data.read_bits(data.read_uint8()), allowedDifficulty = data.read_bits(data.read_bits(6)), allowedControls = data.read_bits(data.read_uint8()), allowed_observe_types = data.read_bits(data.read_bits(2)), allowed_ai_builds = data.read_bits(data.read_bits(7)), ) for i in range(data.read_bits(5))], default_difficulty = data.read_bits(6), default_AI_build = data.read_bits(7), cache_handles = [ DepotFile(data.read_aligned_bytes(40)) for i in range(data.read_bits(6)) ], is_blizzardMap = data.read_bool(), is_premade_ffa = data.read_bool(), is_coop_mode = data.read_bool(), ), lobby_state = dict( phase = data.read_bits(3), max_users = data.read_bits(5), max_observers = data.read_bits(5), slots = [dict( control = data.read_uint8(), user_id = data.read_bits(4) if data.read_bool() else None, team_id = data.read_bits(4), colorPref = data.read_bits(5) if data.read_bool() else None, race_pref = data.read_uint8() if data.read_bool() else None, difficulty = data.read_bits(6), ai_build = data.read_bits(7), handicap = data.read_bits(7), observe = data.read_bits(2), working_set_slot_id = data.read_uint8() if data.read_bool() else None, rewards = [data.read_uint32() for i in range(data.read_bits(6))], toon_handle = data.read_aligned_bytes(data.read_bits(7)), # 14 licenses = [data.read_uint32() for i in range(data.read_bits(9))], # 56 ) for i in range(data.read_bits(5))], # 58 random_seed = data.read_uint32(), host_user_id = data.read_bits(4) if data.read_bool() else None, # 52 is_single_player = data.read_bool(), # 27 game_duration = data.read_uint32(), # 4 default_difficulty = data.read_bits(6), # 1 default_ai_build = data.read_bits(7), # 0 ), ) return init_data
def __init__(self, replay_file, filename=None, load_level=4, **options): super(Replay, self).__init__(replay_file, filename, **options) self.datapack = None self.raw_data = dict() #default values, filled in during file read self.player_names = list() self.other_people = set() self.speed = "" self.type = "" self.game_type = "" self.real_type = "" self.category = "" self.is_ladder = False self.is_private = False self.map = None self.map_hash = "" self.gateway = "" self.events = list() self.events_by_type = defaultdict(list) self.teams, self.team = list(), dict() self.players, self.player = list(), utils.PersonDict() self.observers = list() #Unordered list of Observer self.people, self.humans = list(), list( ) #Unordered list of Players+Observers self.person = utils.PersonDict() #Maps pid to Player/Observer self.attributes = defaultdict(dict) self.messages = list() self.recorder = None # Player object self.packets = list() self.objects = {} self.active_units = {} self.game_fps = 16.0 # Bootstrap the readers. self.registered_readers = defaultdict(list) self.register_default_readers() # Bootstrap the datapacks. self.registered_datapacks = list() self.register_default_datapacks() # Unpack the MPQ and read header data if requested if load_level >= 0: try: self.archive = mpyq.MPQArchive(replay_file, listfile=False) except Exception as e: trace = sys.exc_info()[2] raise exceptions.MPQError("Unable to construct the MPQArchive", e), None, trace header_content = self.archive.header['user_data_header']['content'] header_data = BitPackedDecoder(header_content).read_struct() self.versions = header_data[1].values() self.frames = header_data[3] self.build = self.versions[4] self.release_string = "{0}.{1}.{2}.{3}".format(*self.versions[1:5]) self.game_length = utils.Length(seconds=self.frames / 16) self.length = self.real_length = utils.Length( seconds=int(self.frames / self.game_fps)) # Load basic details if requested if load_level >= 1: for data_file in [ 'replay.initData', 'replay.details', 'replay.attributes.events' ]: self._read_data(data_file, self._get_reader(data_file)) self.load_details() self.datapack = self._get_datapack() # Can only be effective if map data has been loaded if options.get('load_map', False): self.load_map() # Load players if requested if load_level >= 2: for data_file in ['replay.message.events']: self._read_data(data_file, self._get_reader(data_file)) self.load_messages() self.load_players() # Load events if requested if load_level >= 3: for data_file in ['replay.game.events']: self._read_data(data_file, self._get_reader(data_file)) self.load_events() # Load tracker events if requested if load_level >= 4: for data_file in ['replay.tracker.events']: self._read_data(data_file, self._get_reader(data_file)) self.load_tracker_events() for event in self.events: event.load_context(self)
def __call__(self, data, replay): data = BitPackedDecoder(data) game_events = list() fstamp = 0 debug = replay.opt.debug data_length = data.length read_frames = data.read_frames read_bits = data.read_bits read_uint8 = data.read_uint8 tell = data.tell read_bytes = data.read_bytes byte_align = data.byte_align append = game_events.append event_start = 0 try: while event_start != data_length: fstamp += read_frames() pid = read_bits(5) event_type = read_bits(7) # Check for a lookup if event_type in self.EVENT_DISPATCH: event = self.EVENT_DISPATCH[event_type](data, fstamp, pid, event_type) if debug: event.bytes = data.read_range(event_start, tell()) append(event) # Otherwise maybe it is an unknown chunk elif event_type == 0x26: read_bytes(8) elif event_type == 0x27: read_bytes(4) elif event_type == 0x37: read_bytes(8) elif event_type == 0x38: arr1 = [read_bits(32) for i in range(read_bits(8))] arr2 = [read_bits(32) for i in range(read_bits(8))] elif event_type == 0x3c: read_bytes(2) elif event_type == 0x47: read_bytes(4) elif event_type == 0x48: read_bytes(4) elif event_type == 0x4C: read_bits(4) elif event_type == 0x59: read_bytes(4) elif event_type == 0x00: read_bytes(1) # Otherwise throw a read error else: raise ReadError( "Event type {0} unknown at position {1}.".format( hex(event_type), hex(event_start)), event_type, event_start, replay, game_events, data) byte_align() event_start = tell() return game_events except ParseError as e: raise ReadError( "Parse error '{0}' unknown at position {1}.".format( e.msg, hex(event_start)), event_type, event_start, replay, game_events, data) except EOFError as e: raise ReadError( "EOFError error '{0}' unknown at position {1}.".format( e.msg, hex(event_start)), event_type, event_start, replay, game_events, data)
def __call__(self, data, replay): # The replay.message.events file is a single long list containing three # different element types (minimap pings, player messages, and some sort # of network packets); each differentiated by flags. data = BitPackedDecoder(data) pings = list() messages = list() packets = list() frame = 0 while not data.done(): # All the element types share the same time, pid, flags header. frame += data.read_frames() pid = data.read_bits(5) t = data.read_bits(3) flags = data.read_uint8() if flags in (0x83, 0x89): # We need some tests for this, probably not right x = data.read_uint32() y = data.read_uint32() pings.append(PingEvent(frame, pid, flags, x, y)) elif flags == 0x80: info = data.read_bytes(4) packets.append(PacketEvent(frame, pid, flags, info)) elif flags & 0x80 == 0: lo_mask = 2**self.TARGET_BITS - 1 hi_mask = 0xFF ^ lo_mask target = flags & lo_mask extension = (flags & hi_mask) << 3 length = data.read_uint8() text = data.read_bytes(length + extension) messages.append( ChatEvent(frame, pid, flags, target, text, (flags, lo_mask, hi_mask, length, extension))) return AttributeDict(pings=pings, messages=messages, packets=packets)
def __call__(self, data, replay): # The entire details file is just a serialized data structure # # See: utils.Replaydata.read_struct for a format description # # The general file structure is as follows: # TODO: add the data types for each node in the structure # # List of Players: # Name # BnetData: # unknown1 # unknown2 # subregion_id # bnet_id # actual_race (Terran, Protoss, Zerg) # ColorData: # alpha (0-255) # red (0-255) # green (0-255) # blue (0-255) # Unknown1 # Unknown2 # handicap (0-100) # Team Number - according to András # Result (0,1,2) - (Unknown, Win, Loss), thanks András # Map # Unknown1 # Unknown2 # Unknown3 # file_time - Time file was created/replay was made # utc_adjustment # Unknown4 # Unknown5 # Unknown6 # Unknown7 # Unknown8 # Unknown9 # Unknown10 # details = BitPackedDecoder(data).read_struct() # To make things a little more meaningful in the rest of the code we # step through all the gathered data and map it into namedtuples so that # we don't need to use data[0][0][1][3] to get the battle.net player id. # The field names are documented in the namedtuples section of the # objects file. The ordered_values function extracts the values while # maintaining key order required for proper mapping ordered_values = lambda dict: [v for k, v in sorted(dict.iteritems())] # Because named tuples are read only, we need to build them in pieces # from the bottom up instead of in place from the top down. players = list() for pdata in details[0]: pdata[1] = BnetData(*ordered_values(pdata[1])) pdata[3] = ColorData(*ordered_values(pdata[3])) player = self.PlayerData(*ordered_values(pdata)) players.append(player) details[0] = players details[10] = [DepotFile(bytes) for bytes in details[10]] # As a final touch, label all extracted information using the Details # named tuple from objects.py return self.Details(*ordered_values(details))
def __call__(self, data, replay): data = BitPackedDecoder(data) init_data = dict( player_init_data=[ dict( name=data.read_aligned_bytes(data.read_uint8()), clan_tag=data.read_aligned_bytes(data.read_uint8()) if data.read_bool() else "", # 36 highest_league=data.read_uint8() if data.read_bool() else None, #20 combined_race_levels=data.read_uint32() if data.read_bool() else None, #37 random_seed=data.read_uint32(), race_preference=data.read_uint8() if data.read_bool() else None, #38 team_preference=data.read_uint8() if data.read_bool() else None, #39 test_map=data.read_bool(), test_auto=data.read_bool(), examine=data.read_bool(), custom_interface=data.read_bool(), observe=data.read_bits(2), ) for i in range(data.read_bits(5)) ], game_description=dict( random_value=data.read_uint32(), # 4 game_cache_name=data.read_aligned_bytes( data.read_bits(10)), # 24 game_options=dict( lock_teams=data.read_bool(), #27 teams_together=data.read_bool(), advanced_shared_control=data.read_bool(), random_races=data.read_bool(), battle_net=data.read_bool(), amm=data.read_bool(), competitive=data.read_bool(), no_victory_or_defeat=data.read_bool(), fog=data.read_bits(2), #19 observers=data.read_bits(2), #19 user_difficulty=data.read_bits(2), #19 client_debug_flags=data.read_uint64(), #15 ), game_speed=data.read_bits(3), game_type=data.read_bits(3), max_users=data.read_bits(5), max_observers=data.read_bits(5), max_players=data.read_bits(5), max_teams=data.read_bits(4) + 1, max_colors=data.read_bits(6), max_races=data.read_uint8() + 1, max_controls=data.read_uint8() + 1, map_size_x=data.read_uint8(), map_size_y=data.read_uint8(), map_file_sync_checksum=data.read_uint32(), map_file_name=data.read_aligned_bytes(data.read_bits(11)), map_author_name=data.read_aligned_bytes(data.read_uint8()), mod_file_sync_checksum=data.read_uint32(), slot_descriptions=[ dict( #50 allowed_colors=data.read_bits(data.read_bits(6)), allowed_races=data.read_bits(data.read_uint8()), allowedDifficulty=data.read_bits(data.read_bits(6)), allowedControls=data.read_bits(data.read_uint8()), allowed_observe_types=data.read_bits( data.read_bits(2)), allowed_ai_builds=data.read_bits(data.read_bits(7)), ) for i in range(data.read_bits(5)) ], default_difficulty=data.read_bits(6), default_AI_build=data.read_bits(7), cache_handles=[ DepotFile(data.read_aligned_bytes(40)) for i in range(data.read_bits(6)) ], is_blizzardMap=data.read_bool(), is_premade_ffa=data.read_bool(), is_coop_mode=data.read_bool(), ), lobby_state=dict( phase=data.read_bits(3), max_users=data.read_bits(5), max_observers=data.read_bits(5), slots=[ dict( control=data.read_uint8(), user_id=data.read_bits(4) if data.read_bool() else None, team_id=data.read_bits(4), colorPref=data.read_bits(5) if data.read_bool() else None, race_pref=data.read_uint8() if data.read_bool() else None, difficulty=data.read_bits(6), ai_build=data.read_bits(7), handicap=data.read_bits(7), observe=data.read_bits(2), working_set_slot_id=data.read_uint8() if data.read_bool() else None, rewards=[ data.read_uint32() for i in range(data.read_bits(6)) ], toon_handle=data.read_aligned_bytes( data.read_bits(7)), # 14 licenses=[ data.read_uint32() for i in range(data.read_bits(9)) ], # 56 ) for i in range(data.read_bits(5)) ], # 58 random_seed=data.read_uint32(), host_user_id=data.read_bits(4) if data.read_bool() else None, # 52 is_single_player=data.read_bool(), # 27 game_duration=data.read_uint32(), # 4 default_difficulty=data.read_bits(6), # 1 default_ai_build=data.read_bits(7), # 0 ), ) return init_data
def __call__(self, data, replay): data = BitPackedDecoder(data) init_data = dict( #58 player_init_data=[ dict( #38 name=data.read_aligned_bytes(data.read_uint8()), clan_tag=None, highest_league=None, combined_race_levels=None, random_seed=data.read_uint32(), race_preference=data.read_uint8() if data.read_bool() else None, #38 team_preference=data.read_uint8() if data.read_bool() else None, #39 test_map=data.read_bool(), test_auto=data.read_bool(), examine=data.read_bool(), custom_interface=None, observe=data.read_bits(2), ) for i in range(data.read_bits(5)) ], game_description=dict( # 48 random_value=None, game_cache_name=None, game_options=dict( #40 lock_teams=None, teams_together=None, advanced_shared_control=None, random_races=None, battle_net=None, amm=None, competitive=None, no_victory_or_defeat=None, fog=None, observers=None, user_difficulty=None, client_debug_flags=None, ), game_speed=None, game_type=None, max_users=None, max_observers=None, max_players=None, max_teams=None, max_colors=None, max_races=None, max_controls=None, map_size_x=None, map_size_y=None, map_file_sync_checksum=None, map_file_name=None, map_author_name=None, mod_file_sync_checksum=None, slot_descriptions=[ dict( #47 allowed_colors=None, allowed_races=None, allowedDifficulty=None, allowedControls=None, allowed_observe_types=None, allowed_ai_builds=None, ) for i in range(0) ], default_difficulty=None, default_AI_build=None, cache_handles=[], is_blizzardMap=None, is_premade_ffa=None, is_coop_mode=None, ), lobby_state=dict( #56 phase=None, max_users=None, max_observers=None, slots=[ dict( #54 control=None, user_id=None, team_id=None, colorPref=None, race_pref=None, difficulty=None, ai_build=None, handicap=None, observe=None, working_set_slot_id=None, rewards=[], toon_handle=None, licenses=[], ) for i in range(0) ], # 58 random_seed=None, host_user_id=None, is_single_player=None, game_duration=None, default_difficulty=None, default_ai_build=0, ), ) distance = data.read_range(data.tell(), data.length).find('s2ma') data.read_aligned_bytes(distance) map_data = list() while data.peek(4) == 's2ma': depot_file = DepotFile(data.read_aligned_bytes(40)) init_data['game_description']['cache_handles'].append(depot_file) init_data.setdefault('game_description', dict())['cache_handles'] = map_data return init_data