Exemplo n.º 1
0
    def open(self, file_path):
        with open(file_path, 'rb') as f:
            file_size = common.get_file_size(f)

            # Read magic header
            magic_number = f.read(4)
            if magic_number != 'BIND':
                raise Exception('Not a BIND file! Missing BIND header id.')

            # Read "size" byte size
            # This determines what's the "size" of the size variable itself
            self.size_byte_size = common.read_uint16(f)
            if self.size_byte_size not in (1, 2, 4):
                raise Exception('Illegal BIND size byte size: %s' % self.index_size)
            
            # Read number of entries
            number_of_entries = common.read_uint16(f)

            # Read block size
            self.block_size = common.read_uint32(f)

            # Read header size
            header_size = common.read_uint32(f)

            # The header size is:
            # (16 + number of entries * self.size_byte_size) aligned to self.block_size
            
            # Load the entry sizes 
            processed_entries = []
            previous_entry_end = header_size
            for i in xrange(0, number_of_entries):
                # Calculate the offset of this entry based on the end of the previous entry
                entry_offset = previous_entry_end

                # Read the entry size
                entry_size = 0
                if self.size_byte_size == 1:
                    entry_size = common.read_uint8(f)

                elif self.size_byte_size == 2:
                    entry_size = common.read_uint16(f)

                elif self.size_byte_size == 4:
                    entry_size = common.read_uint32(f)

                # Calculate how much memory this entry takes
                entry_padded_size = common.align_size(entry_size, self.block_size)

                # Update previous_entry_end for the next iteration
                previous_entry_end += entry_padded_size 
            
                # Add the entry to processed entries
                processed_entries.append((entry_offset, entry_size))

            # Read entries
            for (entry_offset, entry_size) in processed_entries:
                f.seek(entry_offset)

                content = f.read(entry_size)
                new_entry = self.add_entry(content)
Exemplo n.º 2
0
    def open(self, file_path):
        with open(file_path, 'rb') as f:
            archive_size = common.get_file_size(f)
            while f.tell() < archive_size:
                magic_number = f.read(4).decode('ascii', 'ignore')
                if magic_number != 'RIFF':
                    raise Exception('Not a WAVE entry!')

                wave_size = common.read_uint32(f) + 8
                f.seek(-8, os.SEEK_CUR)

                content = f.read(wave_size)
                new_entry = self.add_entry(content)

                # Find how many blocks are needed to contain wave_size
                # and skip that amount of remaining bytes
                block_aligned_wave_size = common.align_size(
                    wave_size, WaveArchive.BLOCK_SIZE)
                f.seek(block_aligned_wave_size - wave_size, os.SEEK_CUR)
