import os
import struct
from io import BytesIO

from PIL import Image

def resize(image, size, format=None):
    output = BytesIO()
    back = Image.new('RGBA', size, (0,0,0,0))

    if image.size[0] < size[0] or image.size[1] < size[1]:
        if image.height > image.width:
            factor = size[0] / image.height
        else:
            factor = size[1] / image.width
        image = image.resize((int(image.width * factor), int(image.height * factor)), Image.ANTIALIAS)
    else:
        image.thumbnail(size, Image.ANTIALIAS)

    offset = [0, 0]
    if image.size[0] > image.size[1]:
        offset[1] = int(back.size[1]/2-image.size[1]/2)
    elif image.size[0] < image.size[1]:
        offset[0] = int(back.size[0]/2-image.size[0]/2)
    else:
        offset[0] = int(back.size[0]/2-image.size[0]/2)
        offset[1] = int(back.size[1]/2-image.size[1]/2)

    back.paste(image, tuple(offset))
    format = format or image.format
    back.save(output, format, sizes=[size])
    contents = output.getvalue()
    output.close()
    return contents


struct_symbols = {1:'B',#byte
                  2:'H',#word
                  4:'L',#long word
                  8:'Q' #double long word
                }
endian_symbols = {'little':'<',
                  'big':'>'}

