#!/usr/bin/env python
'''

PS4 Module Loader by SocraticBliss (R)

Major Thanks to...
# aerosoul
# balika011
# Znullptr
# Pablo (kozarovv)
# ChendoChap
# xyz
# CelesteBlue
# kiwidogg
# motoharu
# noname120
# flatz
# Team Reswitched

Extra Special Thanks for telling me my program sucks...
# zecoxao

ps4_module.py: IDA loader for reading Sony PlayStation(R) 4 Module files

'''

from idaapi import *
from idc import *

import csv
import idaapi
import idc
import shutil
import struct

class Binary:

    __slots__ = ('EI_MAGIC', 'EI_CLASS', 'EI_DATA', 'EI_VERSION',
                 'EI_OSABI', 'EI_PADDING', 'EI_ABIVERSION', 'EI_SIZE',
                 'E_TYPE', 'E_MACHINE', 'E_VERSION', 'E_START_ADDR',
                 'E_PHT_OFFSET', 'E_SHT_OFFSET', 'E_FLAGS', 'E_SIZE',
                 'E_PHT_SIZE', 'E_PHT_COUNT', 'E_SHT_SIZE', 'E_SHT_COUNT',
                 'E_SHT_INDEX', 'E_SEGMENTS', 'E_SECTIONS')
    
    # Elf Types
    ET_NONE                   = 0x0
    ET_REL                    = 0x1
    ET_EXEC                   = 0x2
    ET_DYN                    = 0x3
    ET_CORE                   = 0x4
    ET_SCE_EXEC               = 0xFE00
    ET_SCE_REPLAY_EXEC        = 0xFE01
    ET_SCE_RELEXEC            = 0xFE04
    ET_SCE_STUBLIB            = 0xFE0C
    ET_SCE_DYNEXEC            = 0xFE10
    ET_SCE_DYNAMIC            = 0xFE18
    ET_LOPROC                 = 0xFF00
    ET_HIPROC                 = 0xFFFF
    
    # Elf Architecture
    EM_X86_64                 = 0x3E
    
    def __init__(self, f):
    
        f.seek(0)
        
        self.EI_MAGIC         = struct.unpack('4s', f.read(4))[0]
        self.EI_CLASS         = struct.unpack('<B', f.read(1))[0]
        self.EI_DATA          = struct.unpack('<B', f.read(1))[0]
        self.EI_VERSION       = struct.unpack('<B', f.read(1))[0]
        self.EI_OSABI         = struct.unpack('<B', f.read(1))[0]
        self.EI_ABIVERSION    = struct.unpack('<B', f.read(1))[0]
        self.EI_PADDING       = struct.unpack('6x', f.read(6))
        self.EI_SIZE          = struct.unpack('<B', f.read(1))[0]
        
        # Elf Properties
        self.E_TYPE           = struct.unpack('<H', f.read(2))[0]
        self.E_MACHINE        = struct.unpack('<H', f.read(2))[0]
        self.E_VERSION        = struct.unpack('<I', f.read(4))[0]
        self.E_START_ADDR     = struct.unpack('<Q', f.read(8))[0]
        self.E_PHT_OFFSET     = struct.unpack('<Q', f.read(8))[0]
        self.E_SHT_OFFSET     = struct.unpack('<Q', f.read(8))[0]
        self.E_FLAGS          = struct.unpack('<I', f.read(4))[0]
        self.E_SIZE           = struct.unpack('<H', f.read(2))[0]
        self.E_PHT_SIZE       = struct.unpack('<H', f.read(2))[0]
        self.E_PHT_COUNT      = struct.unpack('<H', f.read(2))[0]
        self.E_SHT_SIZE       = struct.unpack('<H', f.read(2))[0]
        self.E_SHT_COUNT      = struct.unpack('<H', f.read(2))[0]
        self.E_SHT_INDEX      = struct.unpack('<H', f.read(2))[0]
        
        # Prevent Other Binaries
        if self.E_MACHINE != Binary.EM_X86_64:
            return None
        
        f.seek(self.E_PHT_OFFSET)
        
        # Elf Program Header Table
        Binary.E_SEGMENTS = [Segment(f) for entry in xrange(self.E_PHT_COUNT)]
        
        f.seek(self.E_SHT_OFFSET)
        
        # Elf Section Header Table
        Binary.E_SECTIONS = [Section(f) for entry in xrange(self.E_SHT_COUNT)]
    
    def type(self):
    
        return {
            Binary.ET_NONE            : 'None',
            Binary.ET_REL             : 'Relocatable',
            Binary.ET_EXEC            : 'Executable',
            Binary.ET_DYN             : 'Shared Object',
            Binary.ET_CORE            : 'Core Dump',
            Binary.ET_SCE_EXEC        : 'Main Module',
            Binary.ET_SCE_REPLAY_EXEC : 'Replay Module',
            Binary.ET_SCE_RELEXEC     : 'Relocatable PRX',
            Binary.ET_SCE_STUBLIB     : 'SDK Stub Library',
            Binary.ET_SCE_DYNEXEC     : 'Main Module - ASLR',
            Binary.ET_SCE_DYNAMIC     : 'Shared Object PRX',
        }.get(self.E_TYPE, 'Missing Program Type!!!')
    
    def procomp(self, processor, pointer, til):
    
        # Processor Type
        idc.set_processor_type(processor, SETPROC_LOADER)
        
        # Compiler Attributes
        idc.set_inf_attr(INF_COMPILER, COMP_GNU)
        idc.set_inf_attr(INF_MODEL, pointer)
        idc.set_inf_attr(INF_SIZEOF_BOOL, 0x1)
        idc.set_inf_attr(INF_SIZEOF_LONG, 0x8)
        idc.set_inf_attr(INF_SIZEOF_LDBL, 0x10)
        
        # Type Library
        idc.add_default_til(til)
        
        # Loader Flags
        idc.set_inf_attr(INF_LFLAGS, LFLG_64BIT)
        
        # Assume GCC3 names
        idc.set_inf_attr(INF_DEMNAMES, DEMNAM_GCC3 | DEMNAM_NAME)
        
        # Analysis Flags 
        # (unchecked) Delete instructions with no xrefs
        # (unchecked) Coagulate data segments in the final pass
        idc.set_inf_attr(INF_AF, 0xDFFFFFDF)
        
        # Return Bitsize
        return self.EI_CLASS
    

class Segment:

    __slots__ = ('TYPE', 'FLAGS', 'OFFSET', 'MEM_ADDR',
                 'FILE_ADDR', 'FILE_SIZE', 'MEM_SIZE', 'ALIGNMENT')
    
    # Segment Types
    PT_NULL                = 0x0
    PT_LOAD                = 0x1
    PT_DYNAMIC             = 0x2
    PT_INTERP              = 0x3
    PT_NOTE                = 0x4
    PT_SHLIB               = 0x5
    PT_PHDR                = 0x6
    PT_TLS                 = 0x7
    PT_NUM                 = 0x8
    PT_SCE_DYNLIBDATA      = 0x61000000
    PT_SCE_PROCPARAM       = 0x61000001
    PT_SCE_MODULEPARAM     = 0x61000002
    PT_SCE_RELRO           = 0x61000010
    PT_GNU_EH_FRAME        = 0x6474E550
    PT_GNU_STACK           = 0x6474E551
    PT_SCE_COMMENT         = 0x6FFFFF00
    PT_SCE_LIBVERSION      = 0x6FFFFF01
    PT_HIOS                = 0x6FFFFFFF
    PT_LOPROC              = 0x70000000
    PT_SCE_SEGSYM          = 0x700000A8
    PT_HIPROC              = 0x7FFFFFFF
    
    # Segment Alignments
    AL_NONE                = 0x0
    AL_BYTE                = 0x1
    AL_WORD                = 0x2
    AL_DWORD               = 0x4
    AL_QWORD               = 0x8
    AL_PARA                = 0x10
    AL_4K                  = 0x4000
    
    def __init__(self, f):
    
        self.TYPE      = struct.unpack('<I', f.read(4))[0]
        self.FLAGS     = struct.unpack('<I', f.read(4))[0]
        self.OFFSET    = struct.unpack('<Q', f.read(8))[0]
        self.MEM_ADDR  = struct.unpack('<Q', f.read(8))[0]
        self.FILE_ADDR = struct.unpack('<Q', f.read(8))[0]
        self.FILE_SIZE = struct.unpack('<Q', f.read(8))[0]
        self.MEM_SIZE  = struct.unpack('<Q', f.read(8))[0]
        self.ALIGNMENT = struct.unpack('<Q', f.read(8))[0]
    
    def alignment(self):
    
        return {
            Segment.AL_NONE            : saAbs,
            Segment.AL_BYTE            : saRelByte,
            Segment.AL_WORD            : saRelWord,
            Segment.AL_DWORD           : saRelDble,
            Segment.AL_QWORD           : saRelQword,
            Segment.AL_PARA            : saRelPara,
            Segment.AL_4K              : saRel4K,
        }.get(self.ALIGNMENT, saRel_MAX_ALIGN_CODE)
    
    def flags(self):
    
        return self.FLAGS & 0xF
    
    def name(self):
    
        return {
            Segment.PT_NULL            : 'NULL',
            Segment.PT_LOAD            : 'CODE' if self.flags() == (SEGPERM_EXEC | SEGPERM_READ) else 'DATA',
            Segment.PT_DYNAMIC         : 'DYNAMIC',
            Segment.PT_INTERP          : 'INTERP',
            Segment.PT_NOTE            : 'NOTE',
            Segment.PT_SHLIB           : 'SHLIB',
            Segment.PT_PHDR            : 'PHDR',
            Segment.PT_TLS             : 'TLS',
            Segment.PT_NUM             : 'NUM',
            Segment.PT_SCE_DYNLIBDATA  : 'SCE_DYNLIBDATA',
            Segment.PT_SCE_PROCPARAM   : 'SCE_PROCPARAM',
            Segment.PT_SCE_MODULEPARAM : 'SCE_MODULEPARAM',
            Segment.PT_SCE_RELRO       : 'SCE_RELRO',
            Segment.PT_GNU_EH_FRAME    : 'GNU_EH_FRAME',
            Segment.PT_GNU_STACK       : 'GNU_STACK',
            Segment.PT_SCE_COMMENT     : 'SCE_COMMENT',
            Segment.PT_SCE_LIBVERSION  : 'SCE_LIBVERSION',
        }.get(self.TYPE, 'UNK')
    
    def struct(self, name, members, location = 0x0):
    
        if self.FLAGS > 7:
            return idc.get_struc_id(name)
        
        entry = idc.add_struc(BADADDR, name, False)
        
        for (member, comment, size) in members:
            flags = idaapi.get_flags_by_size(size)
            
            if member in ['addend', 'offset']:
                idc.add_struc_member(entry, member, location, flags + FF_0OFF, BADADDR, size, BADADDR, 0, REF_OFF64)
            else:
                idc.add_struc_member(entry, member, location, flags, BADADDR, size)
            
            idc.set_member_cmt(entry, location, comment, False)
            location += size
        
        return entry
    
    def type(self):
    
        return {
            Segment.PT_LOAD            : 'CODE' if self.flags() == (SEGPERM_EXEC | SEGPERM_READ) else 'DATA',
            Segment.PT_DYNAMIC         : 'DATA',
            Segment.PT_INTERP          : 'CONST',
            Segment.PT_NOTE            : 'CONST',
            Segment.PT_PHDR            : 'CODE',
            Segment.PT_TLS             : 'BSS',
            Segment.PT_SCE_DYNLIBDATA  : 'CONST',
            Segment.PT_SCE_PROCPARAM   : 'CONST',
            Segment.PT_SCE_MODULEPARAM : 'CONST',
            Segment.PT_SCE_RELRO       : 'DATA',
            Segment.PT_GNU_EH_FRAME    : 'CONST',
            Segment.PT_GNU_STACK       : 'DATA',
        }.get(self.TYPE, 'UNK')
    

