Last active
October 18, 2022 04:09
-
-
Save Andoryuuta/cab93882cd616ea519522b21d663a65f to your computer and use it in GitHub Desktop.
Dragon Quest X - SEDBRES parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import struct | |
import os | |
from pprint import pprint | |
# Terribly slow way of reading null-terminated strings. :) | |
def readcstr(f): | |
return ''.join(iter(lambda: f.read(1).decode('ascii'), '\x00')) | |
#with open('fa2271e63a2ba277.rps', 'rb') as f: | |
with open('0x1e157d10.sedbres', 'rb') as f: | |
# Check the magic header SEDBRES (Square Enix Data Base - Resources) | |
magic = f.read(8) | |
assert(magic == b'SEDBRES ') | |
print('File magic is valid (SEDBRES)') | |
# Read the header. | |
( format_version, | |
flags, | |
unk1, | |
sedbres_info_offset, # Absolute offset in file of the info struct. | |
file_size, | |
unk3, | |
unk4, | |
unk5 | |
) = struct.unpack('<IBBHIIII', f.read(24)) | |
# Read the info struct. | |
( path_table_count, | |
path_table_offset, | |
entry_count, | |
db_type | |
) = struct.unpack('<IIII', f.read(16)) | |
# Calculate the file_base_offset based on the alignment. | |
# The alignment changes depending on the format version. | |
entry_table_start = 0x30 # fixed. | |
entry_size = 16 | |
if format_version >= 4103: | |
alignment = 64 | |
elif format_version >= 4003: | |
alignment = 32 | |
else: | |
alignment = 1 | |
file_base_offset = (entry_table_start + (entry_size*entry_count) + alignment - 1) & ~(alignment - 1) | |
print(f"File format version: {format_version}") | |
print(f"Got file alignment: {alignment}") | |
print(f"File data base offset: {hex(file_base_offset)}") | |
# Read resource entries | |
resource_entries = [] | |
for i in range(entry_count): | |
(index, offset, size, flags) = struct.unpack('<IIII', f.read(16)) | |
entry = {'index': index, 'offset': offset, 'size': size, 'flags': flags}; | |
resource_entries.append(entry) | |
# Read the filename table | |
f.seek(file_base_offset + path_table_offset, os.SEEK_SET) | |
resource_names = [] | |
for i in range(entry_count): | |
s = readcstr(f) | |
resource_names.append(s) | |
# Load the RESOURCE_TYPE meta-resource, this is an actual | |
# resource within the resource table itself. | |
resource_type_idx = resource_names.index('RESOURCE_TYPE') | |
resource_type_entry = resource_entries[resource_type_idx] | |
f.seek(file_base_offset + resource_type_entry['offset']) | |
resource_types = [] | |
for i in range(entry_count): | |
res_type = f.read(4).decode('ascii')[::-1].strip('\x00') | |
resource_types.append(res_type) | |
# Load the RESOURCE_ID meta-resource, this is an actual | |
# resource within the resource table itself. | |
resource_id_idx = resource_names.index('RESOURCE_ID') | |
resource_id_entry = resource_entries[resource_id_idx] | |
f.seek(file_base_offset + resource_id_entry['offset']) | |
resource_ids = [] | |
for i in range(entry_count): | |
res_type = f.read(16).decode('ascii').strip('\x00') | |
resource_ids.append(res_type) | |
# Log all the entries. | |
for i in range(entry_count): | |
print(f'Name: {resource_names[i]}') | |
if i != resource_id_idx and i != resource_type_idx: | |
print(f'\tResource Type: {resource_types[i]}') | |
print(f'\tResource ID: {resource_ids[i]}') | |
entry = resource_entries[i] | |
print(f"\tEntry index: {entry['index']}") | |
print(f"\tEntry offset: {hex(file_base_offset + entry['offset'])}") | |
print(f"\tEntry size: {hex(entry['size'])}") | |
print(f"\tEntry flags: {entry['flags']}") | |
print('') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment