/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 20.11.2015
 *
 * DLL loader declarations for 4coder
 *
 */

// TOP

struct DOS_Header {
    char signature[2];
    i16 lastsize;
    i16 nblocks;
    i16 nreloc;
    i16 hdrsize;
    i16 minalloc;
    i16 maxalloc;
    i16 ss;
    i16 sp;
    i16 checksum;
    i16 ip;
    i16 cs;
    i16 relocpos;
    i16 noverlay;
    i16 reserved1[4];
    i16 oem_id;
    i16 oem_info;
    i16 reserved2[10];
    i32 e_lfanew;
};

enum Target_Machine_Code{
    intel_i386 = 0x14C,
    intel_i860 = 0x14D,

    mips_r3000 = 0x162,
    mips_little_endian = 0x166,
    mips_r10000 = 0x168,

    old_alpha_axp = 0x183,
    alpha_axp = 0x184,

    hitachi_sh3 = 0x1a2,
    hitachi_sh3_dsp = 0x1a3,
    hitachi_sh4 = 0x1a6,
    hitachi_sh5 = 0x1a8,

    arm_little_endian = 0x1c0,
    thumb = 0x1c2,

    matsushita_am33 = 0x1d3,
    power_pc_little_endian = 0x1f0,
    power_pc_with_floating = 0x1f1,

    intel_ia64 = 0x200,
    mips16 = 0x266,
    motorola_68000_series = 0x268,
    
    alpha_axp_64_bit = 0x284,
    
    mips_with_fpu = 0x366,
    mips16_with_fpu = 0x466,
    eft_byte_code = 0xebc,
    
    amd_amd64 = 0x8664,
    mitsubishi_m32r_little_endian = 0x9041,
    clr_pure_msil = 0xc0ee
};

#define file_is_exe 0x2
#define file_is_non_reloctable 0x200
#define file_is_dll 0x2000

struct COFF_Header{
    u16 machine;
    u16 number_of_sections;
    u32 time_date_stamp;
    u32 pointer_to_symbol_table;
    u32 number_of_symbols;
    u16 size_of_optional_header;
    u16 characteristics;
};

struct Data_Directory{ 
    u32 virtual_address;
    u32 size;
};

// This version is untested
struct PE_Opt_Header_32Bit{
    // Universal Portion
    i16 signature;
    i8 major_linker_version; 
    i8 minor_linker_version;
    i32 size_of_code;
    i32 size_of_initialized_data;
    i32 size_of_uninitialized_data;
    i32 address_of_entry_point;
    i32 base_of_code;
    i32 base_of_data;
    
    // Windows Portion
    i32 image_base;
    i32 section_alignment;
    i32 file_alignment;
    i16 major_OS_version;
    i16 minor_OS_version;
    i16 major_image_version;
    i16 minor_image_version;
    i16 major_subsystem_version;
    i16 minor_subsystem_version;
    i32 reserved;
    i32 size_of_image;
    i32 size_of_headers;
    i32 checksum;
    i16 subsystem;
    i16 DLL_characteristics;
    i32 size_of_stack_reserve;
    i32 size_of_stack_commit;
    i32 size_of_heap_reserve;
    i32 size_of_heap_commit;
    i32 loader_flags;
    i32 number_of_rva_and_sizes;
    Data_Directory data_directory[16];
};

struct PE_Opt_Header_64Bit{
    // Universal Portion
    u16 signature;
    u8 major_linker_version; 
    u8 minor_linker_version;
    u32 size_of_code;
    u32 size_of_initialized_data;
    u32 size_of_uninitialized_data;
    u32 address_of_entry_point;
    u32 base_of_code;
    
    // Windows Portion
    u64 image_base;
    u32 section_alignment;
    u32 file_alignment;
    u16 major_OS_version;
    u16 minor_OS_version;
    u16 major_image_version;
    u16 minor_image_version;
    u16 major_subsystem_version;
    u16 minor_subsystem_version;
    u32 reserved;
    u32 size_of_image;
    u32 size_of_headers;
    u32 checksum;
    u16 subsystem;
    u16 DLL_characteristics;
    u64 size_of_stack_reserve;
    u64 size_of_stack_commit;
    u64 size_of_heap_reserve;
    u64 size_of_heap_commit;
    u32 loader_flags;
    u32 number_of_rva_and_sizes;
    Data_Directory data_directory[16];
};

#define bitsig_32bit 267
#define bitsig_64bit 523

#define image_dir_entry_export 0
#define image_dir_entry_import 1
#define image_dir_entry_resource 2
#define image_dir_base_reloc_table 5
#define image_dir_entry_bound_import 11

