Ejemplo n.º 1
0
    def parse(self, **kwargs):
        '''
        Parses this WhileBlock in the way specified by the keyword arguments.

        If rawdata or a filepath is supplied, it will be used to parse
        this WhileBlock. If not, and initdata is supplied, it will be
        used to replace the entries in this WhileBlock.

        If rawdata, initdata, filepath, and init_attrs are all unsupplied,
        all entries in this array will be deleted and replaced with new ones.

        If rawdata, initdata, and filepath are all unsupplied or
        None and init_attrs is False, this method will do nothing.

        If this WhileBlock also has a STEPTREE attribute, it will be
        initialized in the same way as the array elements.

        If attr_index is supplied, the initialization will only be
        done to only the specified attribute or array element.

        Raises AssertionError if attr_index is None and initdata
        does not have __iter__ or __len__ methods.
        Raises TypeError if rawdata and filepath are both supplied.
        Raises TypeError if rawdata doesnt have read, seek, and peek methods.

        Optional keywords arguments:
        # bool:
        init_attrs --- Whether or not to clear the contents of the WhileBlock.
                       Defaults to True. If True, and 'rawdata' and 'filepath'
                       are None, all the cleared array elements will be rebuilt
                       using the desciptor in this Blocks SUB_STRUCT entry.

        # buffer:
        rawdata ------ A peekable buffer that will be used for parsing
                       elements of this WhileBlock. Defaults to None.
                       If supplied, do not supply 'filepath'.

        # int:
        root_offset -- The root offset that all rawdata reading is done from.
                       Pointers and other offsets are relative to this value.
                       Passed to the parser of each elements FieldType when
                       they are rebuilt using the given filepath or rawdata.
        offset ------- The initial offset that rawdata reading is done from.
                       Passed to the parser of each elements FieldType when
                       they are rebuilt using the given filepath or rawdata.

        # int/str:
        attr_index --- The specific attribute index to initialize. Operates on
                       all indices if unsupplied or None. Defaults to None.

        # object:
        initdata ----- An iterable of objects to replace the contents of
                       this WhileBlock if attr_index is None.
                       If attr_index is not None, this is instead an object
                       to replace self[attr_index]

        #str:
        filepath ----- An absolute path to a file to use as rawdata to parse
                       this WhileBlock. If supplied, do not supply 'rawdata'.
        '''
        attr_index = kwargs.pop('attr_index', None)
        initdata = kwargs.pop('initdata', None)
        desc = object.__getattribute__(self, "desc")

        # if initializing the array elements, record the
        # length of this Block before it gets cleared.
        if initdata is None:
            init_len = len(self)
        else:
            init_len = len(initdata)

        writable = kwargs.pop('writable', False)
        with get_rawdata_context(writable=writable, **kwargs) as rawdata:
            if attr_index is not None:
                # parsing/initializing just one attribute
                if isinstance(attr_index, str) and attr_index not in desc:
                    attr_index = desc['NAME_MAP'][attr_index]

                attr_desc = desc['SUB_STRUCT']
                if isinstance(initdata, Block):
                    self[attr_index].parse(initdata=initdata)
                elif initdata is not None:
                    # non-Block initdata was provided for this
                    # attribute, so just place it in the WhileBlock.
                    self[attr_index] = initdata
                elif rawdata or kwargs.get('init_attrs', False):
                    # we are either parsing the attribute from rawdata or nothing
                    kwargs.update(desc=attr_desc, parent=self,
                                  rawdata=rawdata, attr_index=attr_index)
                    kwargs.pop('filepath', None)
                    attr_desc['TYPE'].parser(**kwargs)

                return

            if kwargs.get('init_attrs', True) or initdata is not None:
                # parsing/initializing all array elements, so clear the Block
                list.__delitem__(self, slice(None, None, None))

            # if an initdata was provided, make sure it can be used
            assert (initdata is None or
                    (hasattr(initdata, '__iter__') and
                     hasattr(initdata, '__len__'))), (
                         "If provided, initdata must be an iterable with a length")

            if rawdata is not None:
                # parse the structure from raw data
                try:
                    # we are either parsing the attribute from rawdata or nothing
                    kwargs.update(desc=desc, node=self, rawdata=rawdata)
                    kwargs.pop('filepath', None)
                    desc['TYPE'].parser(**kwargs)
                except Exception as e:
                    e.args += (
                        "Error occurred while attempting to parse %s." %
                        type(self),
                        )
                    raise
                return
            
        if not(kwargs.get('init_attrs', True) or initdata is not None):
            return

        # this ListBlock is an array, so the FieldType
        # of each element should be the same
        try:
            attr_desc = desc['SUB_STRUCT']
            attr_f_type = attr_desc['TYPE']
        except Exception:
            raise TypeError("Could not locate the sub-struct descriptor." +
                            "\nCould not initialize array")

        list.extend(self, [None]*init_len)

        if kwargs.get('init_attrs', True) or issubclass(attr_f_type.node_cls, Block):
            # loop through each element in the array and initialize it
            for i in range(init_len):
                attr_f_type.parser(attr_desc, parent=self, attr_index=i)

        # only initialize the STEPTREE if this Block has a STEPTREE
        s_desc = desc.get('STEPTREE')
        if s_desc:
            s_desc['TYPE'].parser(s_desc, parent=self, attr_index='STEPTREE')

        if initdata is None:
            return

        # parse the initialized attributes with the initdata
        if isinstance(initdata, Block):
            for i in range(init_len):
                self.parse(attr_index=i, initdata=initdata[i])

            # if the initdata has a STEPTREE node, copy it to
            # this Block if this Block can hold a STEPTREE.
            if hasattr(self, "STEPTREE") and hasattr(initdata, "STEPTREE"):
                self.parse(attr_index="STEPTREE", initdata=initdata.STEPTREE)
        else:
            for i in range(len(initdata)):
                self[i] = initdata[i]
