def _get_options(self, cls, **new_options): options = utils.AttributeDict() for opt_cls, cls_options in self.options.items(): if issubclass(cls, opt_cls): options.update(cls_options) options.update(new_options) return options
def __init__(self, file_object, filename=None, **options): self.opt = utils.AttributeDict(options) self.filename = filename or getattr(file_object, 'name', 'Unavailable') file_object.seek(0) self.filehash = hashlib.sha256(file_object.read()).hexdigest() file_object.seek(0)
def __init__(self, file_object, filename=None, factory=None, **options): self.factory = factory self.opt = utils.AttributeDict(options) self.logger = log_utils.get_logger(self.__class__) self.filename = filename or getattr(file_object, 'name', 'Unavailable') if hasattr(file_object, 'seek'): file_object.seek(0) self.filehash = hashlib.sha256(file_object.read()).hexdigest() file_object.seek(0)
def make_replay(self, replay_file, **options): options = utils.AttributeDict(options) # The Replay constructor scans the header of the replay file for # the build number and stores the options for later use. The # options are copied so subsequent option changes are isolated. replay_file.seek(0) replay = objects.Replay(replay_file, **options.copy()) # print "Versions: ", replay.versions # .SC2Replay files are written in Blizzard's MPQ Archive format. # The format stores a header which contains a block table that # specifies the location of each encrypted file. # # Unfortunately, some replay sites modify the replay contents to # add messages promoting their sites without updating the header # correctly. The listfile option(hack) lets us bypass this issue # by specifying the files we want instead of generating a list. # # In order to wrap mpyq exceptions we have to do this try hack. try: replay_file.seek(0) archive = mpyq.MPQArchive(replay_file, listfile=False) except KeyboardInterrupt: raise except: raise exceptions.MPQError("Unable to construct the MPQArchive") # These files are configured for either full or partial parsing for file in options.files: # To wrap mpyq exceptions we have to do this try hack. try: # Handle the tampering with the message.events file that some sites do if file == 'replay.message.events': try: filedata = archive.read_file(file, force_decompress=True) except IndexError as e: # Catch decompression errors if str(e) == "string index out of range": filedata = archive.read_file( file, force_decompress=False) else: raise else: filedata = archive.read_file(file) except KeyboardInterrupt: raise except: raise exceptions.MPQError( "Unable to extract file: {0}".format(file)) # For each file, we build a smart buffer object from the # utf-8 encoded bitstream that mpyq extracts. buffer = utils.ReplayBuffer(filedata) # Each version of Starcraft slightly modifies some portions # of the format for some files. To work with this, the # config file has a nested lookup structure of # [build][file]=>reader which returns the appropriate reader # # TODO: Different versions also have different data mappings # sc2reader doesn't yet handle this difficulty. # # Readers use the type agnostic __call__ interface so that # they can be implemented as functions or classes as needed. # # Readers return the extracted information from the buffer # object which gets stored into the raw data dict for later # use in post processing because correct interpretation of # the information often requires data from other files. reader = config.readers[replay.build][file] reference_name = '_'.join(file.split('.')[1:]) replay.raw[reference_name] = reader(buffer, replay) # Now that the replay has been loaded with the "raw" data from # the archive files we run the system level post processors to # organize the data into a cross referenced data structure. # # After system level processors have run, call each of the post # processors provided by the user. This would be a good place to # convert the object to a serialized json string for cross # language processes or add custom attributes. # # TODO: Maybe we should switch this to a hook based architecture # Needs to be able to load "contrib" type processors.. for process in [processors.Full] + options.processors: replay = process(replay) return replay