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