name_dictionary = {'PEHeader_Machine': {
                                0:'IMAGE_FILE_MACHINE_UNKNOWN',
                                0x014c:'IMAGE_FILE_MACHINE_I386',
                                0x0162:'IMAGE_FILE_MACHINE_R3000',
                                0x0166:'IMAGE_FILE_MACHINE_R4000',
                                0x0168:'IMAGE_FILE_MACHINE_R10000',
                                0x0169:'IMAGE_FILE_MACHINE_WCEMIPSV2',
                                0x0184:'IMAGE_FILE_MACHINE_ALPHA',
                                0x01a2:'IMAGE_FILE_MACHINE_SH3',
                                0x01a3:'IMAGE_FILE_MACHINE_SH3DSP',
                                0x01a4:'IMAGE_FILE_MACHINE_SH3E',
                                0x01a6:'IMAGE_FILE_MACHINE_SH4',
                                0x01a8:'IMAGE_FILE_MACHINE_SH5',
                                0x01c0:'IMAGE_FILE_MACHINE_ARM',
                                0x01c2:'IMAGE_FILE_MACHINE_THUMB',
                                0x01c4:'IMAGE_FILE_MACHINE_ARMNT',
                                0x01d3:'IMAGE_FILE_MACHINE_AM33',
                                0x01f0:'IMAGE_FILE_MACHINE_POWERPC',
                                0x01f1:'IMAGE_FILE_MACHINE_POWERPCFP',
                                0x0200:'IMAGE_FILE_MACHINE_IA64',
                                0x0266:'IMAGE_FILE_MACHINE_MIPS16',
                                0x0284:'IMAGE_FILE_MACHINE_ALPHA64',
                                0x0284:'IMAGE_FILE_MACHINE_AXP64', # same
                                0x0366:'IMAGE_FILE_MACHINE_MIPSFPU',
                                0x0466:'IMAGE_FILE_MACHINE_MIPSFPU16',
                                0x0520:'IMAGE_FILE_MACHINE_TRICORE',
                                0x0cef:'IMAGE_FILE_MACHINE_CEF',
                                0x0ebc:'IMAGE_FILE_MACHINE_EBC',
                                0x8664:'IMAGE_FILE_MACHINE_AMD64',
                                0x9041:'IMAGE_FILE_MACHINE_M32R',
                                0xc0ee:'IMAGE_FILE_MACHINE_CEE'
                      },

                     'PEHeader_Characteristics':{
                                0x0001:'IMAGE_FILE_RELOCS_STRIPPED',
                                0x0002:'IMAGE_FILE_EXECUTABLE_IMAGE',
                                0x0004:'IMAGE_FILE_LINE_NUMS_STRIPPED',
                                0x0008:'IMAGE_FILE_LOCAL_SYMS_STRIPPED',
                                0x0010:'IMAGE_FILE_AGGRESIVE_WS_TRIM',
                                0x0020:'IMAGE_FILE_LARGE_ADDRESS_AWARE',
                                0x0040:'IMAGE_FILE_16BIT_MACHINE',
                                0x0080:'IMAGE_FILE_BYTES_REVERSED_LO',
                                0x0100:'IMAGE_FILE_32BIT_MACHINE',
                                0x0200:'IMAGE_FILE_DEBUG_STRIPPED',
                                0x0400:'IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP',
                                0x0800:'IMAGE_FILE_NET_RUN_FROM_SWAP',
                                0x1000:'IMAGE_FILE_SYSTEM',
                                0x2000:'IMAGE_FILE_DLL',
                                0x4000:'IMAGE_FILE_UP_SYSTEM_ONLY',
                                0x8000:'IMAGE_FILE_BYTES_REVERSED_HI'
                     },
                     'OptionalHeader_Subsystem':{
                                0:'IMAGE_SUBSYSTEM_UNKNOWN',
                                1:'IMAGE_SUBSYSTEM_NATIVE',
                                2:'IMAGE_SUBSYSTEM_WINDOWS_GUI',
                                3:'IMAGE_SUBSYSTEM_WINDOWS_CUI',
                                5:'IMAGE_SUBSYSTEM_OS2_CUI',
                                7:'IMAGE_SUBSYSTEM_POSIX_CUI',
                                8:'IMAGE_SUBSYSTEM_NATIVE_WINDOWS',
                                9:'IMAGE_SUBSYSTEM_WINDOWS_CE_GUI',
                                10:'IMAGE_SUBSYSTEM_EFI_APPLICATION',
                                11:'IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER',
                                12:'IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER',
                                13:'IMAGE_SUBSYSTEM_EFI_ROM',
                                14:'IMAGE_SUBSYSTEM_XBOX',
                                16:'IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION'
                     },
                    'OptionalHeader_DLL_Characteristics':{
                                0x0001:'IMAGE_LIBRARY_PROCESS_INIT', # reserved
                                0x0002:'IMAGE_LIBRARY_PROCESS_TERM', # reserved
                                0x0004:'IMAGE_LIBRARY_THREAD_INIT', # reserved
                                0x0008:'IMAGE_LIBRARY_THREAD_TERM', # reserved
                                0x0020:'IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA',
                                0x0040:'IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE',
                                0x0080:'IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY',
                                0x0100:'IMAGE_DLLCHARACTERISTICS_NX_COMPAT',
                                0x0200:'IMAGE_DLLCHARACTERISTICS_NO_ISOLATION',
                                0x0400:'IMAGE_DLLCHARACTERISTICS_NO_SEH',
                                0x0800:'IMAGE_DLLCHARACTERISTICS_NO_BIND',
                                0x1000:'IMAGE_DLLCHARACTERISTICS_APPCONTAINER',
                                0x2000:'IMAGE_DLLCHARACTERISTICS_WDM_DRIVER',
                                0x4000:'IMAGE_DLLCHARACTERISTICS_GUARD_CF',
                                0x8000:'IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE'
                     },

                    'SectionHeader_Characteristics':{

                                0x00000000:'IMAGE_SCN_TYPE_REG', # reserved
                                0x00000001:'IMAGE_SCN_TYPE_DSECT', # reserved
                                0x00000002:'IMAGE_SCN_TYPE_NOLOAD', # reserved
                                0x00000004:'IMAGE_SCN_TYPE_GROUP', # reserved
                                0x00000008:'IMAGE_SCN_TYPE_NO_PAD', # reserved
                                0x00000010:'IMAGE_SCN_TYPE_COPY', # reserved

                                0x00000020:'IMAGE_SCN_CNT_CODE',
                                0x00000040:'IMAGE_SCN_CNT_INITIALIZED_DATA',
                                0x00000080:'IMAGE_SCN_CNT_UNINITIALIZED_DATA',

                                0x00000100:'IMAGE_SCN_LNK_OTHER',
                                0x00000200:'IMAGE_SCN_LNK_INFO',
                                0x00000400:'IMAGE_SCN_LNK_OVER', # reserved
                                0x00000800:'IMAGE_SCN_LNK_REMOVE',
                                0x00001000:'IMAGE_SCN_LNK_COMDAT',

                                0x00004000:'IMAGE_SCN_MEM_PROTECTED', # obsolete
                                0x00004000:'IMAGE_SCN_NO_DEFER_SPEC_EXC',
                                0x00008000:'IMAGE_SCN_GPREL',
                                0x00008000:'IMAGE_SCN_MEM_FARDATA',
                                0x00010000:'IMAGE_SCN_MEM_SYSHEAP', # obsolete
                                0x00020000:'IMAGE_SCN_MEM_PURGEABLE',
                                0x00020000:'IMAGE_SCN_MEM_16BIT',
                                0x00040000:'IMAGE_SCN_MEM_LOCKED',
                                0x00080000:'IMAGE_SCN_MEM_PRELOAD',

                                0x00100000:'IMAGE_SCN_ALIGN_1BYTES',
                                0x00200000:'IMAGE_SCN_ALIGN_2BYTES',
                                0x00300000:'IMAGE_SCN_ALIGN_4BYTES',
                                0x00400000:'IMAGE_SCN_ALIGN_8BYTES',
                                0x00500000:'IMAGE_SCN_ALIGN_16BYTES', # default alignment
                                0x00600000:'IMAGE_SCN_ALIGN_32BYTES',
                                0x00700000:'IMAGE_SCN_ALIGN_64BYTES',
                                0x00800000:'IMAGE_SCN_ALIGN_128BYTES',
                                0x00900000:'IMAGE_SCN_ALIGN_256BYTES',
                                0x00A00000:'IMAGE_SCN_ALIGN_512BYTES',
                                0x00B00000:'IMAGE_SCN_ALIGN_1024BYTES',
                                0x00C00000:'IMAGE_SCN_ALIGN_2048BYTES',
                                0x00D00000:'IMAGE_SCN_ALIGN_4096BYTES',
                                0x00E00000:'IMAGE_SCN_ALIGN_8192BYTES',
                                0x00F00000:'IMAGE_SCN_ALIGN_MASK',

                                0x01000000:'IMAGE_SCN_LNK_NRELOC_OVFL',
                                0x02000000:'IMAGE_SCN_MEM_DISCARDABLE',
                                0x04000000:'IMAGE_SCN_MEM_NOT_CACHED',
                                0x08000000:'IMAGE_SCN_MEM_NOT_PAGED',
                                0x10000000:'IMAGE_SCN_MEM_SHARED',
                                0x20000000:'IMAGE_SCN_MEM_EXECUTE',
                                0x40000000:'IMAGE_SCN_MEM_READ',
                                0x80000000:'IMAGE_SCN_MEM_WRITE',

                    },
}