Ejemplo n.º 2
0
    def parse(self, **kwargs):
        '''
        Parses this UnionBlock in the way specified by the keyword arguments.

        If initdata is supplied, it will be used to replace the contents
        of this UnionBlocks bytearray. If not, and rawdata or a filepath
        is supplied, it will be used to parse this UnionBlock.

        If rawdata, initdata, filepath, and init_attrs are all unsupplied,
        the contents of this UnionBlocks bytearray will be replaced with
        the DEFAULT value in the descriptor. If one doesnt exist, the
        contents will be replaced with    b'\x00'*desc['SIZE']

        If rawdata, initdata, and filepath are all unsupplied or None and
        init_attrs is False, this method will do nothing.

        Raises TypeError if rawdata and filepath are both supplied.
        Raises TypeError if rawdata doesnt have read, seek, and peek methods.

        Optional keywords arguments:
        # bool:
        init_attrs --- Whether or not to replace the contents of this
                       UnionBlocks bytearray with the DEFAULT descriptor value,
                       or with b'\x00'*desc['SIZE'] if DEFAULT doesnt exist.
                       Changes the active state to None.

        # buffer:
        rawdata ------ A peekable buffer that will be used for parsing
                       this UnionBlock. Defaults to None.
                       If supplied, do not supply 'filepath'.

        # int:
        root_offset -- The root offset that all rawdata reading is done from.
                       Pointers and other offsets are relative to this value.
                       Passed to the parser of this UnionBlocks FieldType.
        offset ------- The initial offset that rawdata reading is done from.
                       Passed to the parser of this UnionBlocks FieldType.

        # iterable:
        initdata ----- An iterable capable of being assigned to a bytearray
                       using the slice notation    self[:] = initdata

        #str:
        filepath ----- An absolute path to a file to use as rawdata to parse
                       this UnionBlock. If supplied, do not supply 'rawdata'.
        '''
        initdata = kwargs.pop('initdata', None)

        if initdata is not None:
            self[:] = initdata
            return  # return early

        desc = object.__getattribute__(self, "desc")
        writable = kwargs.pop('writable', False)
        with get_rawdata_context(writable=writable, **kwargs) as rawdata:
            if rawdata is not None:
                # parse the block from rawdata
                try:
                    kwargs.update(parent=self.parent,
                                  desc=desc,
                                  node=self,
                                  rawdata=rawdata)
                    kwargs.pop('filepath', None)
                    desc['TYPE'].parser(**kwargs)
                    return  # return early
                except Exception as e:
                    e.args += ("Error occurred while attempting to parse %s." %
                               type(self), )
                    raise
            elif kwargs.get('init_attrs', True):
                # initialize the UnionBlock's bytearray data
                self[:] = desc.get('DEFAULT', b'\x00' * desc['SIZE'])
