Skip to content

Instantly share code, notes, and snippets.

@Andoryuuta
Last active October 18, 2022 04:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Andoryuuta/cab93882cd616ea519522b21d663a65f to your computer and use it in GitHub Desktop.
Save Andoryuuta/cab93882cd616ea519522b21d663a65f to your computer and use it in GitHub Desktop.
Dragon Quest X - SEDBRES parser
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