DEFAULT_ENDIAN = 'little'

def read_data(file_data, offset, number_of_bytes, string_data=None):
    """Just reads the straight data with no endianness."""

    if number_of_bytes > 0:
        data = file_data[offset:offset+number_of_bytes]
        #if len(data) != number_of_bytes:
            #print 'data out of bounds:', 'offset', hex(offset), 'data', data, 'data_len', len(data), 'num_bytes', number_of_bytes, 'total', hex(len(file_data))
        return data
    else:
        return bytearray('')

def read_bytes(file_data, offset, number_of_bytes, endian=None, string_data=None):
    """Returns a tuple of the data value and string representation.
    Will read 1,2,4,8 bytes with little endian as the default
    (value, string)
    """

    if number_of_bytes > 0:

        endian = endian or DEFAULT_ENDIAN
        endian = endian_symbols[endian]

        data = bytes(file_data[offset:offset+number_of_bytes])
        if len(data) != number_of_bytes:
            return 0, u''

        return struct.unpack(endian+struct_symbols[number_of_bytes], data)[0], data
    else:
        return 0, u''

def value_to_byte_string(value, number_of_bytes, endian=None):
    endian = endian or DEFAULT_ENDIAN
    endian = endian_symbols[endian]

    return struct.pack(endian+struct_symbols[number_of_bytes], value)

class ResourceTypes(object):
        Cursor = 1
        Bitmap = 2
        Icon = 3
        Menu = 4
        Dialog = 5
        String = 6
        Font_Directory = 7
        Font = 8
        Accelerator = 9
        RC_Data = 10
        Message_Table = 11
        Group_Cursor = 12
        Group_Icon = 14
        Version_Info = 16
        DLG_Include = 17
        Plug_Play = 19
        VXD = 20
        Animated_Cursor = 21
        Animated_Icon = 22
        HTML = 23
        Manifest = 24


