def load_translations(self): # This section of the file seems to map numerical ids to their # corresponding entries in the localization files (see below). # Each mapping has 3 parts: # 1: The id to be mapped # 2: The localization sheet and entry index to map to # 3: A list of ids for the summary tabs the value shows up in # # In some cases it seems that these ids don't map to an entry so # there must be some additional purpose to this section as well. # self.id_map = dict() for mapping in self.parts[1][0]: if isinstance(mapping[2][0], dict): self.id_map[mapping[1][1]] = (mapping[2][0][1], mapping[2][0][2]) # The id mappings for lobby and player properties are stored # separately in the parts[0][5] entry. # # The values for each property are also mapped but the values # don't have their own unique ids so we use a compound key self.lobby_properties = dict() for item in self.parts[0][5]: uid = item[0][1] sheet = item[2][0][1] entry = item[2][0][2] self.id_map[uid] = (sheet, entry) for value in item[1]: sheet = value[1][0][1] entry = value[1][0][2] self.id_map[(uid, value[0])] = (sheet, entry) # Each localization is a pairing of a language id, e.g. enUS # and a list of byte strings that can be decoded into urls for # resources hosted on the battle.net depot servers. # # Sometimes these byte strings are all NULLed out and need to be ignored. for localization in self.parts[0][6][8]: language = localization[0].decode('utf8') files = list() for file_hash in localization[1]: if file_hash[:4].decode('utf8') != '\x00\x00\x00\x00': files.append(utils.DepotFile(file_hash)) self.localization_urls[language] = files # Grab the gateway from the one of the files self.gateway = list( self.localization_urls.values())[0][0].server.lower() # Each of the localization urls points to an XML file with a set of # localization strings and their unique ids. After reading these mappings # into a lang_sheets variable we can use these sheets to make a direct # map from internal id to localize string. # # For now we'll only do this for english localizations. self.lang_sheets = dict() self.translations = dict() for lang, files in self.localization_urls.items(): if lang != self.opt.lang: continue sheets = list() for depot_file in files: sheets.append( self.factory.load_localization(depot_file, **self.opt)) translation = dict() for uid, (sheet, item) in self.id_map.items(): if sheet < len(sheets) and item in sheets[sheet]: translation[uid] = sheets[sheet][item] elif self.opt.debug: msg = "No {0} translation for sheet {1}, item {2}" raise SC2ReaderLocalizationError( msg.format(self.opt.lang, sheet, item)) else: translation[uid] = "Unknown" self.lang_sheets[lang] = sheets self.translations[lang] = translation
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] ]