class Section:
    
    __slots__ = ('NAME', 'TYPE', 'FLAGS', 'MEM_ADDR',
                 'OFFSET', 'FILE_SIZE', 'LINK', 'INFO',
                 'ALIGNMENT', 'FSE_SIZE')
    
    def __init__(self, f):
    
        self.NAME      = struct.unpack('<I', f.read(4))[0]
        self.TYPE      = struct.unpack('<I', f.read(4))[0]
        self.FLAGS     = struct.unpack('<Q', f.read(8))[0]
        self.MEM_ADDR  = struct.unpack('<Q', f.read(8))[0]
        self.OFFSET    = struct.unpack('<Q', f.read(8))[0]
        self.FILE_SIZE = struct.unpack('<Q', f.read(8))[0]
        self.LINK      = struct.unpack('<I', f.read(4))[0]
        self.INFO      = struct.unpack('<I', f.read(4))[0]
        self.ALIGNMENT = struct.unpack('<Q', f.read(8))[0]
        self.FSE_SIZE  = struct.unpack('<Q', f.read(8))[0]
    

class Dynamic:
    
    __slots__ = ('TAG', 'VALUE', 'ID', 'VERSION_MAJOR', 'VERSION_MINOR', 'INDEX')
    
    # Dynamic Tags
    (DT_NULL, DT_NEEDED, DT_PLTRELSZ, DT_PLTGOT, DT_HASH, DT_STRTAB, DT_SYMTAB,
    DT_RELA, DT_RELASZ, DT_RELAENT, DT_STRSZ, DT_SYMENT, DT_INIT, DT_FINI,
    DT_SONAME, DT_RPATH, DT_SYMBOLIC, DT_REL, DT_RELSZ, DT_RELENT, DT_PLTREL,
    DT_DEBUG, DT_TEXTREL, DT_JMPREL, DT_BIND_NOW, DT_INIT_ARRAY, DT_FINI_ARRAY,
    DT_INIT_ARRAYSZ, DT_FINI_ARRAYSZ, DT_RUNPATH, DT_FLAGS, DT_ENCODING, DT_PREINIT_ARRAY,
    DT_PREINIT_ARRAYSZ)         = xrange(0x22)
    DT_SCE_IDTABENTSZ           = 0x61000005
    DT_SCE_FINGERPRINT          = 0x61000007
    DT_SCE_ORIGINAL_FILENAME    = 0x61000009
    DT_SCE_MODULE_INFO          = 0x6100000D
    DT_SCE_NEEDED_MODULE        = 0x6100000F
    DT_SCE_MODULE_ATTR          = 0x61000011
    DT_SCE_EXPORT_LIB           = 0x61000013
    DT_SCE_IMPORT_LIB           = 0x61000015
    DT_SCE_EXPORT_LIB_ATTR      = 0x61000017
    DT_SCE_IMPORT_LIB_ATTR      = 0x61000019
    DT_SCE_STUB_MODULE_NAME     = 0x6100001D
    DT_SCE_STUB_MODULE_VERSION  = 0x6100001F
    DT_SCE_STUB_LIBRARY_NAME    = 0x61000021
    DT_SCE_STUB_LIBRARY_VERSION = 0x61000023
    DT_SCE_HASH                 = 0x61000025
    DT_SCE_PLTGOT               = 0x61000027
    DT_SCE_JMPREL               = 0x61000029
    DT_SCE_PLTREL               = 0x6100002B
    DT_SCE_PLTRELSZ             = 0x6100002D
    DT_SCE_RELA                 = 0x6100002F
    DT_SCE_RELASZ               = 0x61000031
    DT_SCE_RELAENT              = 0x61000033
    DT_SCE_STRTAB               = 0x61000035
    DT_SCE_STRSZ                = 0x61000037
    DT_SCE_SYMTAB               = 0x61000039
    DT_SCE_SYMENT               = 0x6100003B
    DT_SCE_HASHSZ               = 0x6100003D
    DT_SCE_SYMTABSZ             = 0x6100003F
    DT_SCE_HIOS                 = 0x6FFFF000
    DT_GNU_HASH                 = 0x6FFFFEF5
    DT_VERSYM                   = 0x6FFFFFF0
    DT_RELACOUNT                = 0x6FFFFFF9
    DT_RELCOUNT                 = 0x6FFFFFFA
    DT_FLAGS_1                  = 0x6FFFFFFB
    DT_VERDEF                   = 0x6FFFFFFC
    DT_VERDEFNUM                = 0x6FFFFFFD
    
    def __init__(self, f):
    
        self.TAG   = struct.unpack('<Q', f.read(8))[0]
        self.VALUE = struct.unpack('<Q', f.read(8))[0]
    
    def tag(self):
    
        return {
            Dynamic.DT_NULL                     : 'DT_NULL',
            Dynamic.DT_NEEDED                   : 'DT_NEEDED',
            Dynamic.DT_PLTRELSZ                 : 'DT_PLTRELSZ',
            Dynamic.DT_PLTGOT                   : 'DT_PLTGOT',
            Dynamic.DT_HASH                     : 'DT_HASH',
            Dynamic.DT_STRTAB                   : 'DT_STRTAB',
            Dynamic.DT_SYMTAB                   : 'DT_SYMTAB',
            Dynamic.DT_RELA                     : 'DT_RELA',
            Dynamic.DT_RELASZ                   : 'DT_RELASZ',
            Dynamic.DT_RELAENT                  : 'DT_RELAENT',
            Dynamic.DT_STRSZ                    : 'DT_STRSZ',
            Dynamic.DT_SYMENT                   : 'DT_SYMENT',
            Dynamic.DT_INIT                     : 'DT_INIT',
            Dynamic.DT_FINI                     : 'DT_FINI',
            Dynamic.DT_SONAME                   : 'DT_SONAME',
            Dynamic.DT_RPATH                    : 'DT_RPATH',
            Dynamic.DT_SYMBOLIC                 : 'DT_SYMBOLIC',
            Dynamic.DT_REL                      : 'DT_REL',
            Dynamic.DT_RELSZ                    : 'DT_RELSZ',
            Dynamic.DT_RELENT                   : 'DT_RELENT',
            Dynamic.DT_PLTREL                   : 'DT_PLTREL',
            Dynamic.DT_DEBUG                    : 'DT_DEBUG',
            Dynamic.DT_TEXTREL                  : 'DT_TEXTREL',
            Dynamic.DT_JMPREL                   : 'DT_JMPREL',
            Dynamic.DT_BIND_NOW                 : 'DT_BIND_NOW',
            Dynamic.DT_INIT_ARRAY               : 'DT_INIT_ARRAY',
            Dynamic.DT_FINI_ARRAY               : 'DT_FINI_ARRAY',
            Dynamic.DT_INIT_ARRAYSZ             : 'DT_INIT_ARRAYSZ',
            Dynamic.DT_FINI_ARRAYSZ             : 'DT_FINI_ARRAYSZ',
            Dynamic.DT_RUNPATH                  : 'DT_RUN_PATH',
            Dynamic.DT_FLAGS                    : 'DT_FLAGS',
            Dynamic.DT_ENCODING                 : 'DT_ENCODING',
            Dynamic.DT_PREINIT_ARRAY            : 'DT_PREINIT_ARRAY',
            Dynamic.DT_PREINIT_ARRAYSZ          : 'DT_PREINIT_ARRAYSZ',
            Dynamic.DT_SCE_IDTABENTSZ           : 'DT_SCE_IDTABENTSZ',
            Dynamic.DT_SCE_FINGERPRINT          : 'DT_SCE_FINGERPRINT',
            Dynamic.DT_SCE_ORIGINAL_FILENAME    : 'DT_SCE_ORIGINAL_FILENAME',
            Dynamic.DT_SCE_MODULE_INFO          : 'DT_SCE_MODULE_INFO',
            Dynamic.DT_SCE_NEEDED_MODULE        : 'DT_SCE_NEEDED_MODULE',
            Dynamic.DT_SCE_MODULE_ATTR          : 'DT_SCE_MODULE_ATTR',
            Dynamic.DT_SCE_EXPORT_LIB           : 'DT_SCE_EXPORT_LIB',
            Dynamic.DT_SCE_IMPORT_LIB           : 'DT_SCE_IMPORT_LIB',
            Dynamic.DT_SCE_EXPORT_LIB_ATTR      : 'DT_SCE_EXPORT_LIB_ATTR',
            Dynamic.DT_SCE_IMPORT_LIB_ATTR      : 'DT_SCE_IMPORT_LIB_ATTR',
            Dynamic.DT_SCE_STUB_MODULE_NAME     : 'DT_SCE_STUB_MODULE_NAME',
            Dynamic.DT_SCE_STUB_MODULE_VERSION  : 'DT_SCE_STUB_MODULE_VERSION',
            Dynamic.DT_SCE_STUB_LIBRARY_NAME    : 'DT_SCE_STUB_LIBRARY_NAME',
            Dynamic.DT_SCE_STUB_LIBRARY_VERSION : 'DT_SCE_STUB_LIBRARY_VERSION',
            Dynamic.DT_SCE_HASH                 : 'DT_SCE_HASH',
            Dynamic.DT_SCE_PLTGOT               : 'DT_SCE_PLTGOT',
            Dynamic.DT_SCE_JMPREL               : 'DT_SCE_JMPREL',
            Dynamic.DT_SCE_PLTREL               : 'DT_SCE_PLTREL',
            Dynamic.DT_SCE_PLTRELSZ             : 'DT_SCE_PLTRELSZ',
            Dynamic.DT_SCE_RELA                 : 'DT_SCE_RELA',
            Dynamic.DT_SCE_RELASZ               : 'DT_SCE_RELASZ',
            Dynamic.DT_SCE_RELAENT              : 'DT_SCE_RELAENT',
            Dynamic.DT_SCE_STRTAB               : 'DT_SCE_STRTAB',
            Dynamic.DT_SCE_STRSZ                : 'DT_SCE_STRSZ',
            Dynamic.DT_SCE_SYMTAB               : 'DT_SCE_SYMTAB',
            Dynamic.DT_SCE_SYMENT               : 'DT_SCE_SYMENT',
            Dynamic.DT_SCE_HASHSZ               : 'DT_SCE_HASHSZ',
            Dynamic.DT_SCE_SYMTABSZ             : 'DT_SCE_SYMTABSZ',
            Dynamic.DT_SCE_HIOS                 : 'DT_SCE_HIOS',
            Dynamic.DT_GNU_HASH                 : 'DT_GNU_HASH',
            Dynamic.DT_VERSYM                   : 'DT_VERSYM',
            Dynamic.DT_RELACOUNT                : 'DT_RELACOUNT',
            Dynamic.DT_RELCOUNT                 : 'DT_RELCOUNT',
            Dynamic.DT_FLAGS_1                  : 'DT_FLAGS_1',
            Dynamic.DT_VERDEF                   : 'DT_VERDEF',
            Dynamic.DT_VERDEFNUM                : 'DT_VERDEFNUM',
        }.get(self.TAG, 'Missing Dynamic Tag!!!')
    
    def lib_attribute(self):
    
        return {
            0x1  : 'AUTO_EXPORT',
            0x2  : 'WEAK_EXPORT',
            0x8  : 'LOOSE_IMPORT',
            0x9  : 'AUTO_EXPORT|LOOSE_IMPORT',
            0x10 : 'WEAK_EXPORT|LOOSE_IMPORT',
        }.get(self.INDEX, 'Missing Import Library Attribute!!!')
    
    def mod_attribute(self):
    
        return {
            0x0  : 'NONE',
            0x1  : 'SCE_CANT_STOP',
            0x2  : 'SCE_EXCLUSIVE_LOAD',
            0x4  : 'SCE_EXCLUSIVE_START',
            0x8  : 'SCE_CAN_RESTART',
            0x10 : 'SCE_CAN_RELOCATE',
            0x20 : 'SCE_CANT_SHARE',
        }.get(self.INDEX, 'Missing Module Attribute!!!')
    
    def comment(self, address, stubs, modules, libraries):
    
        if self.TAG in [Dynamic.DT_NEEDED, Dynamic.DT_SONAME]:
            return '%s | %s' % (self.tag(), stubs[self.VALUE])
        elif self.TAG == Dynamic.DT_SCE_HASH:
            return '%s | %#x' % (self.tag(), address + Dynamic.HASHTAB)
        elif self.TAG == Dynamic.DT_SCE_STRTAB:
            address += Dynamic.STRTAB
            idc.add_entry(address, address, '.dynstr', False)
            return '%s | %#x' % (self.tag(), address)
        elif self.TAG == Dynamic.DT_SCE_SYMTAB:
            address += Dynamic.SYMTAB
            idc.add_entry(address, address, '.dynsym', False)
            return '%s | %#x' % (self.tag(), address)
        elif self.TAG == Dynamic.DT_SCE_JMPREL:
            return '%s | %#x' % (self.tag(), address + Dynamic.JMPTAB)
        elif self.TAG == Dynamic.DT_SCE_RELA:
            return '%s | %#x' % (self.tag(), address + Dynamic.RELATAB)
        elif self.TAG in [Dynamic.DT_SCE_NEEDED_MODULE, Dynamic.DT_SCE_IMPORT_LIB,
                          Dynamic.DT_SCE_IMPORT_LIB_ATTR, Dynamic.DT_SCE_EXPORT_LIB,
                          Dynamic.DT_SCE_EXPORT_LIB_ATTR, Dynamic.DT_SCE_MODULE_INFO,
                          Dynamic.DT_SCE_MODULE_ATTR, Dynamic.DT_SCE_ORIGINAL_FILENAME]:
            self.ID             = self.VALUE >> 48
            self.VERSION_MINOR  = (self.VALUE >> 40) & 0xF
            self.VERSION_MAJOR  = (self.VALUE >> 32) & 0xF
            self.INDEX          = self.VALUE & 0xFFF
            
            if self.TAG in [Dynamic.DT_SCE_NEEDED_MODULE, Dynamic.DT_SCE_MODULE_INFO]:
                return '%s | MID:%#x Version:%i.%i Name:%s' % \
                       (self.tag(), self.ID, self.VERSION_MAJOR, self.VERSION_MINOR, modules[self.INDEX])
            elif self.TAG in [Dynamic.DT_SCE_IMPORT_LIB, Dynamic.DT_SCE_EXPORT_LIB]:
                return '%s | LID:%#x Version:%i Name:%s' % \
                       (self.tag(), self.ID, self.VERSION_MAJOR, libraries[self.INDEX])
            elif self.TAG == Dynamic.DT_SCE_MODULE_ATTR:
                return '%s | %s' % (self.tag(), self.mod_attribute())
            elif self.TAG in [Dynamic.DT_SCE_IMPORT_LIB_ATTR, Dynamic.DT_SCE_EXPORT_LIB_ATTR]:
                return '%s | LID:%#x Attributes:%s' % \
                       (self.tag(), self.ID, self.lib_attribute())
            elif self.TAG == Dynamic.DT_SCE_ORIGINAL_FILENAME:
                return '%s | %s' % (self.tag(), stubs[self.VALUE])
        
        return '%s | %#x' % (self.tag(), self.VALUE)
    
    def process(self, stubs, modules, libraries):
    
        if self.TAG == Dynamic.DT_INIT:
            Dynamic.INIT = self.VALUE
            idc.add_entry(Dynamic.INIT, Dynamic.INIT, '.init', True)
        elif self.TAG == Dynamic.DT_FINI:
            Dynamic.FINI = self.VALUE
            idc.add_entry(Dynamic.FINI, Dynamic.FINI, '.fini', True)
        elif self.TAG in [Dynamic.DT_NEEDED, Dynamic.DT_SONAME]:
            stubs[self.VALUE] = 0
        elif self.TAG == Dynamic.DT_SCE_STRTAB:
            Dynamic.STRTAB = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_STRSZ:
            Dynamic.STRTABSZ = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_SYMTAB:
            Dynamic.SYMTAB = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_SYMTABSZ:
            Dynamic.SYMTABSZ = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_JMPREL:
            Dynamic.JMPTAB = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_PLTRELSZ:
            Dynamic.JMPTABSZ = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_PLTREL:
            if self.VALUE == 0x7:
                return '%s | %#x | DT_RELA' % (self.tag(), self.VALUE)
        elif self.TAG == Dynamic.DT_SCE_RELA:
            Dynamic.RELATAB = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_RELASZ:
            Dynamic.RELATABSZ = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_HASH:
            Dynamic.HASHTAB = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_HASHSZ:
            Dynamic.HASHTABSZ = self.VALUE
        elif self.TAG == Dynamic.DT_SCE_PLTGOT:
            Dynamic.GOT = self.VALUE
            idc.add_entry(Dynamic.GOT, Dynamic.GOT, '.got.plt', False)
        elif self.TAG in [Dynamic.DT_SCE_NEEDED_MODULE, Dynamic.DT_SCE_IMPORT_LIB,
                          Dynamic.DT_SCE_IMPORT_LIB_ATTR, Dynamic.DT_SCE_EXPORT_LIB,
                          Dynamic.DT_SCE_EXPORT_LIB_ATTR, Dynamic.DT_SCE_MODULE_INFO,
                          Dynamic.DT_SCE_MODULE_ATTR, Dynamic.DT_SCE_ORIGINAL_FILENAME]:
            self.ID             = self.VALUE >> 48
            self.VERSION_MINOR  = (self.VALUE >> 40) & 0xF
            self.VERSION_MAJOR  = (self.VALUE >> 32) & 0xF
            self.INDEX          = self.VALUE & 0xFFF
            
            if self.TAG in [Dynamic.DT_SCE_NEEDED_MODULE, Dynamic.DT_SCE_MODULE_INFO]:
                if self.INDEX not in modules:
                    modules[self.INDEX] = 0
                return '%s | MID:%#x Version:%i.%i | %#x' % \
                       (self.tag(), self.ID, self.VERSION_MAJOR, self.VERSION_MINOR, self.INDEX)
            elif self.TAG in [Dynamic.DT_SCE_IMPORT_LIB, Dynamic.DT_SCE_EXPORT_LIB]:
                if self.INDEX not in libraries:
                    libraries[self.INDEX] = self.ID
                return '%s | LID:%#x Version:%i | %#x' % \
                       (self.tag(), self.ID, self.VERSION_MAJOR, self.INDEX)
            elif self.TAG == Dynamic.DT_SCE_MODULE_ATTR:
                return '%s | %s' % (self.tag(), self.mod_attribute())
            elif self.TAG in [Dynamic.DT_SCE_IMPORT_LIB_ATTR, Dynamic.DT_SCE_EXPORT_LIB_ATTR]:
                return '%s | LID:%#x Attributes:%s' % \
                       (self.tag(), self.ID, self.lib_attribute())
            elif self.TAG == Dynamic.DT_SCE_ORIGINAL_FILENAME:
                stubs[self.INDEX] = 0
        
        return '%s | %#x' % (self.tag(), self.VALUE)
    