resource_types = {1: 'Cursor',
                  2: 'Bitmap',
                  3: 'Icon',
                  4: 'Menu',
                  5: 'Dialog',
                  6: 'String',
                  7: 'Font Directory',
                  8: 'Font',
                  9: 'Accelerator',
                  10: 'RC Data',
                  11: 'Message Table',
                  12: 'Group Cursor',
                  14: 'Group Icon',
                  16: 'Version Info',
                  17: 'DLG Include',
                  19: 'Plug and Play',
                  20: 'VXD',
                  21: 'Animated Cursor',
                  22: 'Animated Icon',
                  23: 'HTML',
                  24: 'Manifest'}

_32BIT_PLUS_MAGIC = 0x20b
_32BIT_MAGIC = 0x10b
_ROM_MAGIC = 0x107

def read_from_name_dict(obj, field_name):
    dict_field = '{}_{}'.format(obj.__class__.__name__, field_name)
    return name_dictionary.get(dict_field, {})

def test_bit(value, index):
    mask = 1 << index
    return (value & mask)

def set_bit(value, index):
    mask = 1 << index
    return (value | mask)

def clear_bit(value, index):
    mask = ~(1 << index)
    return (value & mask)

def toggle_bit(value, index):
    mask = 1 << index
    return (value ^ mask)

class PEFormatError(Exception):
    pass

class Printable(object):
    def _attrs(self):
        a = []
        for attr in dir(self):
            if not attr.startswith('_') and not callable(getattr(self, attr)):
                a.append(attr)
        return a

    def _dict_items(self):
        for a in reversed(self._attrs()):
            yield a, getattr(self,a)

    def _dict_string(self):
        vals = []
        for key, val in self._dict_items():
            try:
                vals.append(u'{}={}'.format(key, val))
            except UnicodeDecodeError:
                vals.append(u'{}=<not printable>'.format(key))
        return u', '.join(vals)

    def __repr__(self):
        return str(self)

    def __str__(self):
        return u'{} [{}]'.format(self.__class__.__name__, self._dict_string())


class Structure(Printable):

    _fields = {}

    def __init__(self, size=0, value=None, data=None, absolute_offset=0,
                 name='', friendly_name='', *args, **kwargs):
        self._value = value
        self.size = size
        self.data = data
        self.name = name
        self.friendly_name = friendly_name
        self._absolute_offset = absolute_offset
        self._file_data = None

        for k, v in kwargs.items():
            setattr(self, k, v)

    @property
    def absolute_offset(self):
        return self._absolute_offset

    @absolute_offset.setter
    def absolute_offset(self, abs_offset):
        self._absolute_offset = abs_offset
        for k, v in self._fields.items():
            field = getattr(self, k)
            field.absolute_offset = self.absolute_offset + field.offset

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
        if self._file_data is not None:
            self.data = value_to_byte_string(value, self.size)
            self._file_data[self.absolute_offset:self.absolute_offset+self.size] = bytearray(self.data)
        self._value = value


    def process_field(self, file_data, field_name, field_info):

        if hasattr(self, 'process_'+field_name) and callable(getattr(self, 'process_'+field_name)):
            getattr(self, 'process_'+field_name)(file_data, field_name, field_info)
        else:
            absolute_offset = field_info['offset'] + self.absolute_offset
            size = field_info['size']
            self.size += size
            int_value, data = read_bytes(file_data, absolute_offset, size)
            field_name_dict = read_from_name_dict(self, field_name)
            name = field_name_dict.get(int_value, '')
            friendly_name = name.replace('_', ' ').capitalize()

            setattr(self, field_name, Structure(offset=field_info['offset'],
                                                size=size,
                                                value=int_value, data=data,
                                                absolute_offset=absolute_offset,
                                                name=name, friendly_name=friendly_name))
            getattr(self, field_name)._file_data = file_data


    def process_Characteristics(self, file_data, field_name, field_info):
        absolute_offset = field_info['offset'] + self.absolute_offset
        size = field_info['size']
        self.size += size
        int_value, data = read_bytes(file_data, absolute_offset, size)
        field_name_dict = read_from_name_dict(self, field_name)

        bit_length = len(bin(int_value))-2

        characteristics = {}
        for i in range(bit_length):
            set_bit = test_bit(int_value, i)
            char_name = field_name_dict.get(set_bit, '')
            if set_bit != 0 and char_name:
                characteristics[char_name] = set_bit


        setattr(self, field_name, Structure(offset=field_info['offset'],
                                            size=size,
                                            value=int_value, data=data,
                                            absolute_offset=absolute_offset,
                                            values=characteristics,
                                            ))
        getattr(self, field_name)._file_data = file_data

    @classmethod
    def parse_from_data(cls, file_data, **cls_args):
        """Parses the Structure from the file data."""
        self = cls(**cls_args)
        self._file_data = file_data
        for field_name, field_info in self._fields.items():
            self.process_field(file_data, field_name, field_info)
        return self