Exemplo n.º 3
0
    def open(self, file_path):
        with open(file_path, 'rb') as f:
            file_size = common.get_file_size(f)

            # Read magic header
            magic_number = f.read(4).decode('ascii', 'ignore')
            if magic_number != 'TEXT':
                raise Exception('Not a TEXT file!')

            # Read number of entries
            number_of_entries = common.read_uint32(f)

            # Read size of header
            header_size = common.read_uint32(f)
            if (header_size != 16):
                msg = '# Warning: Non-standard TEXT header size: %s' % header_size
                self.warn(msg)

            # Read content start offset
            content_start_offset = common.read_uint32(f)

            # There should be no post-header padding,
            # but check for it anyways
            pre_header_skip_position = f.tell()

            # Skip the header
            f.seek(header_size)

            # Complete the header padding calculation
            self.header_padding = f.tell() - pre_header_skip_position

            # Read entries
            # Don't create them yet,
            # since we want to store the string index instead of the offset
            # and we can't figure out the string index until we've seen all the strings
            entry_unknowns = [0] * number_of_entries
            entry_string_offsets = [0] * number_of_entries
            for i in range(0, number_of_entries):
                entry_unknowns[i] = common.read_uint32(f)
                entry_string_offsets[i] = common.read_uint32(f)

            # There should be no post-header padding,
            # but check for it anyways
            pre_entry_skip_position = f.tell()

            # Skip to the content start
            f.seek(content_start_offset)

            # Complete the header padding calculation
            self.entry_padding = f.tell() - pre_entry_skip_position

            # Read the actual strings (which are used multiple times per entry)
            # Figure out the string index by sorting string offsets from low to high
            string_offset_index_map = {}
            for index, offset in enumerate(sorted(set(entry_string_offsets))):

                # Update the string offset map
                string_offset_index_map[offset] = index

                # Go to the string offset
                f.seek(offset)

                # If offset is at the end, skip
                if (offset >= file_size):
                    self.warn(
                        'Out of bounds string in TEXT file, using nulls instead'
                    )
                    self.strings.append((None, None, None))
                    continue

                # Read the unknowns
                unknown_first = common.read_uint32(f)
                unknown_second = common.read_uint32(f)

                # Read content until null-terminator (but aligned to 32-bit boundaries)
                raw_string_content = b''
                while True:
                    raw_string_content += f.read(4)
                    if raw_string_content[-1] == 0:
                        break

                # Convert Shift-JIS to unicode
                string_content = common.from_eva_sjis(raw_string_content)

                # Strip trailing \0's
                string_content = string_content.rstrip('\0')

                # Add this string to the array
                self.strings.append(
                    (unknown_first, unknown_second, string_content))

            # Finally create the entries now that we know the string offset to index mapping
            for i in range(0, number_of_entries):
                self.entries.append(
                    (entry_unknowns[i],
                     string_offset_index_map[entry_string_offsets[i]]))
Exemplo n.º 4
0
    def open(self, file_path):
        with open(file_path, 'rb') as f:
            magic_number = f.read(4).decode('ascii', 'ignore')
            if magic_number != 'HGAR':
                raise Exception('Not an HGAR file!')

            self.version = common.read_uint16(f)
            if self.version not in (1, 3):
                raise Exception('Unknown HGAR version: %s' % self.version)
            
            number_of_files = common.read_uint16(f)

            file_header_offsets = [0] * number_of_files
            for i in range(0, number_of_files): 
                file_header_offsets[i] = common.read_uint32(f)

            file_unknowns = [(None, None)] * number_of_files
            file_long_names = [None] * number_of_files

            if self.version == 3:
                for i in range(0, number_of_files):
                    file_unknowns[i] = (common.read_uint32(f), common.read_uint32(f))

                for i in range(0, number_of_files):
                    # Read file number
                    file_number = common.read_uint32(f)

                    # Read name until null-terminator (but aligned to 32-bit boundaries)
                    long_name = b''
                    while True:
                        long_name += f.read(4)

                        if long_name[-1] == 0:
                            break

                    file_long_names[i] = long_name

                    if file_number != i:
                        print('\tWarning: File "%s" is stored as file number %s, but should be %s' % (long_name, file_number, i))

            for i in range(0, number_of_files):
                f.seek(file_header_offsets[i])

                short_name = f.read(0xC)
                reformatted_short_name = short_name[0:-4].rstrip() + short_name[-4:].rstrip()

                encoded_identifier = common.read_uint32(f)
                file_size = common.read_uint32(f)

                new_file = self.add_file(file_long_names[i], reformatted_short_name, file_size)
                new_file.encoded_identifier = encoded_identifier

                new_file.unknown_first = file_unknowns[i][0]
                new_file.unknown_last = file_unknowns[i][1]
                
                new_file.content = f.read(file_size)

                # The new file object will generate the short name and long name
                # upon creation, BUT overwrite these since we're opening
                # and know the exact values (instead of creating from scratch)
                new_file.short_name = reformatted_short_name
                new_file.long_name = file_long_names[i]

            # This must be done after the exact file count is known
            self.decode_identifiers()
