You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
2.8 KiB
Python
89 lines
2.8 KiB
Python
#!/usr/bin/python3
|
|
|
|
import struct
|
|
import sys
|
|
|
|
SFO_MAGIC = b'\x00PSF'
|
|
SFO_HEADER_FORMAT = '<iiiii'
|
|
SFO_INDEX_FORMAT = '<HHLLL'
|
|
SFO_INDEX_STRUCT = struct.Struct(SFO_INDEX_FORMAT)
|
|
|
|
parameter_format_enum = {}
|
|
parameter_format_enum[int(0x0004)] = 'utf8-special'
|
|
parameter_format_enum[int(0x0204)] = 'utf8'
|
|
parameter_format_enum[int(0x0404)] = 'integer'
|
|
|
|
class SFOReadError(Exception):
|
|
'''This error is raised when an SFO file could not be read.'''
|
|
|
|
class SFO:
|
|
def __init__(self, filename):
|
|
self.data = {}
|
|
self._metadata = {}
|
|
with open(filename, mode='rb') as file_handle:
|
|
self._contents = file_handle.read()
|
|
|
|
self._read_header()
|
|
self._read_index()
|
|
|
|
def get_data(self, key):
|
|
if key.upper() in self.data.keys():
|
|
return self.data[key.upper()]
|
|
|
|
def _read_header(self):
|
|
header_raw = struct.unpack(SFO_HEADER_FORMAT, self._contents[0:20])
|
|
if header_raw[0] != int.from_bytes(SFO_MAGIC, 'little'):
|
|
raise SFOReadError('This is not an SFO file.')
|
|
|
|
self._metadata['key_table_offset'] = header_raw[2]
|
|
self._metadata['data_table_offset'] = header_raw[3]
|
|
self._metadata['entry_count'] = header_raw[4]
|
|
|
|
def _read_index(self):
|
|
self._metadata['data'] = []
|
|
index_slice = self._contents[20:self._metadata['key_table_offset']]
|
|
for index_raw in SFO_INDEX_STRUCT.iter_unpack(index_slice):
|
|
data = {}
|
|
data['key_table_offset'] = index_raw[0]
|
|
data['parameter_format'] = parameter_format_enum[index_raw[1]]
|
|
data['parameter_length'] = index_raw[2]
|
|
data['parameter_max_length'] = index_raw[3]
|
|
data['data_table_offset'] = index_raw[4]
|
|
|
|
key_start = self._metadata['key_table_offset'] + data['key_table_offset']
|
|
key = self._read_nt_string(key_start)
|
|
|
|
value_start = self._metadata['data_table_offset'] + data['data_table_offset']
|
|
value_end = value_start + data['parameter_length']
|
|
|
|
if data['parameter_format'] == 'utf8':
|
|
self.data[key] = self._contents[value_start:value_end-1].decode('utf8')
|
|
elif data['parameter_format'] == 'integer':
|
|
self.data[key] = int.from_bytes(self._contents[value_start:value_end], 'little')
|
|
|
|
self._metadata['data'].append(data)
|
|
|
|
def _read_nt_string(self, start_position):
|
|
char = ''
|
|
string = ''
|
|
i = start_position
|
|
|
|
while char != 0:
|
|
char = self._contents[i]
|
|
if char != 0:
|
|
string += chr(char)
|
|
i += 1
|
|
|
|
return string
|
|
|
|
sfo_file = sys.argv[1]
|
|
get_params = sys.argv[2:]
|
|
|
|
sfo = SFO(sfo_file)
|
|
|
|
values = []
|
|
for param in get_params:
|
|
values.append(sfo.get_data(param))
|
|
|
|
print(', '.join(values))
|