class DOSHeader(Structure):
    """The dos header of the PE file"""

    _fields = {'Signature':{'offset':0,
                           'size':2},
              'PEHeaderOffset':{'offset':0x3c,
                                'size':4}
              }




class PEHeader(Structure):
    """PE signature plus the COFF header"""

    _fields = {'Signature':{'offset':0,
                           'size':4},
              'Machine':{'offset':4,
                         'size':2},
              'NumberOfSections':{'offset':6,
                                  'size':2},
              'TimeDateStamp':{'offset':8,
                               'size':4},
              'PointerToSymbolTable':{'offset':12,
                                      'size':4},
              'NumberOfSymbols':{'offset':16,
                                 'size':4},
              'SizeOfOptionalHeader':{'offset':20,
                                      'size':2},
              'Characteristics':{'offset':22,
                                 'size':2}
              }


class OptionalHeader(Structure):
    _fields_32_plus = {'Magic':{'offset':0,
                        'size':2},
              'MajorLinkerVersion':{'offset':2,
                                    'size':1},
              'MinorLinkerVersion':{'offset':3,
                                    'size':1},
              'SizeOfCode':{'offset':4,
                            'size':4},
              'SizeOfInitializedData':{'offset':8,
                                       'size':4},
              'SizeOfUninitializedData':{'offset':12,
                                         'size':4},
              'AddressOfEntryPoint':{'offset':16,
                                     'size':4},
              'BaseOfCode':{'offset':20,
                            'size':4},
              'ImageBase':{'offset':24,
                            'size':8},
              'SectionAlignment':{'offset':32,
                                  'size':4},
              'FileAlignment':{'offset':36,
                                'size':4},
              'MajorOperatingSystemVersion':{'offset':40,
                                             'size':2},
              'MinorOperatingSystemVersion':{'offset':42,
                                             'size':2},
              'MajorImageVersion':{'offset':44,
                                   'size':2},
              'MinorImageVersion':{'offset':46,
                                   'size':2},
              'MajorSubsystemVersion':{'offset':48,
                                       'size':2},
              'MinorSubsystemVersion':{'offset':50,
                                       'size':2},
              'Reserved':{'offset':52,
                          'size':4},
              'SizeOfImage':{'offset':56,
                             'size':4},
              'SizeOfHeaders':{'offset':60,
                               'size':4},
              'SizeOfHeaders':{'offset':60,
                               'size':4},
              'CheckSum': {'offset': 64,
                            'size':4},
              'Subsystem':{'offset':68,
                               'size':2},
              'DLL_Characteristics':{'offset':70,
                               'size':2},
              'SizeOfStackReserve':{'offset':72,
                               'size':8},
              'SizeOfStackCommit':{'offset':80,
                               'size':8},
              'SizeOfHeapReserve':{'offset':88,
                               'size':8},
              'SizeOfHeapCommit':{'offset':96,
                               'size':8},
              'LoaderFlags':{'offset':104,
                               'size':4},
              'NumberOfRvaAndSizes':{'offset':108,
                               'size':4},
              'ExportTableAddress':{'offset':112,
                               'size':4},
              'ExportTableSize':{'offset':116,
                               'size':4},
              'ImportTableAddress':{'offset':120,
                               'size':4},
              'ImportTableSize':{'offset':124,
                               'size':4},
              'ResourceTableAddress':{'offset':128,
                               'size':4},
              'ResourceTableSize':{'offset':132,
                               'size':4},
              'ExceptionTableAddress':{'offset':136,
                               'size':4},
              'ExceptionTableSize':{'offset':140,
                               'size':4},
              'CertificateTableAddress':{'offset':144,
                               'size':4},
              'CertificateTableSize':{'offset':148,
                               'size':4},
              'BaseRelocationTableAddress':{'offset':152,
                               'size':4},
              'BaseRelocationTableSize':{'offset':156,
                               'size':4},
              'DebugAddress':{'offset':160,
                               'size':4},
              'DebugSize':{'offset':164,
                               'size':4},
              'ArchitectureAddress':{'offset':168,
                               'size':4},
              'ArchitectureSize':{'offset':172,
                               'size':4},
              'GlobalPtrAddress':{'offset':176,
                               'size':8},
              'GlobalPtrSize':{'offset':184,
                               'size':0},
              'ThreadLocalStorageTableAddress':{'offset':184,
                               'size':4},
              'ThreadLocalStorageTableSize':{'offset':188,
                               'size':4},
              'LoadConfigTableAddress':{'offset':192,
                               'size':4},
              'LoadConfigTableSize':{'offset':196,
                               'size':4},
              'BoundImportAddress':{'offset':200,
                               'size':4},
              'BoundImportSize':{'offset':204,
                               'size':4},
              'ImportAddressTableAddress':{'offset':208,
                               'size':4},
              'ImportAddressTableSize':{'offset':212,
                               'size':4},
              'DelayImportDescriptorAddress':{'offset':216,
                               'size':4},
              'DelayImportDescriptorSize':{'offset':220,
                               'size':4},
              'COMRuntimeHeaderAddress':{'offset':224,
                               'size':4},
              'COMRuntimeHeaderSize':{'offset':228,
                               'size':4},
              'Reserved2':{'offset':232,
                               'size':8}

            }

    _fields_32 = {'Magic':{'offset':0,
                        'size':2},
              'MajorLinkerVersion':{'offset':2,
                                    'size':1},
              'MinorLinkerVersion':{'offset':3,
                                    'size':1},
              'SizeOfCode':{'offset':4,
                            'size':4},
              'SizeOfInitializedData':{'offset':8,#
                                       'size':4},
              'SizeOfUninitializedData':{'offset':12,
                                         'size':4},
              'AddressOfEntryPoint':{'offset':16,
                                     'size':4},
              'BaseOfCode':{'offset':20,
                            'size':4},
              'BaseOfData':{'offset':24,
                            'size':4},
              'ImageBase':{'offset':28,
                            'size':4},
              'SectionAlignment':{'offset':32,
                                  'size':4},
              'FileAlignment':{'offset':36,
                                'size':4},
              'MajorOperatingSystemVersion':{'offset':40,
                                             'size':2},
              'MinorOperatingSystemVersion':{'offset':42,
                                             'size':2},
              'MajorImageVersion':{'offset':44,
                                   'size':2},
              'MinorImageVersion':{'offset':46,
                                   'size':2},
              'MajorSubsystemVersion':{'offset':48,
                                       'size':2},
              'MinorSubsystemVersion':{'offset':50,
                                       'size':2},
              'Reserved':{'offset':52,
                          'size':4},
              'SizeOfImage':{'offset':56,#
                             'size':4},
              'SizeOfHeaders':{'offset':60,
                               'size':4},
              'CheckSum': {'offset': 64,
                            'size':4},
              'Subsystem':{'offset':68,
                               'size':2},
              'DLL_Characteristics':{'offset':70,
                               'size':2},
              'SizeOfStackReserve':{'offset':72,
                               'size':4},
              'SizeOfStackCommit':{'offset':76,
                               'size':4},
              'SizeOfHeapReserve':{'offset':80,
                               'size':4},
              'SizeOfHeapCommit':{'offset':84,
                               'size':4},
              'LoaderFlags':{'offset':88,
                               'size':4},
              'NumberOfRvaAndSizes':{'offset':92,
                               'size':4},
              'ExportTableAddress':{'offset':96,
                               'size':4},
              'ExportTableSize':{'offset':100,
                               'size':4},
              'ImportTableAddress':{'offset':104,
                               'size':4},
              'ImportTableSize':{'offset':108,
                               'size':4},
              'ResourceTableAddress':{'offset':112,
                               'size':4},
              'ResourceTableSize':{'offset':116,#
                               'size':4},
              'ExceptionTableAddress':{'offset':120,
                               'size':4},
              'ExceptionTableSize':{'offset':124,
                               'size':4},
              'CertificateTableAddress':{'offset':128,
                               'size':4},
              'CertificateTableSize':{'offset':132,
                               'size':4},
              'BaseRelocationTableAddress':{'offset':136,#
                               'size':4},
              'BaseRelocationTableSize':{'offset':140,
                               'size':4},
              'DebugAddress':{'offset':144,
                               'size':4},
              'DebugSize':{'offset':148,
                               'size':4},
              'ArchitectureAddress':{'offset':152,
                               'size':4},
              'ArchitectureSize':{'offset':156,
                               'size':4},
              'GlobalPtrAddress':{'offset':160,
                               'size':8},
              'GlobalPtrSize':{'offset':168,
                               'size':0},
              'ThreadLocalStorageTableAddress':{'offset':168,
                               'size':4},
              'ThreadLocalStorageTableSize':{'offset':172,
                               'size':4},
              'LoadConfigTableAddress':{'offset':176,
                               'size':4},
              'LoadConfigTableSize':{'offset':180,
                               'size':4},
              'BoundImportAddress':{'offset':184,
                               'size':4},
              'BoundImportSize':{'offset':188,
                               'size':4},
              'ImportAddressTableAddress':{'offset':192,
                               'size':4},
              'ImportAddressTableSize':{'offset':196,
                               'size':4},
              'DelayImportDescriptorAddress':{'offset':200,
                               'size':4},
              'DelayImportDescriptorSize':{'offset':204,
                               'size':4},
              'COMRuntimeHeaderAddress':{'offset':208,
                               'size':4},
              'COMRuntimeHeaderSize':{'offset':212,
                               'size':4},
              'Reserved2':{'offset':216,
                               'size':8},


              }

    def process_DLL_Characteristics(self, file_data, field_name, field_info):
        self.process_Characteristics(file_data, field_name, field_info)


    def process_field(self, file_data, field_name, field_info):

        if hasattr(self, 'process_'+field_name) and callable(getattr(self, 'process_'+field_name)):
            getattr(self, 'process_'+field_name)(file_data, field_name, field_info)
        else:
            absolute_offset = field_info['offset'] + self.absolute_offset
            size = field_info['size']
            self.size += size
            int_value, data = read_bytes(file_data, absolute_offset, size)
            field_name_dict = read_from_name_dict(self, field_name)
            name = field_name_dict.get(int_value, '')
            friendly_name = name.replace('_', ' ').capitalize()

            setattr(self, field_name, Structure(offset=field_info['offset'],
                                                size=size,
                                                value=int_value, data=data,
                                                absolute_offset=absolute_offset,
                                                name=name, friendly_name=friendly_name))
            getattr(self, field_name)._file_data = file_data

    @classmethod
    def parse_from_data(cls, file_data, **cls_args):
        """Parses the Structure from the file data."""
        self = cls(**cls_args)
        self._file_data = file_data
        magic, x = read_bytes(file_data, self.absolute_offset, 2)

        if magic == _32BIT_MAGIC:
            self._fields = self._fields_32
        elif magic == _32BIT_PLUS_MAGIC:
            self._fields = self._fields_32_plus
        else:
            print(magic, _32BIT_MAGIC, _32BIT_PLUS_MAGIC)
            raise PEFormatError('Magic for Optional Header is invalid.')

        for field_name, field_info in self._fields.items():
            self.process_field(file_data, field_name, field_info)

        return self

