def init(self, flash_image_name, flash_capacity, erase_block_size,
             image_length, header_generation_number, header_size):
        """"FFFF post-constructor initializer for a new FFFF

        FFFF post-constructor initializer for creating an FFFF (as opposed
        to reading an existing one from a file), and returns a success flag.
        The FFFF ROMimage buffer is sized explicitly from the image_length
        parameter.
        """
        # Validate the parameters
        if (header_size < FFFF_HEADER_SIZE_MIN) or \
           (header_size > FFFF_HEADER_SIZE_MAX):
            raise ValueError("header_size is out of range")
        elif not is_power_of_2(erase_block_size):
            raise ValueError("Erase block size must be 2**n")
        elif (image_length % erase_block_size) != 0:
            raise ValueError("Image length must be a multiple "
                             "of erase bock size")

        self.header_size = header_size
        self.flash_image_name = flash_image_name
        self.flash_capacity = flash_capacity
        self.erase_block_size = erase_block_size
        self.flash_image_length = image_length
        self.header_generation_number = header_generation_number

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.get_header_block_size()
        self.element_location_max = image_length

        # Resize the ROMimage buffer to the correct size
        self.ffff_buf = bytearray(image_length)
        #self.mv = memoryview(self.ffff_buf)

        # Create the 2 FFFF headers
        self.ffff0 = Ffff(self.ffff_buf,
                          0,
                          flash_image_name,
                          flash_capacity,
                          erase_block_size,
                          image_length,
                          header_generation_number,
                          header_size)
        self.ffff1 = Ffff(self.ffff_buf,
                          self.get_header_block_size(),
                          flash_image_name,
                          flash_capacity,
                          erase_block_size,
                          image_length,
                          header_generation_number,
                          header_size)
        return True
    def init(self, flash_image_name, flash_capacity, erase_block_size,
             image_length, header_generation_number, header_size):
        """"FFFF post-constructor initializer for a new FFFF

        FFFF post-constructor initializer for creating an FFFF (as opposed
        to reading an existing one from a file), and returns a success flag.
        The FFFF ROMimage buffer is sized explicitly from the image_length
        parameter.
        """
        # Validate the parameters
        if (header_size < FFFF_HEADER_SIZE_MIN) or \
           (header_size > FFFF_HEADER_SIZE_MAX):
            raise ValueError("header_size is out of range")
        elif not is_power_of_2(erase_block_size):
            raise ValueError("Erase block size must be 2**n")
        elif (image_length % erase_block_size) != 0:
            raise ValueError("Image length must be a multiple "
                             "of erase bock size")

        self.header_size = header_size
        self.flash_image_name = flash_image_name
        self.flash_capacity = flash_capacity
        self.erase_block_size = erase_block_size
        self.flash_image_length = image_length
        self.header_generation_number = header_generation_number

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.get_header_block_size()
        self.element_location_max = image_length

        # Resize the ROMimage buffer to the correct size
        self.ffff_buf = bytearray(image_length)
        #self.mv = memoryview(self.ffff_buf)

        # Create the 2 FFFF headers
        self.ffff0 = Ffff(self.ffff_buf, 0, flash_image_name, flash_capacity,
                          erase_block_size, image_length,
                          header_generation_number, header_size)
        self.ffff1 = Ffff(self.ffff_buf, self.get_header_block_size(),
                          flash_image_name, flash_capacity, erase_block_size,
                          image_length, header_generation_number, header_size)
        return True