struct PE_Section_Definition{
    char name[8];
    u32 loaded_size;
    u32 loaded_location;
    u32 disk_size;
    u32 disk_location;
    u32 disk_relocs;
    u32 reserved1;
    u16 number_of_relocs;
    u16 reserved2;
    u32 flags;
};

#define image_scn_type_no_pad                0x00000008
#define image_scn_cnt_code                   0x00000020
#define image_scn_cnt_initialized_data       0x00000040
#define image_scn_cnt_uninitialized_data     0x00000080
#define image_scn_lnk_other                  0x00000100
#define image_scn_lnk_info                   0x00000200
#define image_scn_lnk_remove                 0x00000800
#define image_scn_lnk_comdat                 0x00001000
#define image_scn_no_defer_spec_exc          0x00004000
#define image_scn_gprel                      0x00008000
#define image_scn_mem_fardata                0x00008000
#define image_scn_mem_purgeable              0x00020000
#define image_scn_mem_16BIT                  0x00020000
#define image_scn_mem_locked                 0x00040000
#define image_scn_mem_preload                0x00080000

#define image_scn_align_1bytes               0x00100000
#define image_scn_align_2bytes               0x00200000
#define image_scn_align_4bytes               0x00300000
#define image_scn_align_8bytes               0x00400000
#define image_scn_align_16bytes              0x00500000
#define image_scn_align_32bytes              0x00600000
#define image_scn_align_64bytes              0x00700000
#define image_scn_align_128bytes             0x00800000
#define image_scn_align_256bytes             0x00900000
#define image_scn_align_512bytes             0x00A00000
#define image_scn_align_1024bytes            0x00B00000
#define image_scn_align_2048bytes            0x00C00000
#define image_scn_align_4096bytes            0x00D00000
#define image_scn_align_8192bytes            0x00E00000
#define image_scn_align_mask                 0x00F00000

#define image_scn_lnk_nreloc_ovfl            0x01000000
#define image_scn_mem_discardable            0x02000000
#define image_scn_mem_not_cached             0x04000000
#define image_scn_mem_not_paged              0x08000000
#define image_scn_mem_shared                 0x10000000
#define image_scn_mem_execute                0x20000000
#define image_scn_mem_read                   0x40000000
#define image_scn_mem_write                  0x80000000

#pragma pack(push, 1)
struct COFF_Relocation{
    u32 virtual_address;
    u32 symbol_table_index;
    u16 type;
};
#pragma pack(pop)

enum Image_Rel_Amd64{
    image_rel_amd64_absolute  = 0x00,
    image_rel_amd64_addr64    = 0x01,
    image_rel_amd64_addr32    = 0x02,
    image_rel_amd64_addr32nb  = 0x03,
    image_rel_amd64_rel32     = 0x04,
    image_rel_amd64_rel32_1   = 0x05,
    image_rel_amd64_rel32_2   = 0x06,
    image_rel_amd64_rel32_3   = 0x07,
    image_rel_amd64_rel32_4   = 0x08,
    image_rel_amd64_rel32_5   = 0x09,
    image_rel_amd64_section   = 0x0A,
    image_rel_amd64_secrel    = 0x0B,
    image_rel_amd64_secrel7   = 0x0C,
    image_rel_amd64_token     = 0x0D,
    image_rel_amd64_srel32    = 0x0E,
    image_rel_amd64_pair      = 0x0F,
    image_rel_amd64_sspan32   = 0x10
};

enum Image_Rel_Arm{
    image_rel_arm_absolute    = 0x0,
    image_rel_arm_addr32      = 0x1,
    image_rel_arm_addr32nb    = 0x2,
    image_rel_arm_branch24    = 0x3,
    image_rel_arm_branch11    = 0x4,
    image_rel_arm_token       = 0x5,
    image_rel_arm_blx24       = 0x6,
    image_rel_arm_blx11       = 0x7,
    image_rel_arm_section     = 0x8,
    image_rel_arm_secrel      = 0x9,
    image_rel_arm_mov32a      = 0xA,
    image_rel_arm_mov32t      = 0xB,
    image_rel_arm_branch20t   = 0xC,
    image_rel_arm_branch24t   = 0xD,
    image_rel_arm_blx32t      = 0xE
};