class SectionHeader(Structure):
    """Section Header. Each section header is a row in the section table"""

    _fields = {'Name':{'offset':0,
                           'size':8},
              'VirtualSize':{'offset':8, #.rsrc
                         'size':4},
              'VirtualAddress':{'offset':12,#.reloc
                                  'size':4},
              'SizeOfRawData':{'offset':16,#.rsrc
                               'size':4},
              'PointerToRawData':{'offset':20,#.reloc
                                      'size':4},
              'PointerToRelocations':{'offset':24,
                                 'size':4},
              'PointerToLineNumbers':{'offset':28,
                                      'size':4},
              'NumberOfRelocations':{'offset':32,
                                 'size':2},
              'NumberOfLineNumbers':{'offset':34,
                                 'size':2},
              'Characteristics':{'offset':36,
                                 'size':4}
              }

class ResourceDirectoryTable(Structure):
    _fields = {'Characteristics':{'offset':0,
                           'size':4},
              'TimeDateStamp':{'offset':4,
                         'size':4},
              'MajorVersion':{'offset':8,
                                  'size':2},
              'MinorVersion':{'offset':10,
                               'size':2},
              'NumberOfNameEntries':{'offset':12,
                                      'size':2},
              'NumberOfIDEntries':{'offset':14,
                                 'size':2}
              }

    def __init__(self, *args, **kwargs):
        self.name_entries = []
        self.id_entries = []
        self.subdirectory_tables = []
        self.data_entries = []

        super(ResourceDirectoryTable, self).__init__(*args, **kwargs)

