Example #1
0
 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
Example #2
0
    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)
Example #3
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)
Example #4
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