Ejemplo n.º 3
0
    def serialize(self, **kwargs):
        '''
        Attempts to serialize the tag to it's current
        filepath, but while appending ".temp" to the end. if it
        successfully saved then it will attempt to either backup or
        delete the old tag and remove .temp from the resaved one.
        '''
        data = self.data
        filepath = kwargs.pop('filepath', self.filepath)
        if filepath is not None:
            filepath = Path(filepath)

        buffer = kwargs.pop('buffer', None)
        if buffer is not None:
            return data.serialize(buffer=buffer, **kwargs)

        temp = kwargs.pop('temp', True)
        backup = kwargs.pop('backup', True)
        replace_backup = kwargs.pop('replace_backup', False)

        calc_pointers = bool(kwargs.pop('calc_pointers', self.calc_pointers))

        # If the definition doesnt exist then dont test after writing
        try:
            int_test = self.definition.build
        except AttributeError:
            int_test = False

        if 'int_test' in kwargs:
            int_test = bool(kwargs.pop('int_test'))
        elif 'integrity_test' in kwargs:
            int_test = bool(kwargs.pop('integrity_test'))

        if filepath.is_dir():
            raise IOError('filepath must be a path to a file, not a folder.')

        # If the path doesnt exist, create it
        filepath.parent.mkdir(exist_ok=True, parents=True)

        temppath = str(filepath) + ".temp"
        backuppath = kwargs.pop("backuppath", str(filepath) + ".backup")
        if not backup:
            backuppath = None

        # open the file to be written and start writing!
        with get_rawdata_context(filepath=temppath, writable=True) as tagfile:
            if hasattr(tagfile, "truncate"):
                tagfile.truncate(0)

            # if we need to calculate any pointers, do so
            if calc_pointers:
                self.set_pointers(kwargs.get('offset', 0))

            # if this is an incomplete object we need to copy the
            # original file to the path of the new file in order to
            # fill in the data we don't yet understand/have mapped out
            if self.definition.incomplete:
                if not Path(self.sourcepath).is_file():
                    raise IOError("Tag is incomplete and the source " +
                                  "file to fill in the remaining " +
                                  "data cannot be found.")

                if self.sourcepath != temppath:
                    shutil.copyfileobj(open(self.sourcepath, 'r+b'),
                                       tagfile, 2*(1024**2))  # 2MB buffer
            elif self.zero_fill:
                # make a file as large as the tag is calculated to fill
                try:
                    datasize = data.binsize
                    tagfile.seek(0, 2)
                    if tagfile.tell() < datasize:
                        tagfile.seek(datasize - 1)
                        tagfile.write(b'\x00')
                except BinsizeError:
                    pass

            kwargs.update(writebuffer=tagfile)
            data.TYPE.serializer(data, **kwargs)

        # if the definition is accessible, we can quick load
        # the tag that was just written to check its integrity
        if int_test:
            try:
                self.definition.build(int_test=True, filepath=temppath)
            except Exception:
                raise IntegrityError(
                    "Serialized Tag failed its data integrity test:\n" +
                    ' '*BPI + str(self.filepath) + '\nTag may be corrupted.')

        if not temp:
            # If we are doing a full save then we try and rename the temp file
            backup_and_rename_temp(filepath, temppath, backuppath,
                                   replace_backup)

        return filepath