class ResourceDirectoryEntryName(Structure):
    _fields = {'NameRVA':{'offset':0,
                           'size':4},
              'DataOrSubdirectoryEntryRVA':{'offset':4, #high bit 1 for subdir RVA
                         'size':4}
              }


    directory_string = None

    def is_data_entry(self):
        return (test_bit(self.DataOrSubdirectoryEntryRVA.value, 31) == 0)

    def data_rva_empty(self):
        return self.get_data_or_subdirectory_rva() == 0

    def get_data_or_subdirectory_rva(self, virtual_to_physical=0):
        return clear_bit(self.DataOrSubdirectoryEntryRVA.value-virtual_to_physical, 31)

    def get_data_or_subdirectory_absolute_offset(self):
        return self.get_data_or_subdirectory_rva() + self._section_header.PointerToRawData.value

    def get_name_absolute_offset(self):
        return clear_bit(self.NameRVA.value, 31) + self._section_header.PointerToRawData.value


class ResourceDirectoryEntryID(Structure):
    _fields = {'IntegerID':{'offset':0,
                           'size':4},
              'DataOrSubdirectoryEntryRVA':{'offset':4,#high bit 1 for Subdir RVA
                         'size':4}
              }


    def is_data_entry(self):
        return (test_bit(self.DataOrSubdirectoryEntryRVA.value, 31) == 0)

    def data_rva_empty(self):
        return self.get_data_or_subdirectory_rva() == 0

    def get_data_or_subdirectory_rva(self, virtual_to_physical=0):
        return clear_bit(self.DataOrSubdirectoryEntryRVA.value-virtual_to_physical, 31)

    def get_data_or_subdirectory_absolute_offset(self, vtp=0):
        return self.get_data_or_subdirectory_rva(vtp) + self._section_header.PointerToRawData.value