class Relocation:

    __slots__ = ('OFFSET', 'INDEX', 'INFO', 'ADDEND')
    
    # PS4 (X86_64) Relocation Codes (40)
    (R_X86_64_NONE, R_X86_64_64, R_X86_64_PC32, R_X86_64_GOT32,
    R_X86_64_PLT32, R_X86_64_COPY, R_X86_64_GLOB_DAT, R_X86_64_JUMP_SLOT,
    R_X86_64_RELATIVE, R_X86_64_GOTPCREL, R_X86_64_32, R_X86_64_32S,
    R_X86_64_16, R_X86_64_PC16, R_X86_64_8, R_X86_64_PC8, R_X86_64_DTPMOD64,
    R_X86_64_DTPOFF64, R_X86_64_TPOFF64, R_X86_64_TLSGD, R_X86_64_TLSLD,
    R_X86_64_DTPOFF32, R_X86_64_GOTTPOFF, R_X86_64_TPOFF32, R_X86_64_PC64,
    R_X86_64_GOTOFF64, R_X86_64_GOTPC32, R_X86_64_GOT64, R_X86_64_GOTPCREL64,
    R_X86_64_GOTPC64, R_X86_64_GOTPLT64, R_X86_64_PLTOFF64, R_X86_64_SIZE32,
    R_X86_64_SIZE64, R_X86_64_GOTPC32_TLSDESC, R_X86_64_TLSDESC_CALL, R_X86_64_TLSDESC,
    R_X86_64_IRELATIVE, R_X86_64_RELATIVE64) = xrange(0x27)
    R_X86_64_ORBIS_GOTPCREL_LOAD             = 0x28 
    
    def __init__(self, f):
    
        self.OFFSET = struct.unpack('<Q', f.read(8))[0]
        self.INFO   = struct.unpack('<Q', f.read(8))[0]
        self.ADDEND = struct.unpack('<Q', f.read(8))[0]
    
    def type(self):
    
        return {
            Relocation.R_X86_64_NONE                : 'R_X86_64_NONE',
            Relocation.R_X86_64_64                  : 'R_X86_64_64',
            Relocation.R_X86_64_PC32                : 'R_X86_64_PC32',
            Relocation.R_X86_64_GOT32               : 'R_X86_64_GOT32',
            Relocation.R_X86_64_PLT32               : 'R_X86_64_PLT32',
            Relocation.R_X86_64_COPY                : 'R_X86_64_COPY',
            Relocation.R_X86_64_GLOB_DAT            : 'R_X86_64_GLOB_DAT',
            Relocation.R_X86_64_JUMP_SLOT           : 'R_X86_64_JUMP_SLOT',
            Relocation.R_X86_64_RELATIVE            : 'R_X86_64_RELATIVE',
            Relocation.R_X86_64_GOTPCREL            : 'R_X86_64_GOTPCREL',
            Relocation.R_X86_64_32                  : 'R_X86_64_32',
            Relocation.R_X86_64_32S                 : 'R_X86_64_32S',
            Relocation.R_X86_64_16                  : 'R_X86_64_16',
            Relocation.R_X86_64_PC16                : 'R_X86_64_PC16',
            Relocation.R_X86_64_8                   : 'R_X86_64_8',
            Relocation.R_X86_64_PC8                 : 'R_X86_64_PC8',
            Relocation.R_X86_64_DTPMOD64            : 'R_X86_64_DTPMOD64',
            Relocation.R_X86_64_DTPOFF64            : 'R_X86_64_DTPOFF64',
            Relocation.R_X86_64_TPOFF64             : 'R_X86_64_TPOFF64',
            Relocation.R_X86_64_TLSGD               : 'R_X86_64_TLSGD',
            Relocation.R_X86_64_TLSLD               : 'R_X86_64_TLSLD',
            Relocation.R_X86_64_DTPOFF32            : 'R_X86_64_DTPOFF32',
            Relocation.R_X86_64_GOTTPOFF            : 'R_X86_64_GOTTPOFF',
            Relocation.R_X86_64_TPOFF32             : 'R_X86_64_TPOFF32',
            Relocation.R_X86_64_PC64                : 'R_X86_64_PC64',
            Relocation.R_X86_64_GOTOFF64            : 'R_X86_64_GOTOFF64',
            Relocation.R_X86_64_GOTPC32             : 'R_X86_64_GOTPC32',
            Relocation.R_X86_64_GOT64               : 'R_X86_64_GOT64',
            Relocation.R_X86_64_GOTPCREL64          : 'R_X86_64_GOTPCREL64',
            Relocation.R_X86_64_GOTPC64             : 'R_X86_64_GOTPC64',
            Relocation.R_X86_64_GOTPLT64            : 'R_X86_64_GOTPLT64',
            Relocation.R_X86_64_PLTOFF64            : 'R_X86_64_PLTOFF64',
            Relocation.R_X86_64_SIZE32              : 'R_X86_64_SIZE32',
            Relocation.R_X86_64_SIZE64              : 'R_X86_64_SIZE64',
            Relocation.R_X86_64_GOTPC32_TLSDESC     : 'R_X86_64_GOTPC32_TLSDESC',
            Relocation.R_X86_64_TLSDESC_CALL        : 'R_X86_64_TLSDESC_CALL',
            Relocation.R_X86_64_TLSDESC             : 'R_X86_64_TLSDESC',
            Relocation.R_X86_64_IRELATIVE           : 'R_X86_64_IRELATIVE',
            Relocation.R_X86_64_RELATIVE64          : 'R_X86_64_RELATIVE64',
            Relocation.R_X86_64_ORBIS_GOTPCREL_LOAD : 'R_X86_64_ORBIS_GOTPCREL_LOAD',
        }.get(self.INFO, 'Missing PS4 Relocation Type!!!')
    
    def process(self, nids, symbols):
    
        if self.INFO > Relocation.R_X86_64_ORBIS_GOTPCREL_LOAD:
            self.INDEX = self.INFO >> 32
            self.INFO &= 0xFF
            
            # Symbol Value + AddEnd (S + A)
            if self.type() == 'R_X86_64_64':
                self.INDEX += self.ADDEND
            
            if self.type() != 'R_X86_64_DTPMOD64':
                symbol = next(value for key, value in enumerate(symbols) if key + 2 == self.INDEX)[1]
        
        # String (Offset) == Base + AddEnd (B + A)
        if self.type() == 'R_X86_64_RELATIVE':
            idaapi.put_qword(self.OFFSET, self.ADDEND)
            idaapi.create_data(self.OFFSET, FF_QWORD, 0x8, BADNODE)
        
        # TLS Object
        elif self.type() in ['R_X86_64_DTPMOD64', 'R_X86_64_DTPOFF64']:
            idc.set_name(self.OFFSET, 'tls_access_struct', SN_NOCHECK | SN_NOWARN | SN_FORCE)
        
        # Object
        else:
            # Resolve the NID...
            idc.set_cmt(self.OFFSET, 'NID: ' + symbol, False)
            object = nids.get(symbol[:11], symbol)
            
            # Rename the Object...
            idc.set_name(self.OFFSET, object, SN_NOCHECK | SN_NOWARN | SN_FORCE)
            idaapi.create_data(self.OFFSET, FF_QWORD, 0x8, BADNODE)
        
        return self.type()
    
    def resolve(self, alphabet, nids, symbols, libraries):
    
        if self.INFO > Relocation.R_X86_64_ORBIS_GOTPCREL_LOAD:
            self.INDEX = self.INFO >> 32
            self.INFO &= 0xFF
            symbol = next(value for key, value in enumerate(symbols) if key + 2 == self.INDEX)[1]
        
        # Library
        try:
            lid1 = alphabet[symbol[12:13]]
            
            # [base64]#
            if symbol[13:14] == '#':
                library = libraries[lid1]
            
            # [base64][base64]#
            elif symbol[14:15] == '#':
                lid2 = alphabet[symbol[13:14]]
                library = libraries[lid1 + lid2]
            
            else:
                raise
        
        # Not a NID
        except:
            library = ''
        
        # Function Name (Offset) == Symbol Value + AddEnd (S + A)
        # Library Name  (Offset) == Symbol Value (S)
        real = idc.get_qword(self.OFFSET)
        idc.add_func(real)
        
        # Hacky way to determine if this is the real function...
        real -= 0x6 if idc.print_insn_mnem(real) == 'push' else 0x0
        
        # Resolve the NID...
        idc.set_cmt(real, 'NID: ' + symbol, False)
        function = nids.get(symbol[:11], symbol)
        
        # Rename the Jump Function...
        idc.set_name(self.OFFSET, '__imp_' + function, SN_NOCHECK | SN_NOWARN | SN_FORCE)
        
        # Rename the Real Function...
        idc.set_name(real, function, SN_NOCHECK | SN_NOWARN | SN_FORCE)
        
        try:
            import_node = idaapi.netnode(library, 0, True)
            import_node.supset(ea2node(real), function)
        
            # Requires customized loader.i / ida_loader.py(d)
            idaapi.import_module(library, None, import_node.index(), None, 'linux')
        
        except:
            pass
        
        return self.type()
    