Ejemplo n.º 4
0
    def parse(self, **kwargs):
        '''
        Parses this ListBlock in the way specified by the keyword arguments.

        If rawdata or a filepath is supplied, it will be used to parse
        this ListBlock(or the specified entry if attr_index is not None).

        If initdata is supplied and not rawdata nor a filepath, it will be
        used to replace the entries in this ListBlock if attr_index is None.
        If attr_index is instead not None, self[attr_index] will be replaced
        with initdata.

        If rawdata, initdata, filepath, and init_attrs are all unsupplied,
        all entries in this list will be initialized with a default value by
        calling the parser function of each entries 'TYPE' descriptor entry.

        If rawdata, initdata, and filepath are all unsupplied or None
        and init_attrs is False, this method will do nothing more than
        replace all index entries with None.

        Raises AssertionError if attr_index is None and initdata
        does not have __iter__ or __len__ methods.
        Raises TypeError if rawdata and filepath are both supplied.
        Raises TypeError if rawdata doesnt have read, seek, and peek methods.

        Optional keywords arguments:
        # bool:
        init_attrs --- Whether or not to clear the contents of the ListBlock.
                       Defaults to True. If True, and 'rawdata' and 'filepath'
                       are None, all the cleared array elements will be rebuilt
                       using their matching descriptors in self.desc

        # buffer:
        rawdata ------ A peekable buffer that will be used for parsing
                       elements of this ListBlock. Defaults to None.
                       If supplied, do not supply 'filepath'.

        # int:
        root_offset -- The root offset that all rawdata reading is done from.
                       Pointers and other offsets are relative to this value.
                       Passed to the parser of this ListBlocks FieldType.
        offset ------- The initial offset that rawdata reading is done from.
                       Passed to the parser of this ListBlocks FieldType.

        # int/str:
        attr_index --- The specific attribute index to initialize. Operates on
                       all indices if unsupplied or None. Defaults to None.

        # object:
        initdata ----- An iterable of objects to replace the contents of
                       this ListBlock if attr_index is None.
                       If attr_index is not None, this is instead an object
                       to replace self[attr_index]

        #str:
        filepath ----- An absolute path to a file to use as rawdata to parse
                       this ListBlock. If supplied, do not supply 'rawdata'.
        '''
        attr_index = kwargs.pop('attr_index', None)
        initdata = kwargs.pop('initdata', None)
        desc = object.__getattribute__(self, "desc")

        writable = kwargs.pop('writable', False)
        with get_rawdata_context(writable=writable, **kwargs) as rawdata:
            if attr_index is not None:
                # parsing/initializing just one attribute
                if isinstance(attr_index, str) and attr_index not in desc:
                    attr_index = desc['NAME_MAP'][attr_index]

                attr_desc = desc[attr_index]
                if initdata is None:
                    # we are either parsing the attribute from rawdata or nothing
                    kwargs.update(desc=attr_desc,
                                  parent=self,
                                  rawdata=rawdata,
                                  attr_index=attr_index)
                    kwargs.pop('filepath', None)
                    attr_desc['TYPE'].parser(**kwargs)
                elif isinstance(self[attr_index], Block):
                    self[attr_index].parse(initdata=initdata)
                else:
                    # non-Block initdata was provided for this
                    # attribute, so just place it in the ListBlock.
                    self[attr_index] = initdata

                return

            if kwargs.get("clear", True):
                # parsing/initializing all attributes, so clear the block
                # and create as many elements as it needs to hold
                list.__init__(self, [None] * desc['ENTRIES'])

            if rawdata is not None:
                # parse the ListBlock from raw data
                try:
                    # we are either parsing the attribute from rawdata or nothing
                    kwargs.update(desc=desc, node=self, rawdata=rawdata)
                    kwargs.pop('filepath', None)
                    desc['TYPE'].parser(**kwargs)
                except Exception as e:
                    a = e.args[:-1]
                    e_str = "\n"
                    try:
                        e_str = e.args[-1] + e_str
                    except IndexError:
                        pass
                    e.args = a + (e_str + "Error occurred while " +
                                  "attempting to parse %s." % type(self), )
                    raise e
            elif kwargs.get('init_attrs', True):
                # initialize the attributes
                for i in range(len(self)):
                    desc[i]['TYPE'].parser(desc[i], parent=self, attr_index=i)

                # Only initialize the STEPTREE if the block has a STEPTREE
                s_desc = desc.get('STEPTREE')
                if s_desc:
                    s_desc['TYPE'].parser(s_desc,
                                          parent=self,
                                          attr_index='STEPTREE')

        if initdata is None:
            return

        # if an initdata was provided, make sure it can be used
        assert (hasattr(initdata, '__iter__')
                and hasattr(initdata, '__len__')), (
                    "If provided, initdata must be an iterable with a length")

        if isinstance(initdata, Block):
            # copy the attributes from initdata into self
            # by name for each of the attributes, but do
            # this only if the name exists in both blocks
            init_name_map = initdata.desc.get(NAME_MAP, ())
            name_map = desc.get(NAME_MAP, ())

            for name in init_name_map:
                if name in name_map:
                    self.parse(attr_index=name_map[name],
                               initdata=initdata[init_name_map[name]])

            # if the initdata has a STEPTREE node, copy it to
            # this Block if this Block can hold a STEPTREE.
            if hasattr(self, "STEPTREE") and hasattr(initdata, "STEPTREE"):
                self.parse(attr_index="STEPTREE", initdata=initdata.STEPTREE)
        else:
            # loop over the ListBlock and copy the entries
            # from initdata into the ListBlock. Make sure to
            # loop as many times as the shortest length of the
            # two so as to prevent IndexErrors.'''
            for i in range(min(len(self), len(initdata))):
                self[i] = initdata[i]