class ResourceDirectoryString(Structure):
    _fields = {'Length':{'offset':0,
                           'size':2},
                #String : offset=2, len=Length
              }

    @classmethod
    def parse_from_data(cls, file_data, **cls_args):
        """Parses the Structure from the file data."""
        self = cls(**cls_args)
        self._file_data = file_data
        str_len, _ = read_bytes(file_data, self.absolute_offset, 2)

        self._fields['String'] = {'offset':2, 'size':str_len}

        for field_name, field_info in self._fields.items():
            self.process_field(file_data, field_name, field_info)

        return self

    def process_String(self, file_data, field_name, field_info):
        absolute_offset = field_info['offset'] + self.absolute_offset
        size = field_info['size']
        self.size += size
        data = u''
        for i in range(size):
            val, dat = read_bytes(file_data, absolute_offset+i*2,2)
            data += str(dat, 'utf-8')

        setattr(self, field_name, Structure(offset=field_info['offset'],
                                            size=size,
                                            data=data,
                                            absolute_offset=absolute_offset,
                                            ))
class ResourceDataEntry(Structure):
    _fields = {'DataRVA':{'offset':0,
                           'size':4},
              'Size':{'offset':4,
                      'size':4},
              'Codepage':{'offset':8,
                      'size':4},
              'Reserved':{'offset':12,
                      'size':4},
              }


    def get_data_absolute_offset(self):
        return self._section_header.PointerToRawData.value - self._section_header.VirtualAddress.value + self.DataRVA.value

    def process_field(self, file_data, field_name, field_info):

        if hasattr(self, 'process_'+field_name) and callable(getattr(self, 'process_'+field_name)):
            getattr(self, 'process_'+field_name)(file_data, field_name, field_info)
        else:
            absolute_offset = field_info['offset'] + self.absolute_offset
            size = field_info['size']
            self.size += size
            int_value, data = read_bytes(file_data, absolute_offset, size)
            field_name_dict = read_from_name_dict(self, field_name)
            name = field_name_dict.get(int_value, '')
            friendly_name = name.replace('_', ' ').capitalize()

            setattr(self, field_name, Structure(offset=field_info['offset'],
                                                size=size,
                                                value=int_value, data=data,
                                                absolute_offset=absolute_offset,
                                                name=name, friendly_name=friendly_name))
            getattr(self, field_name)._file_data = file_data

    @classmethod
    def parse_from_data(cls, file_data, **cls_args):
        """Parses the Structure from the file data."""
        self = cls(**cls_args)
        self._file_data = file_data
        for field_name, field_info in self._fields.items():
            self.process_field(file_data, field_name, field_info)