class Symbol:

    __slots__ = ('NAME', 'INFO', 'OTHER', 'INDEX', 'VALUE', 'SIZE')
    
    # Symbol Information
    ST_LOCAL_NONE      = 0x0
    ST_LOCAL_OBJECT    = 0x1
    ST_LOCAL_FUNCTION  = 0x2
    ST_LOCAL_SECTION   = 0x3
    ST_LOCAL_FILE      = 0x4
    ST_LOCAL_COMMON    = 0x5
    ST_LOCAL_TLS       = 0x6
    ST_GLOBAL_NONE     = 0x10
    ST_GLOBAL_OBJECT   = 0x11
    ST_GLOBAL_FUNCTION = 0x12
    ST_GLOBAL_SECTION  = 0x13
    ST_GLOBAL_FILE     = 0x14
    ST_GLOBAL_COMMON   = 0x15
    ST_GLOBAL_TLS      = 0x16
    ST_WEAK_NONE       = 0x20
    ST_WEAK_OBJECT     = 0x21
    ST_WEAK_FUNCTION   = 0x22
    ST_WEAK_SECTION    = 0x23
    ST_WEAK_FILE       = 0x24
    ST_WEAK_COMMON     = 0x25
    ST_WEAK_TLS        = 0x26
    
    def __init__(self, f):
    
        self.NAME      = struct.unpack('<I', f.read(4))[0]
        self.INFO      = struct.unpack('<B', f.read(1))[0]
        self.OTHER     = struct.unpack('<B', f.read(1))[0]
        self.INDEX     = struct.unpack('<H', f.read(2))[0]
        self.VALUE     = struct.unpack('<Q', f.read(8))[0]
        self.SIZE      = struct.unpack('<Q', f.read(8))[0]
    
    def info(self):
    
        return {
            Symbol.ST_LOCAL_NONE      : 'Local : None',
            Symbol.ST_LOCAL_OBJECT    : 'Local : Object',
            Symbol.ST_LOCAL_FUNCTION  : 'Local : Function',
            Symbol.ST_LOCAL_SECTION   : 'Local : Section',
            Symbol.ST_LOCAL_FILE      : 'Local : File',
            Symbol.ST_LOCAL_COMMON    : 'Local : Common',
            Symbol.ST_LOCAL_TLS       : 'Local : TLS',
            Symbol.ST_GLOBAL_NONE     : 'Global : None',
            Symbol.ST_GLOBAL_OBJECT   : 'Global : Object',
            Symbol.ST_GLOBAL_FUNCTION : 'Global : Function',
            Symbol.ST_GLOBAL_SECTION  : 'Global : Section',
            Symbol.ST_GLOBAL_FILE     : 'Global : File',
            Symbol.ST_GLOBAL_COMMON   : 'Global : Common',
            Symbol.ST_GLOBAL_TLS      : 'Global : TLS',
            Symbol.ST_WEAK_NONE       : 'Weak : None',
            Symbol.ST_WEAK_OBJECT     : 'Weak : Object',
            Symbol.ST_WEAK_FUNCTION   : 'Weak : Function',
            Symbol.ST_WEAK_SECTION    : 'Weak : Section',
            Symbol.ST_WEAK_FILE       : 'Weak : File',
            Symbol.ST_WEAK_COMMON     : 'Weak : Common',
            Symbol.ST_WEAK_TLS        : 'Weak : TLS',
        }.get(self.INFO, 'Missing Symbol Information!!!')
    
    def process(self, symbols):
    
        if self.NAME != 0:
            symbols[self.NAME] = 0
        
        return self.info()
    
    def resolve(self, address, nids, symbol):
    
        # Resolve the NID...
        idc.set_cmt(self.VALUE, 'NID: ' + symbol, False)
        function = nids.get(symbol[:11], symbol)
        
        #print('Function: %s | number: %s' % (function, idaapi.get_func_num(self.VALUE)))
        if idaapi.get_func_num(self.VALUE) > 0:
            idc.del_func(self.VALUE)
        
        if self.VALUE > 0:
            idc.add_func(self.VALUE)
            idc.add_entry(self.VALUE, self.VALUE, function, True)
            idc.set_name(self.VALUE, function, SN_NOCHECK | SN_NOWARN | SN_FORCE)
            idc.set_cmt(address, '%s | %s' % (function, self.info()), False)
        
    