Exemplo n.º 5
0
    def open(self, file_path):
        with open(file_path, 'rb') as f:
            file_size = common.get_file_size(f)

            # Read magic header
            magic_number = f.read(4)
            if magic_number != 'HGPT':
                raise Exception('Not an HGPT file! Missing HGPT header id.')

            # Read pp offset
            pp_offset = common.read_uint16(f)
            if pp_offset < 0x10:
                raise Exception(
                    'PP offset less than 0x10, PS2 variant not supported!')

            # Read if it has an extended header
            has_extended_header = common.read_uint16(f)
            if has_extended_header not in (0, 1):
                raise Exception('Unknown has_extended_header value: %s' %
                                has_extended_header)
            self.has_extended_header = has_extended_header = (
                has_extended_header == 1)

            # Read number of divisions
            number_of_divisions = common.read_uint16(f)

            # Read unknowns
            unknown_one = common.read_uint16(f)
            if unknown_one != 0x0001:
                raise Exception('First unknown is not 0x0001: %08X' %
                                unknown_one)

            # ff ff ff ff in pictures with extended header
            # 00 00 00 00 in pictures w/o extended header
            unknown_two = common.read_uint32(f)

            # Load divisions
            if has_extended_header:
                # Read number of divisions (again)
                number_of_divisions_repeat = common.read_uint16(f)
                if number_of_divisions != number_of_divisions_repeat:
                    raise Exception(
                        'Number of divisions and its repeat don\'t match: %s != %s'
                        % (number_of_divisions, number_of_divisions_repeat))

                # Read unknown
                # 0x0013 is the most common
                # 0x0014 is the other occurence
                unknown_three = common.read_uint16(f)
                if unknown_three != 0x0013:
                    print '# Warning: UnknownThree (0x%X) != 0x0013' % unknown_three

                # Read division name
                self.division_name = (f.read(8) + '\0' * 8)[0:8]

                # Read divisions
                for i in xrange(0, number_of_divisions):
                    division_start_x = common.read_uint16(f)
                    division_start_y = common.read_uint16(f)
                    division_width = common.read_uint16(f)
                    division_height = common.read_uint16(f)

                    self.divisions.append((division_start_x, division_start_y,
                                           division_width, division_height))

                # Calculate and skip zero padding
                divisions_size = 12 + 8 * number_of_divisions
                divisions_padded_size = common.align_size(divisions_size, 16)
                divisions_padding = divisions_padded_size - divisions_size

                f.seek(divisions_padding, os.SEEK_CUR)

            # Check that it's the correct pp_offset
            if f.tell() != pp_offset:
                raise Exception('Incorrect pp offset')

            # Read pp header
            pp_header = common.read_uint32(f)
            if pp_header & 0x0000FFFF != 0x00007070:
                raise Exception('Missing pp header! %08X' % pp_header)

            # Decipher pp format
            pp_format = (pp_header >> 16) & 0xFFFF
            if pp_format not in (0x13, 0x14, 0x8800):
                raise Exception('PP format (0x%X) is unknown' % pp_format)

            # Calculate values that depend on the pp format
            bytes_per_pixel = 1
            bytes_per_pixel_ppd_size = bytes_per_pixel
            tile_width = 16

            if pp_format == 0x8800:
                bytes_per_pixel = 4
                bytes_per_pixel_ppd_size = 1
                tile_width = 4

            elif pp_format == 0x13:
                bytes_per_pixel = 1
                bytes_per_pixel_ppd_size = bytes_per_pixel
                tile_width = 16

            elif pp_format == 0x14:
                bytes_per_pixel = 0.5
                bytes_per_pixel_ppd_size = bytes_per_pixel
                tile_width = 32

            # Read picture display dimensions
            self.width = pp_display_width = common.read_uint16(f)
            self.height = pp_display_height = common.read_uint16(f)

            # Skip zero padding
            f.seek(2 * 4, os.SEEK_CUR)

            # Read ppd header
            ppd_header = common.read_uint32(f)
            if ppd_header & 0x00FFFFFF != 0x00647070:
                raise Exception('Missing ppd header!')

            # Decipher ppd format
            ppd_format = (ppd_header >> 24) & 0xFF
            if ppd_format != (pp_format & 0xFF):
                raise Exception(
                    'PPD format (0x%X) does not match PP format (0x%X)' %
                    (ppd_format, pp_format & 0xFF))

            # Read ppd display resolution
            ppd_display_width = common.read_uint16(f)
            ppd_display_height = common.read_uint16(f)

            if pp_display_width != ppd_display_width:
                raise Exception(
                    'PP display width (%s) != PPD display width (%s)' %
                    (pp_display_width, ppd_display_width))

            if pp_display_height != ppd_display_height:
                raise Exception(
                    'PP display height (%s) != PPD display height (%s)' %
                    (pp_display_height, ppd_display_height))

            # Skip zero padding
            f.seek(4, os.SEEK_CUR)

            # Read ppd sixteenths resolution
            ppd_sixteenths_width = common.read_uint16(f)
            ppd_sixteenths_height = common.read_uint16(f)

            # Calculate ppd sixteenths resolution (using the pp_display resolution)
            calculated_sixteenths_width = common.align_size(
                pp_display_width, 16)
            calculated_sixteenths_height = common.align_size(
                pp_display_height, 8)

            if (calculated_sixteenths_width != ppd_sixteenths_width) or (
                    calculated_sixteenths_height != ppd_sixteenths_height):
                raise Exception(
                    'PPD sixteenths resolution (%s x %s) doesn\'t match the calculated eights resolution (%s x %s)'
                    % (ppd_sixteenths_width, ppd_sixteenths_height,
                       calculated_sixteenths_width,
                       calculated_sixteenths_height))

            # Calculate storage resolution (using the pp_display resolution)
            # This is the resolution that ties in with the ppd_size
            calculated_storage_width = common.align_size(
                pp_display_width, tile_width)
            calculated_storage_height = common.align_size(pp_display_height, 8)

            number_of_pixels = calculated_storage_width * calculated_storage_height

            # Read ppd_size which is the size of the ppd header + number of pixels
            # The ppd header which is 0x20 bytes in size
            ppd_size = common.read_uint32(f)

            calculated_ppd_size = int(
                number_of_pixels * bytes_per_pixel_ppd_size) + 0x20
            if (calculated_ppd_size != ppd_size):
                raise Exception(
                    'PPD size %s does not match the calculated ppd size %s' %
                    (ppd_size, calculated_ppd_size))

            # Calculate the number of bytes
            number_of_bytes = int(number_of_pixels * bytes_per_pixel)

            # Skip padding
            f.seek(3 * 4, os.SEEK_CUR)

            # Read the tiled image data
            # The image data is stored in a scrambled tiled format, so we'll have to reprocess it
            tiled_image_data = [0] * number_of_pixels
            cache_last_pixel = None

            for i in xrange(0, number_of_pixels):
                if number_of_bytes <= 0:
                    break

                if pp_format == 0x13:
                    tiled_image_data[i] = common.read_uint8(f)
                    number_of_bytes -= 1

                elif pp_format == 0x14:
                    if (i & 1) == 0:
                        # Even read
                        cache_last_pixel = common.read_uint8(f)
                    else:
                        # Odd read
                        number_of_bytes -= 1

                    tiled_image_data[i] = cache_last_pixel & 0xF
                    cache_last_pixel = cache_last_pixel >> 4

                elif pp_format == 0x8800:
                    # Read full RGBA
                    tiled_image_data[i] = (common.read_uint8(f),
                                           common.read_uint8(f),
                                           common.read_uint8(f),
                                           decode_alpha(common.read_uint8(f)))
                    number_of_bytes -= 4

            # Skip unread bytes
            f.seek(number_of_bytes, os.SEEK_CUR)

            # Un-tile and store the information as the content
            self.content = [0] * (pp_display_width * pp_display_height)
            tile_height = 8
            tile_size = tile_width * tile_height
            tile_row = tile_size * int(calculated_storage_width / tile_width)
            for y in xrange(0, pp_display_height):
                for x in xrange(0, pp_display_width):
                    tile_y = int(y / tile_height)
                    tile_x = int(x / tile_width)
                    tile_sub_y = y % tile_height
                    tile_sub_x = x % tile_width
                    self.content[y * pp_display_width +
                                 x] = tiled_image_data[tile_y * tile_row +
                                                       tile_x * tile_size +
                                                       tile_sub_y * tile_width
                                                       + tile_sub_x]

            # Check if optional ppc header exists
            if pp_format == 0x8800:
                self.palette = []

            else:
                # Read ppc header
                ppc_header = common.read_uint32(f)
                if ppc_header & 0xFFFFFFFF != 0x00637070:
                    raise Exception('Missing ppc header!')

                # Skip zero padding
                f.seek(2, os.SEEK_CUR)

                # Read the number of palette entries
                # It needs to be multiplied by 8 to get the correct total
                palette_total = common.read_uint16(f) * 8

                # Skip zero padding
                f.seek(2 * 4, os.SEEK_CUR)

                # Read palette
                self.palette = [(common.read_uint8(f), common.read_uint8(f),
                                 common.read_uint8(f),
                                 decode_alpha(common.read_uint8(f)))
                                for i in xrange(0, palette_total)]