enum Image_Rel_Arm64{
    image_rel_arm64_absolute        = 0x0,
    image_rel_arm64_addr32          = 0x1,
    image_rel_arm64_addr32nb        = 0x2,
    image_rel_arm64_branch26        = 0x3,
    image_rel_arm64_pagebase_rel21  = 0x4,
    image_rel_arm64_rel21           = 0x5,
    image_rel_arm64_pageoffset_12a  = 0x6,
    image_rel_arm64_pageoffset_12l  = 0x7,
    image_rel_arm64_secrel          = 0x8,
    image_rel_arm64_secrel_low12a   = 0x9,
    image_rel_arm64_secrel_high12a  = 0xA,
    image_rel_arm64_secrel_low12l   = 0xB,
    image_rel_arm64_token           = 0xC,
    image_rel_arm64_section         = 0xD,
    image_rel_arm64_addr64          = 0xE
};

// NOTE(allen):
//  skipped Hitachi SuperH
//  skiiped IBM PowerPC

enum Image_Rel_i386{
    image_rel_i386_absolute = 0x0,
    image_rel_i386_dir16    = 0x1,
    image_rel_i386_rel16    = 0x2,
    image_rel_i386_dir32    = 0x3,
    image_rel_i386_dir32nb  = 0x4,
    image_rel_i386_seg12    = 0x5,
    image_rel_i386_section  = 0x6,
    image_rel_i386_secrel   = 0x7,
    image_rel_i386_token    = 0x8,
    image_rel_i386_secrel7  = 0x9,
    image_rel_i386_rel32    = 0xA
};

// NOTE(allen):
//  skipped ia64
//  skipped MIPS
//  skiiped Mitsubishi

struct Relocation_Block_Header{
    u32 page_base_offset;
    u32 block_size;
};

#define reloc_entry_type_mask   0xF000
#define reloc_entry_type_shift  12
#define reloc_entry_offset_mask 0x0FFF

struct Relocation_Block_Entry{
    u16 entry;
};

enum DLL_Relocation_Type{
    image_base_absolute,
    // nothing 
    
    image_base_high,
    // add high 16 bits of diff to 16 bits at offset 
    
    image_base_low,
    // add low 16 bits of diff to 16 bits at offset 
    
    image_base_highlow,
    // adds all 32 bits to 32 bits at offset 
    
    image_base_highadj,
    // consumes two slots: high 16 bits at location, low 16 bits at next location
    
    image_base_arm_mov32a,
    // mips: jump instruction; arm: MOVW+MOVT 
    
    image_base_reserved1,
    
    image_base_arm_mov32t,
    // MOVW+MOVT in Thumb mode 
    
    image_base_reserved2,
    
    image_base_mips_jmpaddr16,
    // mips16 jump instruction 
    
    image_base_dir64
    // adds to 64 bits field
};

struct DLL_Data{
    DOS_Header *dos_header;
    COFF_Header *coff_header;
    PE_Opt_Header_32Bit *opt_header_32;
    PE_Opt_Header_64Bit *opt_header_64;
    PE_Section_Definition *section_defs;
    b32 is_64bit;
};

struct DLL_Loaded{
    DOS_Header *dos_header;
    COFF_Header *coff_header;
    PE_Opt_Header_32Bit *opt_header_32;
    PE_Opt_Header_64Bit *opt_header_64;
    PE_Section_Definition *section_defs;
    b32 is_64bit;
    
    Data img;
    u32 export_start;
    u32 text_start;
    u32 text_size;
};

struct DLL_Export_Directory_Table{
    u32 export_flags;
    u32 time_date_stamp;
    u16 major_version;
    u16 minor_version;
    u32 name_offset;
    u32 ordinal_base;
    u32 number_of_addresses;
    u32 number_of_name_pointers;
    u32 address_offset;
    u32 name_pointer_offset;
    u32 ordinal_offset;
};

struct DLL_Export_Address{
    u32 export_offset;
};

struct DLL_Export_Name{
    u32 name_offset;
};

struct DLL_Export_Ordinal{
    u16 ordinal;
};

struct DLL_Debug_Entry{
    u32 characteristics;
    u32 time_date_stamp;
    u16 major_version;
    u16 minor_version;
    u32 type;
    u32 size_of_data;
    u32 offset_of_data;
    u32 disk_offset_of_data;
} thingy;

enum DLL_Debug_Type{
    img_dbg_type_unknown,
    img_dbg_type_coff,
    img_dbg_type_codeview,
    img_dbg_type_fpo,
    img_dbg_type_misc,
    img_dbg_type_exception,
    img_dbg_type_fixup,
    img_dbg_type_omap_to_src,
    img_dbg_type_omap_from_src
};

char DOS_error_message[] = "This program cannot be run in DOS mode.";
i32 DOS_error_offset = 0x4E;
i32 DOS_error_size = sizeof(DOS_error_message) - 1;

char PE_header[] = {'P', 'E', 0, 0};
i32 PE_header_size = 4;

// BOTTOM