# PROGRAM START

# Open File Dialog...
def accept_file(f, filename):

    ps4 = Binary(f)
    
    # No Kernels
    if ps4.E_MACHINE == Binary.EM_X86_64 and ps4.E_START_ADDR < 0xFFFFFFFF82200000:
        return { 'format': 'PS4 - ' + ps4.type(),
                 'options': ACCEPT_FIRST }
    return 0

# Load NID Library...
def load_nids(location, nids = {}):

    try:
        with open(location) as database:
            nids = dict(row for row in csv.reader(database, delimiter=' '))
    
    except IOError:
        retry = idaapi.ask_file(0, 'aerolib.csv|*.csv|All files (*.*)|*.*', 'Please gimme your aerolib.csv file')
        
        if retry != None:
            try:
                with open(retry) as database:
                    nids = dict(row for row in csv.reader(database, delimiter=' '))
                
                shutil.copy2(retry, location)
                
            except:
                print('Ok, no NIDs for you!')
        else:
            print('Ok, no NIDs for you!')
    
    return nids

# Pablo's IDC
def pablo(mode, address, end, search):

    while address < end:
        address = idaapi.find_binary(address, end, search, 0x10, SEARCH_DOWN)
        
        if address > idaapi.get_segm_by_name('CODE').end_ea:
            offset = address - 0x3
            
            if idaapi.isUnknown(idaapi.getFlags(offset)):
                if idaapi.get_qword(offset) <= end:
                    idaapi.create_data(offset, FF_QWORD, 0x8, BADNODE)
            
            address = offset + 0x4
        
        else:
            address += mode
            idaapi.do_unknown(address, 0)
            idaapi.create_insn(address)
            idaapi.add_func(address, BADADDR)
            address += 0x1