Ejemplo n.º 5
0
    def import_node(self):
        '''Prompts the user for an exported node file.
        Imports data into the node from the file.'''
        if None in (self.parent, self.node):
            return

        try:
            initialdir = self.tag_window.app_root.last_load_dir
        except AttributeError:
            initialdir = None

        ext = self.field_ext

        filepath = askopenfilename(initialdir=initialdir,
                                   defaultextension=ext,
                                   filetypes=[(self.name, "*" + ext),
                                              ('All', '*')],
                                   title="Import '%s' from..." % self.name,
                                   parent=self)

        if not filepath:
            return

        curr_size = None
        index = self.attr_index

        try:
            undo_node = self.node
            curr_size = self.parent.get_size(attr_index=index)

            with get_rawdata_context(writable=False,
                                     filepath=filepath) as rawdata:
                try:
                    self.parent.set_size(len(rawdata), attr_index=index)
                except Exception:
                    # sometimes rawdata has an explicit size, so an exception
                    # will be raised when trying to change it. just ignore it
                    pass

                self.parent.parse(rawdata=rawdata, attr_index=index)
                self.node = self.parent[index]
                self.set_edited()

                self.edit_create(undo_node=undo_node, redo_node=self.node)

                # until i come up with a better method, i'll have to rely on
                # reloading the root field widget so stuff(sizes) will be updated
                try:
                    root = self.f_widget_parent
                    while hasattr(root, 'f_widget_parent'):
                        if root.f_widget_parent is None:
                            break
                        root = root.f_widget_parent

                    root.reload()
                except Exception:
                    print(format_exc())
                    print("Could not reload after importing '%s' node." %
                          self.name)
        except Exception:
            print(format_exc())
            print("Could not import '%s' node." % self.name)
            if curr_size is None:
                pass
            elif hasattr(self.node, 'parse'):
                self.node.set_size(curr_size)
            else:
                self.parent.set_size(curr_size, attr_index=index)