class FfffRomimage:

    # FFFF constructor
    #
    def __init__(self):
        """FFFF ROMimage constructor

        The FFFF representation contains a buffer of the ROM span covered by
        the image.
        """
        # FFFF header fields
        self.header_size = FFFF_HEADER_SIZE_DEFAULT
        self.ffff0 = None
        self.ffff1 = None
        self.ffff_buf = None
        self.mv = None
        self.flash_image_name = None
        self.flash_capacity = 0
        self.erase_block_size = 0
        self.flash_image_length = 0
        self.header_generation_number = 0
        self.element_location_min = 0
        self.element_location_max = 0

    def init(self, flash_image_name, flash_capacity, erase_block_size,
             image_length, header_generation_number, header_size):
        """"FFFF post-constructor initializer for a new FFFF

        FFFF post-constructor initializer for creating an FFFF (as opposed
        to reading an existing one from a file), and returns a success flag.
        The FFFF ROMimage buffer is sized explicitly from the image_length
        parameter.
        """
        # Validate the parameters
        if (header_size < FFFF_HEADER_SIZE_MIN) or \
           (header_size > FFFF_HEADER_SIZE_MAX):
            raise ValueError("header_size is out of range")
        elif not is_power_of_2(erase_block_size):
            raise ValueError("Erase block size must be 2**n")
        elif (image_length % erase_block_size) != 0:
            raise ValueError("Image length must be a multiple "
                             "of erase bock size")

        self.header_size = header_size
        self.flash_image_name = flash_image_name
        self.flash_capacity = flash_capacity
        self.erase_block_size = erase_block_size
        self.flash_image_length = image_length
        self.header_generation_number = header_generation_number

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.get_header_block_size()
        self.element_location_max = image_length

        # Resize the ROMimage buffer to the correct size
        self.ffff_buf = bytearray(image_length)
        #self.mv = memoryview(self.ffff_buf)

        # Create the 2 FFFF headers
        self.ffff0 = Ffff(self.ffff_buf, 0, flash_image_name, flash_capacity,
                          erase_block_size, image_length,
                          header_generation_number, header_size)
        self.ffff1 = Ffff(self.ffff_buf, self.get_header_block_size(),
                          flash_image_name, flash_capacity, erase_block_size,
                          image_length, header_generation_number, header_size)
        return True

    def init_from_file(self, filename):
        """"FFFF post-constructor initializer to read an FFFF from file

        Distinct from "init" above, this reads in an existing FFFF file
        and parses it, returning a success flag. The FFFF ROMimage buffer
        is sized to the supplied file.
        """
        if filename:
            # Try to open the file, and if that fails, try appending the
            # extension.
            names = (filename, filename + FFFF_FILE_EXTENSION)
            rf = None
            for i in range(len(names)):
                try:
                    rf = io.open(names[i], 'rb')
                    break
                except:
                    rf = None

            if not rf:
                raise IOError(" can't find FFFF file" + filename)

            try:
                # Read the FFFF file.
                rf.seek(0, 2)
                read_size = rf.tell()

                # Resize the buffer to hold the file
                self.ffff_buf = bytearray(read_size)
                rf.seek(0, 0)
                rf.readinto(self.ffff_buf)
                rf.close()
            except IOError:
                raise IOError("can't read {0:s}".format(filename))

            self.get_romimage_characteristics()

            # Create the 1st FFFF header/object
            #self.mv = memoryview(self.ffff_buf)
            self.ffff0 = Ffff(self.ffff_buf, 0, self.flash_image_name,
                              self.flash_capacity, self.erase_block_size,
                              self.flash_image_length,
                              self.header_generation_number, 0)
            self.ffff0.unpack()

            # Scan for 2nd header
            offset = self.get_header_block_size()
            while offset < FFFF_MAX_HEADER_BLOCK_OFFSET:
                # Unpack and validate the nose and tail sentinels
                ffff_hdr = unpack_from("<16s", self.ffff_buf, offset)
                nose_sentinel = ffff_hdr[0]
                ffff_hdr = unpack_from("<16s", self.ffff_buf,
                                       offset + FFFF_HDR_OFF_TAIL_SENTINEL)
                tail_sentinel = ffff_hdr[0]

                # Create the 2nd FFFF header/object?
                if nose_sentinel == FFFF_SENTINEL and \
                        tail_sentinel == FFFF_SENTINEL:
                    self.ffff1 = Ffff(self.ffff_buf, offset,
                                      self.flash_image_name,
                                      self.flash_capacity,
                                      self.erase_block_size,
                                      self.flash_image_length,
                                      self.header_generation_number, 0)
                    self.ffff1.unpack()
                    break
                else:
                    offset <<= 1
        else:
            raise ValueError("no file specified")

        return True

    def get_header_block_size(self):
        # Determine the size of the FFFF header block, defined as a
        # power-of-2 * the erase-block-size
        return get_header_block_size(self.erase_block_size, self.header_size)

    def recalculate_header_offsets(self):
        """ Recalculate element table size and offsets from header_size

        Because we have variable-size FFFF headers, we need to recalculate the
        number of entries in the section table, and the offsets to all fields
        which follow.
        """
        global FFFF_HDR_NUM_ELEMENTS, FFFF_HDR_LEN_ELEMENT_TBL, \
            FFFF_HDR_NUM_RESERVED, FFFF_HDR_LEN_RESERVED, \
            FFFF_HDR_OFF_RESERVED, FFFF_RSVD_SIZE, FFFF_HDR_OFF_TAIL_SENTINEL
        # TFTF section table and derived lengths
        FFFF_HDR_NUM_ELEMENTS = \
            ((self.header_size - FFFF_HDR_LEN_FIXED_PART) // FFFF_ELT_LENGTH)
        FFFF_HDR_LEN_ELEMENT_TBL = (FFFF_HDR_NUM_ELEMENTS * FFFF_ELT_LENGTH)

        FFFF_HDR_LEN_RESERVED = (
            self.header_size -
            (FFFF_HDR_LEN_FIXED_PART + FFFF_HDR_LEN_ELEMENT_TBL))
        FFFF_HDR_NUM_RESERVED = FFFF_HDR_LEN_RESERVED / FFFF_RSVD_SIZE
        self.reserved = [0] * FFFF_HDR_NUM_RESERVED

        # Offsets to fields following the section table
        FFFF_HDR_OFF_ELEMENT_TBL = (FFFF_HDR_OFF_RESERVED +
                                    FFFF_HDR_LEN_RESERVED)
        FFFF_HDR_OFF_TAIL_SENTINEL = (FFFF_HDR_OFF_ELEMENT_TBL +
                                      FFFF_HDR_LEN_ELEMENT_TBL)

    def get_romimage_characteristics(self):
        # Extract the ROMimage size and characteritics from the first FFFF
        # header in the buffer.

        # Unpack the fixed part of the header
        ffff_hdr = unpack_from("<16s16s48sLLLLL", self.ffff_buf)
        sentinel = ffff_hdr[0]
        self.timestamp = ffff_hdr[1]
        self.flash_image_name = ffff_hdr[2]
        self.flash_capacity = ffff_hdr[3]
        self.erase_block_size = ffff_hdr[4]
        self.header_size = ffff_hdr[5]
        self.flash_image_length = ffff_hdr[6]
        self.header_generation_number = ffff_hdr[7]

        # Because we have variable-size FFFF headers, we need to recalculate
        # the number of entries in the section table, and the offsets to all
        # fields which follow.
        self.recalculate_header_offsets()

        # Unpack the 2nd sentinel at the tail
        ffff_hdr = unpack_from("<16s", self.ffff_buf,
                               FFFF_HDR_OFF_TAIL_SENTINEL)
        tail_sentinel = ffff_hdr[0]

        # Verify the sentinels
        if sentinel != FFFF_SENTINEL or tail_sentinel != FFFF_SENTINEL:
            raise ValueError("invalid sentinel")

        # Validate the block size and image length
        if not is_power_of_2(self.erase_block_size):
            raise ValueError("Erase block size must be 2**n")
        elif (self.flash_image_length % self.erase_block_size) != 0:
            raise ValueError("Image length must be a multiple "
                             "of erase bock size")

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.get_header_block_size()
        self.element_location_max = self.flash_capacity

    def add_element(self, element_type, element_class, element_id,
                    element_length, element_location, element_generation,
                    filename):
        # Add a new element to the element table but don't load the
        # TFTF file into the ROMimage buffer.  This is called for FFFF
        # creation, and adds the element to both FFFF headers.
        if self.ffff0 and self.ffff1:
            return \
                self.ffff0.add_element(element_type,
                                       element_class,
                                       element_id,
                                       element_length,
                                       element_location,
                                       element_generation,
                                       filename) and \
                self.ffff1.add_element(element_type,
                                       element_class,
                                       element_id,
                                       element_length,
                                       element_location,
                                       element_generation,
                                       filename)
        else:
            raise ValueError("No FFFF in which to add element")

    def post_process(self):
        """Post-process the FFFF header

        Reads the TFTF files into the ROMimage buffer for both FFFF headers.
        (Called by "create-ffff" after processing all arguments)
        """
        if self.ffff0 and self.ffff1:
            self.ffff0.post_process(self.mv)
            self.ffff1.post_process(self.mv)
        else:
            raise ValueError("No FFFF to post-process")

    def display(self, header_index, filename=None):
        """Display an FFFF header"""

        if self.ffff0 and self.ffff1:
            identical = self.ffff0.same_as(self.ffff1)
            self.ffff0.display(0, not identical, identical, filename)
            self.ffff1.display(1, True, identical, filename)
        else:
            raise ValueError("No FFFF to display")

    def write(self, out_filename):
        """Create the FFFF file

        Create the FFFF file, write the FFFF ROMimage buffer to it and return
        a success flag.  Appends the default FFFF file extension if omitted
        """
        # Reject the write if we didn't pass the sniff test
        if self.ffff0.header_validity != FFFF_HDR_VALID:
            raise ValueError("Invalid FFFF header 0")
        if self.ffff1.header_validity != FFFF_HDR_VALID:
            raise ValueError("Invalid FFFF header 1")

        # Ensure the output file ends in the default file extension if
        # the user hasn't specified their own extension.
        if rfind(out_filename, ".") == -1:
            out_filename += FFFF_FILE_EXTENSION

        # Output the entire FFFF blob
        with open(out_filename, 'wb') as wf:
            wf.write(self.ffff_buf)
            print("Wrote", out_filename)
            return True

    def explode(self, root_filename=None):
        """Write out the component elements

        Write the component elements out as separate files and return
        a success flag.  Appends the default FFFF file extension if omitted
        """

        if not root_filename:
            root_filename = "ffff"
        if self.ffff0.same_as(self.ffff1):
            self.ffff0.write_elements(root_filename)
        else:
            self.ffff0.write_elements(root_filename + "_0")
            self.ffff1.write_elements(root_filename + "_1")

    def create_map_file(self, base_name, base_offset):
        """Create a map file from the base name

        Create a map file from the base name substituting or adding ".map"
        as the file extension, and write out the offsets for the FFFF and
        TFTF fields.
        """
        index = base_name.rindex(".")
        if index != -1:
            base_name = base_name[:index]
        map_name = base_name + ".map"
        with open(map_name, 'w') as mapfile:
            self.write_map(mapfile, base_offset)

    def write_map(self, wf, base_offset):
        """Display the field names and offsets of an FFFF romimage"""
        if self.ffff0 and self.ffff1:
            self.ffff0.write_map(wf, 0, "ffff[0]")
            self.ffff1.write_map(wf, self.get_header_block_size(), "ffff[1]")
            if self.ffff0.same_as(self.ffff1):
                # The FFFF headers are identical, just traverse one for
                # the component TFTFs
                self.ffff0.write_map_elements(wf, 0, "ffff")
            else:
                # The FFFF headers are different, so traverse both for
                # the component TFTFs
                self.ffff0.write_map_elements(wf, 0, "ffff[0]")
                self.ffff1.write_map_elements(wf, 0, "ffff[1]")
        else:
            raise ValueError("No FFFF to display")
    def init_from_file(self, filename):
        """"FFFF post-constructor initializer to read an FFFF from file

        Distinct from "init" above, this reads in an existing FFFF file
        and parses it, returning a success flag. The FFFF ROMimage buffer
        is sized to the supplied file.
        """
        if filename:
            # Try to open the file, and if that fails, try appending the
            # extension.
            names = (filename, filename + FFFF_FILE_EXTENSION)
            rf = None
            for i in range(len(names)):
                try:
                    rf = io.open(names[i], 'rb')
                    break
                except:
                    rf = None

            if not rf:
                raise IOError(" can't find FFFF file" + filename)

            try:
                # Read the FFFF file.
                rf.seek(0, 2)
                read_size = rf.tell()

                # Resize the buffer to hold the file
                self.ffff_buf = bytearray(read_size)
                rf.seek(0, 0)
                rf.readinto(self.ffff_buf)
                rf.close()
            except IOError:
                raise IOError("can't read {0:s}".format(filename))

            self.get_romimage_characteristics()

            # Create the 1st FFFF header/object
            #self.mv = memoryview(self.ffff_buf)
            self.ffff0 = Ffff(self.ffff_buf, 0, self.flash_image_name,
                              self.flash_capacity, self.erase_block_size,
                              self.flash_image_length,
                              self.header_generation_number, 0)
            self.ffff0.unpack()

            # Scan for 2nd header
            offset = self.get_header_block_size()
            while offset < FFFF_MAX_HEADER_BLOCK_OFFSET:
                # Unpack and validate the nose and tail sentinels
                ffff_hdr = unpack_from("<16s", self.ffff_buf, offset)
                nose_sentinel = ffff_hdr[0]
                ffff_hdr = unpack_from("<16s", self.ffff_buf,
                                       offset + FFFF_HDR_OFF_TAIL_SENTINEL)
                tail_sentinel = ffff_hdr[0]

                # Create the 2nd FFFF header/object?
                if nose_sentinel == FFFF_SENTINEL and \
                        tail_sentinel == FFFF_SENTINEL:
                    self.ffff1 = Ffff(self.ffff_buf, offset,
                                      self.flash_image_name,
                                      self.flash_capacity,
                                      self.erase_block_size,
                                      self.flash_image_length,
                                      self.header_generation_number, 0)
                    self.ffff1.unpack()
                    break
                else:
                    offset <<= 1
        else:
            raise ValueError("no file specified")

        return True
class FfffRomimage:

    # FFFF constructor
    #
    def __init__(self):
        """FFFF ROMimage constructor

        The FFFF representation contains a buffer of the ROM span covered by
        the image.
        """
        # FFFF header fields
        self.header_size = FFFF_HEADER_SIZE_DEFAULT
        self.ffff0 = None
        self.ffff1 = None
        self.ffff_buf = None
        self.mv = None
        self.flash_image_name = None
        self.flash_capacity = 0
        self.erase_block_size = 0
        self.flash_image_length = 0
        self.header_generation_number = 0
        self.element_location_min = 0
        self.element_location_max = 0

    def init(self, flash_image_name, flash_capacity, erase_block_size,
             image_length, header_generation_number, header_size):
        """"FFFF post-constructor initializer for a new FFFF

        FFFF post-constructor initializer for creating an FFFF (as opposed
        to reading an existing one from a file), and returns a success flag.
        The FFFF ROMimage buffer is sized explicitly from the image_length
        parameter.
        """
        # Validate the parameters
        if (header_size < FFFF_HEADER_SIZE_MIN) or \
           (header_size > FFFF_HEADER_SIZE_MAX):
            raise ValueError("header_size is out of range")
        elif not is_power_of_2(erase_block_size):
            raise ValueError("Erase block size must be 2**n")
        elif (image_length % erase_block_size) != 0:
            raise ValueError("Image length must be a multiple "
                             "of erase bock size")

        self.header_size = header_size
        self.flash_image_name = flash_image_name
        self.flash_capacity = flash_capacity
        self.erase_block_size = erase_block_size
        self.flash_image_length = image_length
        self.header_generation_number = header_generation_number

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.get_header_block_size()
        self.element_location_max = image_length

        # Resize the ROMimage buffer to the correct size
        self.ffff_buf = bytearray(image_length)
        #self.mv = memoryview(self.ffff_buf)

        # Create the 2 FFFF headers
        self.ffff0 = Ffff(self.ffff_buf,
                          0,
                          flash_image_name,
                          flash_capacity,
                          erase_block_size,
                          image_length,
                          header_generation_number,
                          header_size)
        self.ffff1 = Ffff(self.ffff_buf,
                          self.get_header_block_size(),
                          flash_image_name,
                          flash_capacity,
                          erase_block_size,
                          image_length,
                          header_generation_number,
                          header_size)
        return True

    def init_from_file(self, filename):
        """"FFFF post-constructor initializer to read an FFFF from file

        Distinct from "init" above, this reads in an existing FFFF file
        and parses it, returning a success flag. The FFFF ROMimage buffer
        is sized to the supplied file.
        """
        if filename:
            # Try to open the file, and if that fails, try appending the
            # extension.
            names = (filename, filename + FFFF_FILE_EXTENSION)
            rf = None
            for i in range(len(names)):
                try:
                    rf = io.open(names[i], 'rb')
                    break
                except:
                    rf = None

            if not rf:
                raise IOError(" can't find FFFF file" + filename)

            try:
                # Read the FFFF file.
                rf.seek(0, 2)
                read_size = rf.tell()

                # Resize the buffer to hold the file
                self.ffff_buf = bytearray(read_size)
                rf.seek(0, 0)
                rf.readinto(self.ffff_buf)
                rf.close()
            except IOError:
                raise IOError("can't read {0:s}".format(filename))

            self.get_romimage_characteristics()

            # Create the 1st FFFF header/object
            #self.mv = memoryview(self.ffff_buf)
            self.ffff0 = Ffff(self.ffff_buf, 0,
                              self.flash_image_name,
                              self.flash_capacity,
                              self.erase_block_size,
                              self.flash_image_length,
                              self.header_generation_number,
                              0)
            self.ffff0.unpack()

            # Scan for 2nd header
            offset = self.get_header_block_size()
            while offset < FFFF_MAX_HEADER_BLOCK_OFFSET:
                # Unpack and validate the nose and tail sentinels
                ffff_hdr = unpack_from("<16s", self.ffff_buf,
                                       offset)
                nose_sentinel = ffff_hdr[0]
                ffff_hdr = unpack_from("<16s", self.ffff_buf,
                                       offset +
                                       FFFF_HDR_OFF_TAIL_SENTINEL)
                tail_sentinel = ffff_hdr[0]

                # Create the 2nd FFFF header/object?
                if nose_sentinel == FFFF_SENTINEL and \
                        tail_sentinel == FFFF_SENTINEL:
                    self.ffff1 = Ffff(self.ffff_buf, offset,
                                      self.flash_image_name,
                                      self.flash_capacity,
                                      self.erase_block_size,
                                      self.flash_image_length,
                                      self.header_generation_number,
                                      0)
                    self.ffff1.unpack()
                    break
                else:
                    offset <<= 1
        else:
            raise ValueError("no file specified")

        return True

    def get_header_block_size(self):
        # Determine the size of the FFFF header block, defined as a
        # power-of-2 * the erase-block-size
        return get_header_block_size(self.erase_block_size, self.header_size)

    def recalculate_header_offsets(self):
        """ Recalculate element table size and offsets from header_size

        Because we have variable-size FFFF headers, we need to recalculate the
        number of entries in the section table, and the offsets to all fields
        which follow.
        """
        global FFFF_HDR_NUM_ELEMENTS, FFFF_HDR_LEN_ELEMENT_TBL, \
            FFFF_HDR_NUM_RESERVED, FFFF_HDR_LEN_RESERVED, \
            FFFF_HDR_OFF_RESERVED, FFFF_RSVD_SIZE, FFFF_HDR_OFF_TAIL_SENTINEL
        # TFTF section table and derived lengths
        FFFF_HDR_NUM_ELEMENTS = \
            ((self.header_size - FFFF_HDR_LEN_FIXED_PART) // FFFF_ELT_LENGTH)
        FFFF_HDR_LEN_ELEMENT_TBL = (FFFF_HDR_NUM_ELEMENTS * FFFF_ELT_LENGTH)

        FFFF_HDR_LEN_RESERVED = (self.header_size -
                                (FFFF_HDR_LEN_FIXED_PART +
                                 FFFF_HDR_LEN_ELEMENT_TBL))
        FFFF_HDR_NUM_RESERVED = FFFF_HDR_LEN_RESERVED / FFFF_RSVD_SIZE
        self.reserved = [0] * FFFF_HDR_NUM_RESERVED

        # Offsets to fields following the section table
        FFFF_HDR_OFF_ELEMENT_TBL = (FFFF_HDR_OFF_RESERVED +
                                    FFFF_HDR_LEN_RESERVED)
        FFFF_HDR_OFF_TAIL_SENTINEL = (FFFF_HDR_OFF_ELEMENT_TBL +
                                      FFFF_HDR_LEN_ELEMENT_TBL)

    def get_romimage_characteristics(self):
        # Extract the ROMimage size and characteritics from the first FFFF
        # header in the buffer.

        # Unpack the fixed part of the header
        ffff_hdr = unpack_from("<16s16s48sLLLLL", self.ffff_buf)
        sentinel = ffff_hdr[0]
        self.timestamp = ffff_hdr[1]
        self.flash_image_name = ffff_hdr[2]
        self.flash_capacity = ffff_hdr[3]
        self.erase_block_size = ffff_hdr[4]
        self.header_size = ffff_hdr[5]
        self.flash_image_length = ffff_hdr[6]
        self.header_generation_number = ffff_hdr[7]

        # Because we have variable-size FFFF headers, we need to recalculate
        # the number of entries in the section table, and the offsets to all
        # fields which follow.
        self.recalculate_header_offsets()

        # Unpack the 2nd sentinel at the tail
        ffff_hdr = unpack_from("<16s", self.ffff_buf,
                               FFFF_HDR_OFF_TAIL_SENTINEL)
        tail_sentinel = ffff_hdr[0]

        # Verify the sentinels
        if sentinel != FFFF_SENTINEL or tail_sentinel != FFFF_SENTINEL:
            raise ValueError("invalid sentinel")

        # Validate the block size and image length
        if not is_power_of_2(self.erase_block_size):
            raise ValueError("Erase block size must be 2**n")
        elif (self.flash_image_length % self.erase_block_size) != 0:
            raise ValueError("Image length must be a multiple "
                             "of erase bock size")

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.get_header_block_size()
        self.element_location_max = self.flash_capacity

    def add_element(self, element_type, element_class, element_id,
                    element_length, element_location, element_generation,
                    filename):
        # Add a new element to the element table but don't load the
        # TFTF file into the ROMimage buffer.  This is called for FFFF
        # creation, and adds the element to both FFFF headers.
        if self.ffff0 and self.ffff1:
            return \
                self.ffff0.add_element(element_type,
                                       element_class,
                                       element_id,
                                       element_length,
                                       element_location,
                                       element_generation,
                                       filename) and \
                self.ffff1.add_element(element_type,
                                       element_class,
                                       element_id,
                                       element_length,
                                       element_location,
                                       element_generation,
                                       filename)
        else:
            raise ValueError("No FFFF in which to add element")

    def post_process(self):
        """Post-process the FFFF header

        Reads the TFTF files into the ROMimage buffer for both FFFF headers.
        (Called by "create-ffff" after processing all arguments)
        """
        if self.ffff0 and self.ffff1:
            self.ffff0.post_process(self.mv)
            self.ffff1.post_process(self.mv)
        else:
            raise ValueError("No FFFF to post-process")

    def display(self, header_index, filename=None):
        """Display an FFFF header"""

        if self.ffff0 and self.ffff1:
            identical = self.ffff0.same_as(self.ffff1)
            self.ffff0.display(0, not identical, identical, filename)
            self.ffff1.display(1, True, identical, filename)
        else:
            raise ValueError("No FFFF to display")

    def write(self, out_filename):
        """Create the FFFF file

        Create the FFFF file, write the FFFF ROMimage buffer to it and return
        a success flag.  Appends the default FFFF file extension if omitted
        """
        # Reject the write if we didn't pass the sniff test
        if self.ffff0.header_validity != FFFF_HDR_VALID:
            raise ValueError("Invalid FFFF header 0")
        if self.ffff1.header_validity != FFFF_HDR_VALID:
            raise ValueError("Invalid FFFF header 1")

        # Ensure the output file ends in the default file extension if
        # the user hasn't specified their own extension.
        if rfind(out_filename, ".") == -1:
            out_filename += FFFF_FILE_EXTENSION

        # Output the entire FFFF blob
        with open(out_filename, 'wb') as wf:
            wf.write(self.ffff_buf)
            print("Wrote", out_filename)
            return True

    def explode(self, root_filename=None):
        """Write out the component elements

        Write the component elements out as separate files and return
        a success flag.  Appends the default FFFF file extension if omitted
        """

        if not root_filename:
            root_filename = "ffff"
        if self.ffff0.same_as(self.ffff1):
            self.ffff0.write_elements(root_filename)
        else:
            self.ffff0.write_elements(root_filename + "_0")
            self.ffff1.write_elements(root_filename + "_1")

    def create_map_file(self, base_name, base_offset):
        """Create a map file from the base name

        Create a map file from the base name substituting or adding ".map"
        as the file extension, and write out the offsets for the FFFF and
        TFTF fields.
        """
        index = base_name.rindex(".")
        if index != -1:
            base_name = base_name[:index]
        map_name = base_name + ".map"
        with open(map_name, 'w') as mapfile:
            self.write_map(mapfile, base_offset)

    def write_map(self, wf, base_offset):
        """Display the field names and offsets of an FFFF romimage"""
        if self.ffff0 and self.ffff1:
            self.ffff0.write_map(wf, 0, "ffff[0]")
            self.ffff1.write_map(wf, self.get_header_block_size(), "ffff[1]")
            if self.ffff0.same_as(self.ffff1):
                # The FFFF headers are identical, just traverse one for
                # the component TFTFs
                self.ffff0.write_map_elements(wf, 0, "ffff")
            else:
                # The FFFF headers are different, so traverse both for
                # the component TFTFs
                self.ffff0.write_map_elements(wf, 0, "ffff[0]")
                self.ffff1.write_map_elements(wf, 0, "ffff[1]")
        else:
            raise ValueError("No FFFF to display")
    def init_from_file(self, filename):
        """"FFFF post-constructor initializer to read an FFFF from file

        Distinct from "init" above, this reads in an existing FFFF file
        and parses it, returning a success flag. The FFFF ROMimage buffer
        is sized to the supplied file.
        """
        if filename:
            # Try to open the file, and if that fails, try appending the
            # extension.
            names = (filename, filename + FFFF_FILE_EXTENSION)
            rf = None
            for i in range(len(names)):
                try:
                    rf = io.open(names[i], 'rb')
                    break
                except:
                    rf = None

            if not rf:
                raise IOError(" can't find FFFF file" + filename)

            try:
                # Read the FFFF file.
                rf.seek(0, 2)
                read_size = rf.tell()

                # Resize the buffer to hold the file
                self.ffff_buf = bytearray(read_size)
                rf.seek(0, 0)
                rf.readinto(self.ffff_buf)
                rf.close()
            except IOError:
                raise IOError("can't read {0:s}".format(filename))

            self.get_romimage_characteristics()

            # Create the 1st FFFF header/object
            #self.mv = memoryview(self.ffff_buf)
            self.ffff0 = Ffff(self.ffff_buf, 0,
                              self.flash_image_name,
                              self.flash_capacity,
                              self.erase_block_size,
                              self.flash_image_length,
                              self.header_generation_number,
                              0)
            self.ffff0.unpack()

            # Scan for 2nd header
            offset = self.get_header_block_size()
            while offset < FFFF_MAX_HEADER_BLOCK_OFFSET:
                # Unpack and validate the nose and tail sentinels
                ffff_hdr = unpack_from("<16s", self.ffff_buf,
                                       offset)
                nose_sentinel = ffff_hdr[0]
                ffff_hdr = unpack_from("<16s", self.ffff_buf,
                                       offset +
                                       FFFF_HDR_OFF_TAIL_SENTINEL)
                tail_sentinel = ffff_hdr[0]

                # Create the 2nd FFFF header/object?
                if nose_sentinel == FFFF_SENTINEL and \
                        tail_sentinel == FFFF_SENTINEL:
                    self.ffff1 = Ffff(self.ffff_buf, offset,
                                      self.flash_image_name,
                                      self.flash_capacity,
                                      self.erase_block_size,
                                      self.flash_image_length,
                                      self.header_generation_number,
                                      0)
                    self.ffff1.unpack()
                    break
                else:
                    offset <<= 1
        else:
            raise ValueError("no file specified")

        return True
예제 #7
0
class FfffRomimage:

    # FFFF constructor
    #
    def __init__(self):
        """FFFF ROMimage constructor

        The FFFF representation contains a buffer of the ROM span covered by
        the image.
        """
        # FFFF header fields
        self.ffff0 = None
        self.ffff1 = None
        self.ffff_buf = None
        self.mv = None
        self.flash_image_name = None
        self.flash_capacity = 0
        self.erase_block_size = 0
        self.flash_image_length = 0
        self.header_generation_number = 0
        self.element_location_min = 0
        self.element_location_max = 0

    def init(self, flash_image_name, flash_capacity, erase_block_size,
             image_length, header_generation_number):
        """"FFFF post-constructor initializer for a new FFFF

        FFFF post-constructor initializer for creating an FFFF (as opposed
        to reading an existing one from a file), and returns a success flag.
        The FFFF ROMimage buffer is sized explicitly from the image_length
        parameter.
        """
        # Validate the parameters
        if not is_power_of_2(erase_block_size):
            error("Erase block size must be 2**n")
            return False
        elif (image_length % erase_block_size) != 0:
            error("Image length must be a multiple of erase bock size")
            return False

        self.flash_image_name = flash_image_name
        self.flash_capacity = flash_capacity
        self.erase_block_size = erase_block_size
        self.flash_image_length = image_length
        self.header_generation_number = header_generation_number

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.header_block_size()
        self.element_location_max = image_length

        # Resize the ROMimage buffer to the correct size
        self.ffff_buf = bytearray(image_length)
        #self.mv = memoryview(self.ffff_buf)

        # Create the 2 FFFF headers
        self.ffff0 = Ffff(self.ffff_buf, 0, flash_image_name, flash_capacity,
                          erase_block_size, image_length,
                          header_generation_number)
        self.ffff1 = Ffff(self.ffff_buf, self.header_block_size(),
                          flash_image_name, flash_capacity, erase_block_size,
                          image_length, header_generation_number)
        return True

    def init_from_file(self, filename):
        """"FFFF post-constructor initializer to read an FFFF from file

        Distinct from "init" above, this reads in an existing FFFF file
        and parses it, returning a success flag. The FFFF ROMimage buffer
        is sized to the supplied file.
        """
        success = True
        if filename:
            # Try to open the file, and if that fails, try appending the
            # extension.
            names = (filename, filename + FFFF_FILE_EXTENSION)
            rf = None
            for i in range(len(names)):
                try:
                    rf = io.open(names[i], 'rb')
                    break
                except:
                    rf = None

            if not rf:
                error(" can't find FFFF file", filename)
                return False

            try:
                # Read the FFFF file.
                rf.seek(0, 2)
                read_size = rf.tell()

                # Resize the buffer to hold the file
                self.ffff_buf = bytearray(read_size)
                rf.seek(0, 0)
                rf.readinto(self.ffff_buf)
                rf.close()

                if not self.get_romimage_characteristics():
                    error("invalid file")
                    return False

                # Create the 1st FFFF header/object
                #self.mv = memoryview(self.ffff_buf)
                self.ffff0 = Ffff(self.ffff_buf, 0, self.flash_image_name,
                                  self.flash_capacity, self.erase_block_size,
                                  self.flash_image_length,
                                  self.header_generation_number)
                self.ffff0.unpack()

                # Scan for 2nd header
                offset = self.header_block_size()
                while offset < FFFF_MAX_HEADER_BLOCK_OFFSET:
                    # Unpack and validate the nose and tail sentinels
                    ffff_hdr = unpack_from("<16s", self.ffff_buf, offset)
                    nose_sentinel = ffff_hdr[0]
                    ffff_hdr = unpack_from("<16s", self.ffff_buf,
                                           offset + FFFF_HDR_OFF_TAIL_SENTINEL)
                    tail_sentinel = ffff_hdr[0]

                    # Create the 2nd FFFF header/object?
                    if nose_sentinel == FFFF_SENTINEL and \
                            tail_sentinel == FFFF_SENTINEL:
                        self.header_block_size = offset
                        self.ffff1 = Ffff(self.ffff_buf, offset,
                                          self.flash_image_name,
                                          self.flash_capacity,
                                          self.erase_block_size,
                                          self.flash_image_length,
                                          self.header_generation_number)
                        self.ffff1.unpack()
                        break
                    else:
                        offset <<= 1
            except:
                error("can't read", filename)
                success = False
        else:
            error("no file specified")
            success = False

        return success

    def header_block_size(self):
        # Determine the size of the FFFF header block, defined as a
        # power-of-2 * the erase-block-size
        for size in range(self.erase_block_size, FFFF_MAX_HEADER_BLOCK_SIZE):
            if size > FFFF_HDR_LENGTH:
                return size

    def get_romimage_characteristics(self):
        # Extract the ROMimage size and characteritics from the first FFFF
        # header in the buffer.

        # Unpack the fixed part of the header
        ffff_hdr = unpack_from("<16s16s48sLLLLL", self.ffff_buf)
        sentinel = ffff_hdr[0]
        self.timestamp = ffff_hdr[1]
        self.flash_image_name = ffff_hdr[2]
        self.flash_capacity = ffff_hdr[3]
        self.erase_block_size = ffff_hdr[4]
        self.header_size = ffff_hdr[5]
        self.flash_image_length = ffff_hdr[6]
        self.header_generation_number = ffff_hdr[7]

        # Unpack the 2nd sentinel at the tail
        ffff_hdr = unpack_from("<16s", self.ffff_buf,
                               FFFF_HDR_OFF_TAIL_SENTINEL)
        tail_sentinel = ffff_hdr[0]

        # Verify the sentinels
        if sentinel != FFFF_SENTINEL or \
                tail_sentinel != FFFF_SENTINEL:
            error("invalid sentinel")
            return False

        # Validate the block size and image length
        if not is_power_of_2(self.erase_block_size):
            error("Erase block size must be 2**n")
            return False
        elif (self.flash_image_length % self.erase_block_size) != 0:
            error("Image length must be a multiple of erase bock size")
            return False

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.header_block_size()
        self.element_location_max = self.flash_capacity
        return True

    def add_element(self, element_type, element_id, element_generation,
                    element_location, element_length, filename):
        # Add a new element to the element table but don't load the
        # TFTF file into the ROMimage buffer.  This is called for FFFF
        # creation, and adds the element to both FFFF headers.  It returns
        # a success flag
        if self.ffff0 and self.ffff1:
            return \
                self.ffff0.add_element(element_type, element_id,
                                       element_generation,
                                       element_location, element_length,
                                       filename) and \
                self.ffff1.add_element(element_type, element_id,
                                       element_generation,
                                       element_location, element_length,
                                       filename)
        else:
            error("No FFFF in which to add element")
            return False

    def post_process(self):
        """Post-process the FFFF header

        Reads the TFTF files into the ROMimage buffer for both FFFF headers.
        (Called by "create-ffff" after processing all arguments)
        """
        if self.ffff0 and self.ffff1:
            self.ffff0.post_process(self.mv)
            self.ffff1.post_process(self.mv)
        else:
            error("No FFFF to post-process")

    def display(self, header_index, filename=None):
        """Display an FFFF header"""

        if self.ffff0 and self.ffff1:
            identical = self.ffff0.same_as(self.ffff1)
            self.ffff0.display(0, not identical, identical, filename)
            self.ffff1.display(1, True, identical, filename)
        else:
            error("No FFFF to display")

    def write(self, out_filename):
        """Create the FFFF file

        Create the FFFF file, write the FFFF ROMimage buffer to it and return
        a success flag.  Appends the default FFFF file extension if omitted
        """

        # Reject the write if we didn't pass the sniff test
        if self.ffff0.header_validity != FFFF_HDR_VALID:
            error("Invalid FFFF header 0")
            return False
        if self.ffff1.header_validity != FFFF_HDR_VALID:
            error("Invalid FFFF header 1")
            return False

        # Ensure the output file ends in the default file extension if
        # the user hasn't specified their own extension.
        if rfind(out_filename, ".") == -1:
            out_filename += FFFF_FILE_EXTENSION

        try:
            # Output the entire FFFF blob
            with open(out_filename, 'wb') as wf:
                wf.write(self.ffff_buf)
                print("Wrote", out_filename)
                return True
        except:
            error("Failed to write", out_filename)
            return False

    def explode(self, root_filename=None):
        """Write out the component elements

        Write the component elements out as separate files and return
        a success flag.  Appends the default FFFF file extension if omitted
        """

        if not root_filename:
            root_filename = "ffff"
        if self.ffff0.same_as(self.ffff1):
            self.ffff0.write_elements(root_filename)
        else:
            self.ffff0.write_elements(root_filename + "_0")
            self.ffff1.write_elements(root_filename + "_1")
예제 #8
0
class FfffRomimage:

    # FFFF constructor
    #
    def __init__(self):
        """FFFF ROMimage constructor

        The FFFF representation contains a buffer of the ROM span covered by
        the image.
        """
        # FFFF header fields
        self.ffff0 = None
        self.ffff1 = None
        self.ffff_buf = None
        self.mv = None
        self.flash_image_name = None
        self.flash_capacity = 0
        self.erase_block_size = 0
        self.flash_image_length = 0
        self.header_generation_number = 0
        self.element_location_min = 0
        self.element_location_max = 0

    def init(self, flash_image_name, flash_capacity, erase_block_size,
             image_length, header_generation_number):
        """"FFFF post-constructor initializer for a new FFFF

        FFFF post-constructor initializer for creating an FFFF (as opposed
        to reading an existing one from a file), and returns a success flag.
        The FFFF ROMimage buffer is sized explicitly from the image_length
        parameter.
        """
        # Validate the parameters
        if not is_power_of_2(erase_block_size):
            error("Erase block size must be 2**n")
            return False
        elif (image_length % erase_block_size) != 0:
            error("Image length must be a multiple of erase bock size")
            return False

        self.flash_image_name = flash_image_name
        self.flash_capacity = flash_capacity
        self.erase_block_size = erase_block_size
        self.flash_image_length = image_length
        self.header_generation_number = header_generation_number

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.header_block_size()
        self.element_location_max = image_length

        # Resize the ROMimage buffer to the correct size
        self.ffff_buf = bytearray(image_length)
        #self.mv = memoryview(self.ffff_buf)

        # Create the 2 FFFF headers
        self.ffff0 = Ffff(self.ffff_buf, 0, flash_image_name,
                          flash_capacity, erase_block_size,
                          image_length, header_generation_number)
        self.ffff1 = Ffff(self.ffff_buf, self.header_block_size(),
                          flash_image_name, flash_capacity,
                          erase_block_size, image_length,
                          header_generation_number)
        return True

    def init_from_file(self, filename):
        """"FFFF post-constructor initializer to read an FFFF from file

        Distinct from "init" above, this reads in an existing FFFF file
        and parses it, returning a success flag. The FFFF ROMimage buffer
        is sized to the supplied file.
        """
        success = True
        if filename:
            # Try to open the file, and if that fails, try appending the
            # extension.
            names = (filename, filename + FFFF_FILE_EXTENSION)
            rf = None
            for i in range(len(names)):
                try:
                    rf = io.open(names[i], 'rb')
                    break
                except:
                    rf = None

            if not rf:
                error(" can't find FFFF file", filename)
                return False

            try:
                # Read the FFFF file.
                rf.seek(0, 2)
                read_size = rf.tell()

                # Resize the buffer to hold the file
                self.ffff_buf = bytearray(read_size)
                rf.seek(0, 0)
                rf.readinto(self.ffff_buf)
                rf.close()

                if not self.get_romimage_characteristics():
                    error("invalid file")
                    return False

                # Create the 1st FFFF header/object
                #self.mv = memoryview(self.ffff_buf)
                self.ffff0 = Ffff(self.ffff_buf, 0,
                                  self.flash_image_name,
                                  self.flash_capacity,
                                  self.erase_block_size,
                                  self.flash_image_length,
                                  self.header_generation_number)
                self.ffff0.unpack()

                # Scan for 2nd header
                offset = self.header_block_size()
                while offset < FFFF_MAX_HEADER_BLOCK_OFFSET:
                    # Unpack and validate the nose and tail sentinels
                    ffff_hdr = unpack_from("<16s", self.ffff_buf,
                                           offset)
                    nose_sentinel = ffff_hdr[0]
                    ffff_hdr = unpack_from("<16s", self.ffff_buf,
                                           offset +
                                           FFFF_HDR_OFF_TAIL_SENTINEL)
                    tail_sentinel = ffff_hdr[0]

                    # Create the 2nd FFFF header/object?
                    if nose_sentinel == FFFF_SENTINEL and \
                            tail_sentinel == FFFF_SENTINEL:
                        self.header_block_size = offset
                        self.ffff1 = Ffff(self.ffff_buf, offset,
                                          self.flash_image_name,
                                          self.flash_capacity,
                                          self.erase_block_size,
                                          self.flash_image_length,
                                          self.header_generation_number)
                        self.ffff1.unpack()
                        break
                    else:
                        offset <<= 1
            except:
                error("can't read", filename)
                success = False
        else:
            error("no file specified")
            success = False

        return success

    def header_block_size(self):
        # Determine the size of the FFFF header block, defined as a
        # power-of-2 * the erase-block-size
        for size in range(self.erase_block_size, FFFF_MAX_HEADER_BLOCK_SIZE):
            if size > FFFF_HDR_LENGTH:
                return size

    def get_romimage_characteristics(self):
        # Extract the ROMimage size and characteritics from the first FFFF
        # header in the buffer.

        # Unpack the fixed part of the header
        ffff_hdr = unpack_from("<16s16s48sLLLLL", self.ffff_buf)
        sentinel = ffff_hdr[0]
        self.timestamp = ffff_hdr[1]
        self.flash_image_name = ffff_hdr[2]
        self.flash_capacity = ffff_hdr[3]
        self.erase_block_size = ffff_hdr[4]
        self.header_size = ffff_hdr[5]
        self.flash_image_length = ffff_hdr[6]
        self.header_generation_number = ffff_hdr[7]

        # Unpack the 2nd sentinel at the tail
        ffff_hdr = unpack_from("<16s", self.ffff_buf,
                               FFFF_HDR_OFF_TAIL_SENTINEL)
        tail_sentinel = ffff_hdr[0]

        # Verify the sentinels
        if sentinel != FFFF_SENTINEL or \
                tail_sentinel != FFFF_SENTINEL:
            error("invalid sentinel")
            return False

        # Validate the block size and image length
        if not is_power_of_2(self.erase_block_size):
            error("Erase block size must be 2**n")
            return False
        elif (self.flash_image_length % self.erase_block_size) != 0:
            error("Image length must be a multiple of erase bock size")
            return False

        # Determine the ROM range that can hold the elements
        self.element_location_min = 2 * self.header_block_size()
        self.element_location_max = self.flash_capacity
        return True

    def add_element(self, element_type, element_id, element_generation,
                    element_location, element_length, filename):
        # Add a new element to the element table but don't load the
        # TFTF file into the ROMimage buffer.  This is called for FFFF
        # creation, and adds the element to both FFFF headers.  It returns
        # a success flag
        if self.ffff0 and self.ffff1:
            return \
                self.ffff0.add_element(element_type, element_id,
                                       element_generation,
                                       element_location, element_length,
                                       filename) and \
                self.ffff1.add_element(element_type, element_id,
                                       element_generation,
                                       element_location, element_length,
                                       filename)
        else:
            error("No FFFF in which to add element")
            return False

    def post_process(self):
        """Post-process the FFFF header

        Reads the TFTF files into the ROMimage buffer for both FFFF headers.
        (Called by "create-ffff" after processing all arguments)
        """
        if self.ffff0 and self.ffff1:
            self.ffff0.post_process(self.mv)
            self.ffff1.post_process(self.mv)
        else:
            error("No FFFF to post-process")

    def display(self, header_index, filename=None):
        """Display an FFFF header"""

        if self.ffff0 and self.ffff1:
            identical = self.ffff0.same_as(self.ffff1)
            self.ffff0.display(0, not identical, identical, filename)
            self.ffff1.display(1, True, identical, filename)
        else:
            error("No FFFF to display")

    def write(self, out_filename):
        """Create the FFFF file

        Create the FFFF file, write the FFFF ROMimage buffer to it and return
        a success flag.  Appends the default FFFF file extension if omitted
        """
        
        # Reject the write if we didn't pass the sniff test
        if self.ffff0.header_validity != FFFF_HDR_VALID:
            error("Invalid FFFF header 0")
            return False
        if self.ffff1.header_validity != FFFF_HDR_VALID:
            error("Invalid FFFF header 1")
            return False

        # Ensure the output file ends in the default file extension if
        # the user hasn't specified their own extension.
        if rfind(out_filename, ".") == -1:
            out_filename += FFFF_FILE_EXTENSION

        try:
            # Output the entire FFFF blob
            with open(out_filename, 'wb') as wf:
                wf.write(self.ffff_buf)
                print("Wrote", out_filename)
                return True
        except:
            error("Failed to write", out_filename)
            return False

    def explode(self, root_filename=None):
        """Write out the component elements

        Write the component elements out as separate files and return
        a success flag.  Appends the default FFFF file extension if omitted
        """

        if not root_filename:
            root_filename = "ffff"
        if self.ffff0.same_as(self.ffff1):
            self.ffff0.write_elements(root_filename)
        else:
            self.ffff0.write_elements(root_filename + "_0")
            self.ffff1.write_elements(root_filename + "_1")