# Load Input Binary...
def load_file(f, neflags, format):

    print('# PS4 Module Loader')
    ps4 = Binary(f)
    
    # PS4 Processor, Compiler, Library
    bitness = ps4.procomp('metapc', CM_N64 | CM_M_NN | CM_CC_FASTCALL, 'ps4_errno_700')
    
    # Load Aerolib...
    nids = load_nids(idc.idadir() + '/loaders/aerolib.csv')
    
    # Segment Loading...
    for segm in ps4.E_SEGMENTS:
    
        # Process Loadable Segments...
        if segm.name() in ['CODE', 'DATA', 'SCE_RELRO', 'DYNAMIC', 'GNU_EH_FRAME', 'SCE_DYNLIBDATA']:
        
            address = segm.MEM_ADDR if segm.name() not in ['DYNAMIC', 'SCE_DYNLIBDATA'] else segm.OFFSET + 0x1000000
            size    = segm.MEM_SIZE if segm.name() not in ['DYNAMIC', 'SCE_DYNLIBDATA'] else segm.FILE_SIZE
            
            print('# Processing %s Segment...' % segm.name())
            f.file2base(segm.OFFSET, address, address + segm.FILE_SIZE, FILEREG_PATCHABLE)
            
            if segm.name() not in ['DYNAMIC', 'GNU_EH_FRAME']:
                
                idaapi.add_segm(0, address, address + size, segm.name(), segm.type(), ADDSEG_NOTRUNC | ADDSEG_FILLGAP)
                
                # Processor Specific Segment Details
                idc.set_segm_addressing(address, bitness)
                idc.set_segm_alignment(address, segm.alignment())
                idc.set_segm_attr(address, SEGATTR_PERM, segm.flags())
            
            # Process Dynamic Segment....
            elif segm.name() == 'DYNAMIC':
            
                stubs = {}
                modules = {}
                libraries = {}
                f.seek(segm.OFFSET)
                
                offset = segm.OFFSET
                dynamic = address
                dynamicsize = size
                
                for entry in xrange(size / 0x10):
                    idc.set_cmt(address + (entry * 0x10), Dynamic(f).process(stubs, modules, libraries), False)
            
            '''
            # Process Exception Handling Segment...
            elif segm.name() == 'GNU_EH_FRAME':
                
                # Exception Handling Frame Header Structure
                members = [('version', 'Version', 0x1),
                           ('eh_frame_ptr_enc', 'Encoding of Exception Handling Frame Pointer', 0x1),
                           ('fde_count_enc', 'Encoding of Frame Description Entry Count', 0x1),
                           ('table_enc', 'Encoding of Table Entries', 0x1)]
                struct = segm.struct('EHFrame', members)
                
                idaapi.create_struct(address, 0x4, struct)
                
                # Exception Handling Structure
                members = [('exception', 'value', 0x8)]
                struct = segm.struct('Exception', members)
                
                for entry in xrange(size / 0x8):
                    idaapi.create_struct(address + (entry * 0x8), 0x8, struct)
            '''
        
        # Process SCE 'Special' Shared Object Segment...
        if segm.name() == 'SCE_DYNLIBDATA':
        
            # SCE Fingerprint
            idc.make_array(address, 0x14)
            idc.set_name(address, 'SCE_FINGERPRINT', SN_NOCHECK | SN_NOWARN | SN_FORCE)
            idc.set_cmt(address, ' '.join(x.encode('hex') for x in idc.get_bytes(address, 0x14)).upper(), False)
            
            # Dynamic Symbol Table
            try:
                # --------------------------------------------------------------------------------------------------------
                # Dynamic Symbol Entry Structure
                members = [('name', 'Name (String Index)', 0x4),
                           ('info', 'Info (Binding : Type)', 0x1),
                           ('other', 'Other', 0x1),
                           ('shtndx', 'Section Index', 0x2),
                           ('value', 'Value', 0x8),
                           ('size', 'Size', 0x8)]
                struct = segm.struct('Symbol', members)
                
                # Dynamic Symbol Table
                location = address + Dynamic.SYMTAB
                f.seek(segm.OFFSET + Dynamic.SYMTAB)
                symbols = {}
                
                for entry in xrange(Dynamic.SYMTABSZ / 0x18):
                    idaapi.create_struct(location + (entry * 0x18), 0x18, struct)
                    idc.set_cmt(location + (entry * 0x18), Symbol(f).process(symbols), False)
            
            except:
                pass
            
            # Dynamic String Table
            try:
                # --------------------------------------------------------------------------------------------------------
                # Dynamic String Table
                location = address + Dynamic.STRTAB
                f.seek(segm.OFFSET + Dynamic.STRTAB)
                
                # Stubs
                for key in stubs:
                    idc.create_strlit(location + key, BADADDR)
                    stubs[key] = idc.get_strlit_contents(location + key, BADADDR)
                    idc.set_cmt(location + key, 'Stub', False)
                
                #print('Stubs: %s' % stubs)
                
                # Modules
                for key in modules:
                    idc.create_strlit(location + key, BADADDR)
                    modules[key] = idc.get_strlit_contents(location + key, BADADDR)
                    idc.set_cmt(location + key, 'Module', False)
                
                #print('Modules: %s' % modules)
                
                # Libraries and LIDs
                lids = {}
                for key, value in libraries.iteritems():
                    idc.create_strlit(location + key, BADADDR)
                    lids[value] = idc.get_strlit_contents(location + key, BADADDR)
                    libraries[key] = idc.get_strlit_contents(location + key, BADADDR)
                    idc.set_cmt(location + key, 'Library', False)
                
                #print('LIDs: %s' % lids)
                
                # Symbols
                for key in symbols:
                    idc.create_strlit(location + key, BADADDR)
                    symbols[key] = idc.get_strlit_contents(location + key, BADADDR)
                    idc.set_cmt(location + key, 'Symbol', False)
                
                #print('Symbols: %s' % symbols)
            
            except:
                pass
            
            # Resolve Export Symbols
            try:
                symbols = sorted(symbols.iteritems())
                location = address + Dynamic.SYMTAB + 0x30
                f.seek(segm.OFFSET + Dynamic.SYMTAB + 0x30)
                
                for entry in xrange((Dynamic.SYMTABSZ - 0x30) / 0x18):
                    Symbol(f).resolve(location + (entry * 0x18), nids, symbols[entry][1])
            
            except:
                pass
            
            # Jump Table
            try:                
                # --------------------------------------------------------------------------------------------------------
                # Jump Entry Structure
                members = [('offset', 'Offset (String Index)', 0x8),
                           ('info', 'Info (Symbol Index : Relocation Code)', 0x8),
                           ('addend', 'AddEnd', 0x8)]
                struct = segm.struct('Jump', members)
                
                # PS4 Base64 Alphabet
                base64 = list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-')
                alphabet = { character:index for index, character in enumerate(base64) }
                #print('Base64 Table: %s' % alphabet)
                
                # Jump Table
                location = address + Dynamic.JMPTAB
                f.seek(segm.OFFSET + Dynamic.JMPTAB)
                
                for entry in xrange(Dynamic.JMPTABSZ / 0x18):
                    idaapi.create_struct(location + (entry * 0x18), 0x18, struct)
                    idc.set_cmt(location + (entry * 0x18), Relocation(f).resolve(alphabet, nids, symbols, lids), False)
            
            except:
                pass
            
            # Relocation Table
            try:
                # --------------------------------------------------------------------------------------------------------
                # Relocation Entry Structure (with specific addends)
                members = [('offset', 'Offset (String Index)', 0x8),
                           ('info', 'Info (Symbol Index : Relocation Code)', 0x8),
                           ('addend', 'AddEnd', 0x8)]
                struct = segm.struct('Relocation', members)
                
                # Relocation Table (with specific addends)
                location = address + Dynamic.RELATAB
                f.seek(segm.OFFSET + Dynamic.RELATAB)
                
                for entry in xrange(Dynamic.RELATABSZ / 0x18):
                    idaapi.create_struct(location + (entry * 0x18), 0x18, struct)
                    idc.set_cmt(location + (entry * 0x18), Relocation(f).process(nids, symbols), False)
            
            except:
                pass
            
            # Hash Table
            try:
                # --------------------------------------------------------------------------------------------------------
                # Hash Entry Structure
                members = [('bucket', 'Bucket', 0x2),
                           ('chain', 'Chain', 0x2),
                           ('buckets', 'Buckets', 0x2),
                           ('chains', 'Chains', 0x2)]
                struct = segm.struct('Hash', members)
                
                # Hash Table
                location = address + Dynamic.HASHTAB
                f.seek(segm.OFFSET + Dynamic.HASHTAB)
                
                for entry in xrange(Dynamic.HASHTABSZ / 0x8):
                    idaapi.create_struct(location + (entry * 0x8), 0x8, struct)
            
            except:
                pass
            
            # Dynamic Tag Table
            try:
                # --------------------------------------------------------------------------------------------------------
                # Dynamic Tag Entry Structure
                members = [('tag', 'Tag', 0x8),
                           ('value', 'Value', 0x8)]
                struct = segm.struct('Tag', members)
                
                f.seek(offset)
                
                for entry in xrange(dynamicsize / 0x10):
                    idaapi.create_struct(dynamic + (entry * 0x10), 0x10, struct)
                    idc.set_cmt(dynamic + (entry * 0x10), Dynamic(f).comment(address, stubs, modules, libraries), False)
            
            except:
                pass
            
    
    code = idaapi.get_segm_by_name('CODE')
    
    # Start Function
    idc.add_entry(ps4.E_START_ADDR, ps4.E_START_ADDR, 'start', True)
    
    # Set No Return for __stack_chk_fail...
    try:
        function = idc.get_name_ea_simple('__stack_chk_fail')
        function = idaapi.get_func(function)
        function.flags |= FUNC_NORET
        idaapi.update_func(function)
    
    except:
        pass
    
    # --------------------------------------------------------------------------------------------------------
    # Pablo's IDC
    try:
        print('# Processing Pablo\'s Push IDC...')
        
        # Script 1) Push it real good...
        # Default patterns set
        pablo(0, code.start_ea, 0x10, '55 48 89')
        pablo(2, code.start_ea, code.end_ea, '90 90 55 48 ??')
        pablo(2, code.start_ea, code.end_ea, 'C3 90 55 48 ??')
        pablo(2, code.start_ea, code.end_ea, '66 90 55 48 ??')
        pablo(2, code.start_ea, code.end_ea, 'C9 C3 55 48 ??')
        pablo(2, code.start_ea, code.end_ea, '0F 0B 55 48 ??')
        pablo(2, code.start_ea, code.end_ea, 'EB ?? 55 48 ??')
        pablo(2, code.start_ea, code.end_ea, '5D C3 55 48 ??')
        pablo(2, code.start_ea, code.end_ea, '5B C3 55 48 ??')
        pablo(2, code.start_ea, code.end_ea, '90 90 55 41 ?? 41 ??')
        pablo(2, code.start_ea, code.end_ea, '66 90 48 81 EC ?? 00 00 00')
        pablo(2, code.start_ea, code.end_ea, '0F 0B 48 89 9D ?? ?? FF FF 49 89')
        pablo(2, code.start_ea, code.end_ea, '90 90 53 4C 8B 54 24 20')
        pablo(2, code.start_ea, code.end_ea, '90 90 55 41 56 53')
        pablo(2, code.start_ea, code.end_ea, '90 90 53 48 89')
        pablo(2, code.start_ea, code.end_ea, '90 90 41 ?? 41 ??')
        pablo(3, code.start_ea, code.end_ea, '0F 0B 90 55 48 ??')
        pablo(3, code.start_ea, code.end_ea, 'EB ?? 90 55 48 ??')
        pablo(3, code.start_ea, code.end_ea, '41 5F C3 55 48 ??')
        pablo(3, code.start_ea, code.end_ea, '41 5C C3 55 48 ??')
        pablo(3, code.start_ea, code.end_ea, '31 C0 C3 55 48 ??')
        pablo(3, code.start_ea, code.end_ea, '41 5D C3 55 48 ??')
        pablo(3, code.start_ea, code.end_ea, '41 5E C3 55 48 ??')
        pablo(3, code.start_ea, code.end_ea, '66 66 90 55 48 ??')
        pablo(3, code.start_ea, code.end_ea, '0F 1F 00 55 48 ??')
        pablo(3, code.start_ea, code.end_ea, '41 ?? C3 53 48')
        pablo(3, code.start_ea, code.end_ea, '0F 1F 00 48 81 EC ?? 00 00 00')
        pablo(4, code.start_ea, code.end_ea, '0F 1F 40 00 55 48 ??')
        pablo(4, code.start_ea, code.end_ea, '0F 1F 40 00 48 81 EC ?? 00 00 00')
        pablo(5, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 55 48 ??')
        pablo(5, code.start_ea, code.end_ea, 'E8 ?? ?? ?? ?? 55 48 ??')
        pablo(5, code.start_ea, code.end_ea, '48 83 C4 ?? C3 55 48 ??')
        pablo(5, code.start_ea, code.end_ea, '0F 1F 44 00 00 55 48 ??')
        pablo(5, code.start_ea, code.end_ea, '0F 1F 44 00 00 48 81 EC ?? 00 00 00')
        pablo(6, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 55 48 ??')
        pablo(6, code.start_ea, code.end_ea, 'E8 ?? ?? ?? ?? 90 55 48 ??')
        pablo(6, code.start_ea, code.end_ea, '66 0F 1F 44 00 00 55 48 ??')
        pablo(7, code.start_ea, code.end_ea, '0F 1F 80 00 00 00 00 55 48 ??')
        pablo(8, code.start_ea, code.end_ea, '0F 1F 84 00 00 00 00 00 55 48 ??')
        pablo(8, code.start_ea, code.end_ea, 'C3 0F 1F 80 00 00 00 00 48')
        pablo(8, code.start_ea, code.end_ea, '0F 1F 84 00 00 00 00 00 53 48 83 EC')
        
        # Special cases patterns set
        pablo(13, code.start_ea, code.end_ea, 'C3 90 90 90 90 90 90 90 90 90 90 90 90 48')
        pablo(13, code.start_ea, code.end_ea, 'C3 90 90 90 90 90 90 90 90 90 90 90 90 55')
        pablo(17, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 48')
        pablo(19, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 90 90 48')
        pablo(19, code.start_ea, code.end_ea, 'E8 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 90 90 48')
        pablo(20, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 48')
        pablo(20, code.start_ea, code.end_ea, 'E9 ?? ?? ?? ?? 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 48')

    except:
        pass
    
    # --------------------------------------------------------------------------------------------------------
    # Chendo's Syscall Commenter
    try:
        print('# Processing Chendo\'s Syscall Commenter...')
        
        SYSCALLS = [
            'nosys',
            'exit',
            'fork',
            'read',
            'write',
            'open',
            'close',
            'wait4',
            'creat',
            'link',
            'unlink',
            'execv',
            'chdir',
            'fchdir',
            'mkd',
            'chmod',
            'chown',
            'obreak',
            'getfsstat',
            'lseek',
            'getpid',
            'mount',
            'unmount',
            'setuid',
            'getuid',
            'geteuid',
            'ptrace',
            'recvmsg',
            'sendmsg',
            'recvfrom',
            'accept',
            'getpeername',
            'getsockname',
            'access',
            'chflags',
            'fchflags',
            'sync',
            'kill',
            'stat',
            'getppid',
            'lstat',
            'dup',
            'pipe',
            'getegid',
            'profil',
            'ktrace',
            'sigaction',
            'getgid',
            'sigprocmask',
            'getlogin',
            'setlogin',
            'acct',
            'sigpending',
            'sigaltstack',
            'ioctl',
            'reboot',
            'revoke',
            'symlink',
            'readlink',
            'execve',
            'umask',
            'chroot',
            'fstat',
            'getkerninfo',
            'getpagesize',
            'msync',
            'vfork',
            'vread',
            'vwrite',
            'sbrk',
            'sstk',
            'mmap',
            'ovadvise',
            'munmap',
            'mprotect',
            'madvise',
            'vhangup',
            'vlimit',
            'mincore',
            'getgroups',
            'setgroups',
            'getpgrp',
            'setpgid',
            'setitimer',
            'wait',
            'swapon',
            'getitimer',
            'gethostname',
            'sethostname',
            'getdtablesize',
            'dup2',
            'getdopt',
            'fcntl',
            'select',
            'setdopt',
            'fsync',
            'setpriority',
            'socket',
            'connect',
            'accept',
            'getpriority',
            'send',
            'recv',
            'sigreturn',
            'bind',
            'setsockopt',
            'listen',
            'vtimes',
            'sigvec',
            'sigblock',
            'sigsetmask',
            'sigsuspend',
            'sigstack',
            'recvmsg',
            'sendmsg',
            'vtrace',
            'gettimeofday',
            'getrusage',
            'getsockopt',
            'resuba',
            'readv',
            'writev',
            'settimeofday',
            'fchown',
            'fchmod',
            'recvfrom',
            'setreuid',
            'setregid',
            'rename',
            'truncate',
            'ftruncate',
            'flock',
            'mkfifo',
            'sendto',
            'shutdown',
            'socketpair',
            'mkdir',
            'rmdir',
            'utimes',
            'sigreturn',
            'adjtime',
            'getpeername',
            'gethostid',
            'sethostid',
            'getrlimit',
            'setrlimit',
            'killpg',
            'setsid',
            'quotactl',
            'quota',
            'getsockname',
            'sem_lock',
            'sem_wakeup',
            'asyncdaemon',
            'nlm_syscall',
            'nfssvc',
            'getdirentries',
            'statfs',
            'fstatfs',
            '#159',
            'lgetfh',
            'getfh',
            'getdomainname',
            'setdomainname',
            'uname',
            'sysarch',
            'rtprio',
            '#167',
            '#168',
            'semsys',
            'msgsys',
            'shmsys',
            '#172',
            'pread',
            'pwrite',
            'setfib',
            'ntp_adjtime',
            'sfork',
            'getdescriptor',
            'setdescriptor',
            '#180',
            'setgid',
            'setegid',
            'seteuid',
            'lfs_bmapv',
            'lfs_markv',
            'lfs_segclean',
            'lfs_segwait',
            'stat',
            'fstat',
            'lstat',
            'pathconf',
            'fpathconf',
            '#193',
            'getrlimit',
            'setrlimit',
            'getdirentries',
            'mmap',
            'nosys',
            'lseek',
            'truncate',
            'ftruncate',
            'sysctl',
            'mlock',
            'munlock',
            'undelete',
            'futimes',
            'getpgid',
            'newreboot',
            'poll',
            '#210',
            '#211',
            '#212',
            '#213',
            '#214',
            '#215',
            '#216',
            '#217',
            '#218',
            '#219',
            'semctl',
            'semget',
            'semop',
            'semconfig',
            'msgctl',
            'msgget',
            'msgsnd',
            'msgrcv',
            'shmat',
            'shmctl',
            'shmdt',
            'shmget',
            'clock_gettime',
            'clock_settime',
            'clock_getres',
            'ktimer_create',
            'ktimer_delete',
            'ktimer_settime',
            'ktimer_gettime',
            'ktimer_getoverrun',
            'nasleep',
            'ffclock_getcounter',
            'ffclock_setestimate',
            'ffclock_getestimate',
            '#244',
            '#245',
            '#246',
            'clock_getcpuclockid2',
            'ntp_gettime',
            '#249',
            'minherit',
            'rfork',
            'openbsd_poll',
            'issetugid',
            'lchown',
            'aio_read',
            'aio_write',
            'lio_listio',
            '#258',
            '#259',
            '#260',
            '#261',
            '#262',
            '#263',
            '#264',
            '#265',
            '#266',
            '#267',
            '#268',
            '#269',
            '#270',
            '#271',
            'getdents',
            '#273',
            'lchmod',
            'lchown',
            'lutimes',
            'msync',
            'nstat',
            'nfstat',
            'nlstat',
            '#281',
            '#282',
            '#283',
            '#284',
            '#285',
            '#286',
            '#287',
            '#288',
            'preadv',
            'pwritev',
            '#291',
            '#292',
            '#293',
            '#294',
            '#295',
            '#296',
            'fhstatfs',
            'fhopen',
            'fhstat',
            'modnext',
            'modstat',
            'modfnext',
            'modfind',
            'kldload',
            'kldunload',
            'kldfind',
            'kldnext',
            'kldstat',
            'kldfirstmod',
            'getsid',
            'setresuid',
            'setresgid',
            'signasleep',
            'aio_return',
            'aio_suspend',
            'aio_cancel',
            'aio_error',
            'aio_read',
            'aio_write',
            'lio_listio',
            'yield',
            'thr_sleep',
            'thr_wakeup',
            'mlockall',
            'munlockall',
            'getcwd',
            'sched_setparam',
            'sched_getparam',
            'sched_setscheduler',
            'sched_getscheduler',
            'sched_yield',
            'sched_get_priority_max',
            'sched_get_priority_min',
            'sched_rr_get_interval',
            'utrace',
            'sendfile',
            'kldsym',
            'jail',
            'nnpfs_syscall',
            'sigprocmask',
            'sigsuspend',
            'sigaction',
            'sigpending',
            'sigreturn',
            'sigtimedwait',
            'sigwaitinfo',
            'acl_get_file',
            'acl_set_file',
            'acl_get_fd',
            'acl_set_fd',
            'acl_delete_file',
            'acl_delete_fd',
            'acl_aclcheck_file',
            'acl_aclcheck_fd',
            'extattrctl',
            'extattr_set_file',
            'extattr_get_file',
            'extattr_delete_file',
            'aio_waitcomplete',
            'getresuid',
            'getresgid',
            'kqueue',
            'kevent',
            'cap_get_proc',
            'cap_set_proc',
            'cap_get_fd',
            'cap_get_file',
            'cap_set_fd',
            'cap_set_file',
            '#370',
            'extattr_set_fd',
            'extattr_get_fd',
            'extattr_delete_fd',
            'setugid',
            'nfsclnt',
            'eaccess',
            'afs3_syscall',
            'nmount',
            'kse_exit',
            'kse_wakeup',
            'kse_create',
            'kse_thr_interrupt',
            'kse_release',
            'mac_get_proc',
            'mac_set_proc',
            'mac_get_fd',
            'mac_get_file',
            'mac_set_fd',
            'mac_set_file',
            'kenv',
            'lchflags',
            'uuidgen',
            'sendfile',
            'mac_syscall',
            'getfsstat',
            'statfs',
            'fstatfs',
            'fhstatfs',
            '#399',
            'ksem_close',
            'ksem_post',
            'ksem_wait',
            'ksem_trywait',
            'ksem_init',
            'ksem_open',
            'ksem_unlink',
            'ksem_getvalue',
            'ksem_destroy',
            'mac_get_pid',
            'mac_get_link',
            'mac_set_link',
            'extattr_set_link',
            'extattr_get_link',
            'extattr_delete_link',
            'mac_execve',
            'sigaction',
            'sigreturn',
            'xstat',
            'xfstat',
            'xlstat',
            'getcontext',
            'setcontext',
            'swapcontext',
            'swapoff',
            'acl_get_link',
            'acl_set_link',
            'acl_delete_link',
            'acl_aclcheck_link',
            'sigwait',
            'thr_create',
            'thr_exit',
            'thr_self',
            'thr_kill',
            '#434',
            '#435',
            'jail_attach',
            'extattr_list_fd',
            'extattr_list_file',
            'extattr_list_link',
            'kse_switchin',
            'ksem_timedwait',
            'thr_suspend',
            'thr_wake',
            'kldunloadf',
            'audit',
            'auditon',
            'getauid',
            'setauid',
            'getaudit',
            'setaudit',
            'getaudit_addr',
            'setaudit_addr',
            'auditctl',
            'umtx_op',
            'thr_new',
            'sigqueue',
            'kmq_open',
            'kmq_setattr',
            'kmq_timedreceive',
            'kmq_timedsend',
            'kmq_tify',
            'kmq_unlink',
            'abort2',
            'thr_set_name',
            'aio_fsync',
            'rtprio_thread',
            '#467',
            '#468',
            'getpath_fromfd',
            'getpath_fromaddr',
            'sctp_peeloff',
            'sctp_generic_sendmsg',
            'sctp_generic_sendmsg_iov',
            'sctp_generic_recvmsg',
            'pread',
            'pwrite',
            'mmap',
            'lseek',
            'truncate',
            'ftruncate',
            'thr_kill2',
            'shm_open',
            'shm_unlink',
            'cpuset',
            'cpuset_setid',
            'cpuset_getid',
            'cpuset_getaffinity',
            'cpuset_setaffinity',
            'faccessat',
            'fchmodat',
            'fchownat',
            'fexecve',
            'fstatat',
            'futimesat',
            'linkat',
            'mkdirat',
            'mkfifoat',
            'mkdat',
            'openat',
            'readlinkat',
            'renameat',
            'symlinkat',
            'unlinkat',
            'posix_openpt',
            'gssd_syscall',
            'jail_get',
            'jail_set',
            'jail_remove',
            'closefrom',
            'semctl',
            'msgctl',
            'shmctl',
            'lpathconf',
            'cap_new',
            'cap_rights_get',
            'cap_enter',
            'cap_getmode',
            'pdfork',
            'pdkill',
            'pdgetpid',
            'pdwait4',
            'pselect',
            'getloginclass',
            'setloginclass',
            'rctl_get_racct',
            'rctl_get_rules',
            'rctl_get_limits',
            'rctl_add_rule',
            'rctl_remove_rule',
            'posix_fallocate',
            'posix_fadvise',
            'regmgr_call',
            'jitshm_create',
            'jitshm_alias',
            'dl_get_list',
            'dl_get_info',
            'dl_notify_event',
            'evf_create',
            'evf_delete',
            'evf_open',
            'evf_close',
            'evf_wait',
            'evf_trywait',
            'evf_set',
            'evf_clear',
            'evf_cancel',
            'query_memory_protection',
            'batch_map',
            'osem_create',
            'osem_delete',
            'osem_open',
            'osem_close',
            'osem_wait',
            'osem_trywait',
            'osem_post',
            'osem_cancel',
            'namedobj_create',
            'namedobj_delete',
            'set_vm_container',
            'debug_init',
            'suspend_process',
            'resume_process',
            'opmc_enable',
            'opmc_disable',
            'opmc_set_ctl',
            'opmc_set_ctr',
            'opmc_get_ctr',
            'budget_create',
            'budget_delete',
            'budget_get',
            'budget_set',
            'virtual_query',
            'mdbg_call',
            'sblock_create',
            'sblock_delete',
            'sblock_enter',
            'sblock_exit',
            'sblock_xenter',
            'sblock_xexit',
            'eport_create',
            'eport_delete',
            'eport_trigger',
            'eport_open',
            'eport_close',
            'is_in_sandbox',
            'dmem_container',
            'get_authinfo',
            'mname',
            'dynlib_dlopen',
            'dynlib_dlclose',
            'dynlib_dlsym',
            'dynlib_get_list',
            'dynlib_get_info',
            'dynlib_load_prx',
            'dynlib_unload_prx',
            'dynlib_do_copy_relocations',
            'dynlib_prepare_dlclose',
            'dynlib_get_proc_param',
            'dynlib_process_needed_and_relocate',
            'sandbox_path',
            'mdbg_service',
            'randomized_path',
            'rdup',
            'dl_get_metadata',
            'workaround8849',
            'is_development_mode',
            'get_self_auth_info',
            'dynlib_get_info_ex',
            'budget_getid',
            'budget_get_ptype',
            'get_paging_stats_of_all_threads',
            'get_proc_type_info',
            'get_resident_count',
            'prepare_to_suspend_process',
            'get_resident_fmem_count',
            'thr_get_name',
            'set_gpo',
            'get_paging_stats_of_all_objects',
            'test_debug_rwmem',
            'free_stack',
            'suspend_system',
            'ipmimgr_call',
            'get_gpo',
            'get_vm_map_timestamp',
            'opmc_set_hw',
            'opmc_get_hw',
            'get_cpu_usage_all',
            'mmap_dmem',
            'physhm_open',
            'physhm_unlink',
            'resume_internal_hdd',
            'thr_suspend_ucontext',
            'thr_resume_ucontext',
            'thr_get_ucontext',
            'thr_set_ucontext',
            'set_timezone_info',
            'set_phys_fmem_limit',
            'utc_to_localtime',
            'localtime_to_utc',
            'set_uevt',
            'get_cpu_usage_proc',
            'get_map_statistics',
            'set_chicken_switches',
            '#644',
            '#645',
            'get_kernel_mem_statistics',
            'get_sdk_compiled_version',
            'app_state_change',
            'dynlib_get_obj_member',
            'budget_get_ptype_of_budget',
            'prepare_to_resume_process',
            'process_terminate',
            'blockpool_open',
            'blockpool_map',
            'blockpool_unmap',
            'dynlib_get_info_for_libdbg',
            'blockpool_batch',
            'fdatasync',
            'dynlib_get_list2',
            'dynlib_get_info2',
            'aio_submit',
            'aio_multi_delete',
            'aio_multi_wait',
            'aio_multi_poll',
            'aio_get_data',
            'aio_multi_cancel',
            'get_bio_usage_all',
            'aio_create',
            'aio_submit_cmd',
            'aio_init',
            'get_page_table_stats',
            'dynlib_get_list_for_libdbg',
            'blockpool_move',
            'virtual_query_all',
            'reserve_2mb_page',
            'cpumode_yield',
            'get_phys_page_size'
        ]
        
        address = code.start_ea
        end     = code.end_ea
        
        while address < end:
            address = idaapi.find_binary(address, end, '00 00 49 89 CA 0F 05', 0x10, SEARCH_DOWN)
            number = address - 0x2
            
            if address < end:
                number = idaapi.get_wide_word(number)
                idc.set_cmt(address + 0x5, 'sys_' + SYSCALLS[number], False)
                address += 0x8
            
    except:
        pass
    
    # --------------------------------------------------------------------------------------------------------
    # Error Code Enumerator
    try:
        print('# Processing Error Codes...')
        
        idc.import_type(-1, 'PS4_ERROR_CODES')
        
        member = idaapi.get_enum_member_by_name('SCE_KERNEL_ERROR_EPERM')
        enum   = idaapi.get_enum_member_enum(member)
        
        address = code.start_ea
        
        while True:
            address = idaapi.find_text(address, 0, 0, '80[0-9a-fA-F]{6}h', SEARCH_DOWN | SEARCH_NEXT | SEARCH_REGEX)
            if address == BADADDR: break
            
            # 0x80 is always at the end
            offset = idaapi.get_item_size(address) - 0x4
            errno = idaapi.get_dword(address + offset)
            
            if idaapi.get_enum_member(enum, errno, -1, 0) != BADADDR:
                idaapi.op_enum(address, 1, enum, 0)
            
            address = idaapi.next_head(address, BADADDR)
    
    except:
        pass
    
    print('# Done!')
    return 1

# PROGRAM END