Exemplo n.º 6
0
    def open(self, file_path):
        with open(file_path, 'rb') as f:
            file_size = common.get_file_size(f)

            # Read magic header
            magic_number = f.read(4).decode('ascii', 'ignore')
            if magic_number != '.EVS':
                raise Exception('Not an EVS file!')

            # Read the number of entries
            number_of_entries = common.read_uint32(f)

            # Read entry offsets
            entry_offsets = [0] * number_of_entries
            for i in range(0, number_of_entries):
                entry_offsets[i] = common.read_uint32(f)

            # Loop through entries
            for i in range(0, number_of_entries):
                f.seek(entry_offsets[i])

                # Read entry type
                entry_type = common.read_uint16(f)

                # Read entry size
                entry_size = common.read_uint16(f)

                # Read entry
                number_of_parameters = get_number_of_parameters(entry_type)
                has_content_section = entry_type in HAS_CONTENT_SECTION

                if number_of_parameters is None:
                    # Commands that I've yet to document the parameter count for
                    raise Exception(
                        'Unknown number of parameters for entry type: 0x%X, size: %s'
                        % (entry_type, entry_size))

                # Read the parameters
                entry_parameters = []
                for j in range(0, number_of_parameters):
                    entry_parameters.append(common.read_uint32(f))

                # Whatever remains after the parameters is the content
                parameter_size = 4 * number_of_parameters
                remaining_bytes = entry_size - parameter_size

                if remaining_bytes < 0:
                    raise Exception(
                        'Number of parameters overshoots total entry size; entry type: 0x%X, size: %s'
                        % (entry_type, entry_size))

                if has_content_section:
                    raw_entry_content = f.read(remaining_bytes)

                    # Convert encoding
                    entry_content = common.from_eva_sjis(raw_entry_content)

                    # Strip trailing \0's
                    entry_content = entry_content.rstrip('\0')

                else:
                    entry_content = None

                if not has_content_section and (entry_size != parameter_size):
                    raise Exception(
                        'Extra unannounced content in entry type: 0x%X, size: %s'
                        % (entry_type, entry_size))

                # Add entry
                self.entries.append(
                    (entry_type, entry_parameters, entry_content))
Exemplo n.º 7
0
 def open(self, file_path):
     with open(file_path, 'rb') as f:
         self.size = common.read_uint32(f)
         self.content = f.read()