Ejemplo n.º 6
0
    def parse(self, **kwargs):
        '''
        Parses this ArrayBlock in the way specified by the keyword arguments.

        If rawdata or a filepath is supplied, it will be used to parse
        this ArrayBlock(or the specified entry if attr_index is not None).

        If initdata is supplied and not rawdata nor a filepath, it will be
        used to replace the entries in this ArrayBlock if attr_index is None.
        If attr_index is instead not None, self[attr_index] will be
        replaced with initdata.

        If rawdata, initdata, filepath, and init_attrs are all unsupplied,
        all entries in this array will be deleted and replaced with new ones.

        If rawdata, initdata, and filepath are all unsupplied or None
        and init_attrs is False, this method will do nothing more than
        replace all index entries with None.

        Raises AssertionError if attr_index is None and initdata
        does not have __iter__ or __len__ methods.
        Raises TypeError if rawdata and filepath are both supplied.
        Raises TypeError if rawdata doesnt have read, seek, and peek methods.

        Optional keywords arguments:
        # bool:
        init_attrs --- Whether or not to clear the contents of the ArrayBlock.
                       Defaults to True. If True, and 'rawdata' and 'filepath'
                       are None, all the cleared array elements will be rebuilt
                       using the desciptor in this Blocks SUB_STRUCT entry.

        # buffer:
        rawdata ------ A peekable buffer that will be used for parsing
                       elements of this ArrayBlock. Defaults to None.
                       If supplied, do not supply 'filepath'.

        # int:
        root_offset -- The root offset that all rawdata reading is done from.
                       Pointers and other offsets are relative to this value.
                       Passed to the parser of this ArrayBlocks FieldType.
        offset ------- The initial offset that rawdata reading is done from.
                       Passed to the parser of this ArrayBlocks FieldType.

        # int/str:
        attr_index --- The specific attribute index to initialize. Operates on
                       all indices if unsupplied or None. Defaults to None.

        # object:
        initdata ----- An iterable of objects to replace the contents of
                       this ArrayBlock if attr_index is None.
                       If attr_index is not None, this is instead an object
                       to replace self[attr_index]

        #str:
        filepath ----- An absolute path to a file to use as rawdata to parse
                       this ArrayBlock. If supplied, do not supply 'rawdata'.
        '''
        attr_index = kwargs.pop('attr_index', None)
        initdata = kwargs.pop('initdata', None)
        desc = object.__getattribute__(self, "desc")

        writable = kwargs.pop('writable', False)
        with get_rawdata_context(writable=writable, **kwargs) as rawdata:
            if attr_index is not None:
                # parsing/initializing just one attribute
                if isinstance(attr_index, str) and attr_index not in desc:
                    attr_index = desc['NAME_MAP'][attr_index]

                attr_desc = desc['SUB_STRUCT']
                if initdata is None:
                    # we are either parsing the attribute from rawdata or nothing
                    kwargs.update(desc=attr_desc, parent=self, rawdata=rawdata,
                                  attr_index=attr_index)
                    kwargs.pop('filepath', None)
                    attr_desc['TYPE'].parser(**kwargs)
                elif isinstance(self[attr_index], Block):
                    self[attr_index].parse(initdata=initdata)
                else:
                    # non-Block initdata was provided for this
                    # attribute, so just place it in the ArrayBlock.
                    self[attr_index] = initdata

                return

            # parsing/initializing all array elements, so clear and resize
            list.__delitem__(self, slice(None, None, None))
            if initdata is not None:
                list.extend(self, [None]*len(initdata))
                self.set_size()  # update the size to the initdata length
            else:
                list.extend(self, [None]*self.get_size())

            if rawdata is not None:
                # parse the ArrayBlock from raw data
                try:
                    # we are either parsing the attribute from rawdata or nothing
                    kwargs.update(desc=desc, node=self, rawdata=rawdata)
                    kwargs.pop('filepath', None)
                    desc['TYPE'].parser(**kwargs)
                except Exception as e:
                    a = e.args[:-1]
                    e_str = "\n"
                    try:
                        e_str = e.args[-1] + e_str
                    except IndexError:
                        pass
                    e.args = a + (e_str + "Error occurred while " +
                                  "attempting to parse %s." % type(self),)
                    raise e
            elif kwargs.get('init_attrs', True) or initdata is not None:
                # initialize the attributes
                try:
                    attr_desc = desc['SUB_STRUCT']
                    attr_f_type = attr_desc['TYPE']
                except Exception:
                    attr_desc = attr_f_type = None

                if attr_f_type is None or attr_desc is None:
                    raise TypeError(
                        "Could not locate the sub-struct descriptor.\n" +
                        "Could not initialize ArrayBlock")

                # loop through each element in the array and initialize it
                for i in range(len(self)):
                    attr_f_type.parser(attr_desc, parent=self, attr_index=i)

                # Only initialize the STEPTREE if the block has a STEPTREE
                s_desc = desc.get('STEPTREE')
                if s_desc:
                    s_desc['TYPE'].parser(s_desc, parent=self,
                                          attr_index='STEPTREE')

        if initdata is None:
            return

        # if an initdata was provided, make sure it can be used
        assert (hasattr(initdata, '__iter__') and
                hasattr(initdata, '__len__')), (
                    "If provided, initdata must be an iterable with a length")

        # parse the initialized attributes with the initdata
        if isinstance(initdata, Block):
            for i in range(len(initdata)):
                self.parse(attr_index=i, initdata=initdata[i])

            # if the initdata has a STEPTREE node, copy it to
            # this Block if this Block can hold a STEPTREE.
            if hasattr(self, "STEPTREE") and hasattr(initdata, "STEPTREE"):
                self.parse(attr_index="STEPTREE", initdata=initdata.STEPTREE)
        else:
            for i in range(len(initdata)):
                self[i] = initdata[i]