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

#!/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))