#ifndef MR4TH_PE_H
#define MR4TH_PE_H

////////////////////////////////
// Types: COFF / PE

#pragma pack(push,1)

// MS-DOS Stub

// located at offset=0 in valid executables
#define PE_MSDOS_MAGIC 0x5A4D

typedef struct PE_DosHeader{
  U16 magic;
  U16 last_page_size;
  U16 page_count;
  U16 reloc_count;
  U16 paragraph_header_size;
  U16 min_paragraph;
  U16 max_paragraph;
  U16 init_ss;
  U16 init_sp;
  U16 checksum;
  U16 init_ip;
  U16 init_cs;
  U16 reloc_table_file_off;
  U16 overlay_number;
  U16 reserved[4];
  U16 oem_id;
  U16 oem_info;
  U16 reserved2[10];
  U32 coff_file_offset;
} PE_DosHeader;

// pe signature

#define PE_SIGNATURE 0x00004550

// coff header

#define PE_MachineType_XList(X)\
X(UNKNOWN,   0x0)\
X(ALPHA,     0x184)\
X(ALPHA64,   0x284)\
X(AM33,      0x1d3)\
X(AMD64,     0x8664)\
X(ARM,       0x1c0)\
X(ARM64,     0xaa64)\
X(ARMNT,     0x1c4)\
X(EBC,       0xebc)\
X(I386,      0x14c)\
X(IA64,      0x200)\
X(LOONGARCH32, 0x6232)\
X(LOONGARCH64, 0x6264)\
X(M32R,      0x9041)\
X(MIPS16,    0x266)\
X(MIPSFPU,   0x366)\
X(MIPSFPU16, 0x466)\
X(POWERPC,   0x1f0)\
X(POWERPCFP, 0x1f1)\
X(R4000,     0x166)\
X(RISCV32,   0x5032)\
X(RISCV64,   0x5064)\
X(RISCV128,  0x5128)\
X(SH3,       0x1a2)\
X(SH3DSP,    0x1a3)\
X(SH4,       0x1a6)\
X(SH5,       0x1a8)\
X(THUMB,     0x1c2)\
X(WCEMIPSV2, 0x169)

typedef U16 PE_MachineType;
enum{
#define X(N,C) PE_MachineType_##N = C,
  PE_MachineType_XList(X)
#undef X
};

#define PE_Flags_XList(X)\
X(RELOCS_STRIPPED,     0x0001)\
X(EXECUTABLE_IMAGE,    0x0002)\
X(LINE_NUMS_STRIPPED,  0x0004)\
X(LOCAL_SYMS_STRIPPED, 0x0008)\
X(AGGRESSIVE_WS_TRIM,  0x0010)\
X(LARGE_ADDRESS_AWARE, 0x0020)\
X(RESERVED,            0x0040)\
X(BYTES_REVERSED_LO,   0x0080)\
X(32BIT_MACHINE,       0x0100)\
X(DEBUG_STRIPPED,      0x0200)\
X(REMOVABLE_RUN_FROM_SWAP, 0x0400)\
X(NET_RUN_FROM_SWAP,   0x0800)\
X(SYSTEM,              0x1000)\
X(DLL,                 0x2000)\
X(UP_SYSTEM_ONLY,      0x4000)\
X(BYTES_REVERSED_HI,   0x8000)\

typedef U16 PE_Flags;
enum{
#define X(N,C) PE_Flag_##N = C,
  PE_Flags_XList(X)
#undef X
};

typedef struct PE_CoffHeader{
  PE_MachineType machine_type;
  U16 section_count;
  U32 time_stamp;
  U32 symbol_table_foff;
  U32 symbol_count;
  U16 optional_header_size;
  PE_Flags flags;
} PE_CoffHeader;

// optional headers

#define PE_OptionalHeaderMagic_XList(X)\
X(ROM,      0x107)\
X(PE32,     0x10B)\
X(PE32Plus, 0x20B)

typedef U16 PE_OptionalHeaderMagic;
enum{
#define X(N,C) PE_OptionalHeaderMagic_##N = C,
  PE_OptionalHeaderMagic_XList(X)
#undef X
};

#define PE_WindowsSubsystem_XList(X)\
X(UNKNOWN,                   0)\
X(NATIVE,                    1)\
X(WINDOWS_GUI,               2)\
X(WINDOWS_CUI,               3)\
X(OS2_CUI,                   5)\
X(POSIX_CUI,                 7)\
X(NATIVE_WINDOWS,            8)\
X(WINDOWS_CE_GUI,            9)\
X(EFI_APPLICATION,          10)\
X(EFI_BOOT_SERVICE_DRIVER,  11)\
X(EFI_RUNTIME_DRIVER,       12)\
X(EFI_ROM,                  13)\
X(XBOX,                     14)\
X(WINDOWS_BOOT_APPLICATION, 16)

typedef U16 PE_WindowsSubsystem;
enum{
#define X(N,C) PE_WindowsSubsystem_##N = C,
  PE_WindowsSubsystem_XList(X)
#undef X
};

#define PE_DLLFlags_XList(X)\
X(RESERVED0,             0x0001)\
X(RESERVED1,             0x0002)\
X(RESERVED2,             0x0004)\
X(RESERVED3,             0x0008)\
X(HIGH_ENTROPY_VA,       0x0020)\
X(DYNAMIC_BASE,          0x0040)\
X(FORCE_INTEGRITY,       0x0080)\
X(NX_COMPAT,             0x0100)\
X(NO_ISOLATION,          0x0200)\
X(NO_SEH,                0x0400)\
X(NO_BIND,               0x0800)\
X(APPCONTAINER,          0x1000)\
X(WDM_DRIVER,            0x2000)\
X(GUARD_CF,              0x4000)\
X(TERMINAL_SERVER_AWARE, 0x8000)

typedef U16 PE_DLLFlags;
enum{
#define X(N,C) PE_DLLFlag_##N = C,
  PE_DLLFlags_XList(X)
#undef X
};

typedef struct PE_OptionalHeader32{
  PE_OptionalHeaderMagic magic;
  U8  linker_version_major;
  U8  linker_version_minor;
  U32 total_size_text; // 'text' means executable code here
  U32 total_size_inited_data;
  U32 total_size_zeroed_data;
  U32 entry_point_voff;
  U32 text_voff;
  U32 data_voff;
  U32 image_base_vaddr;
  U32 section_alignment;
  U32 file_alignment;
  U16 os_version_major;
  U16 os_version_minor;
  U16 image_version_major;
  U16 image_version_minor;
  U16 subsystem_version_major;
  U16 subsystem_version_minor;
  U32 reserved0;
  U32 image_size;
  U32 headers_size;
  U32 checksum;
  PE_WindowsSubsystem subsystem;
  PE_DLLFlags dll_flags;
  U32 stack_size_reserve;
  U32 stack_size_commit;
  U32 heap_size_reserve;
  U32 heap_size_commit;
  U32 reserved1;
  U32 data_directory_count;
} PE_OptionalHeader32;

typedef struct PE_OptionalHeader32Plus{
  U16 magic;
  U8  linker_version_major;
  U8  linker_version_minor;
  U32 total_size_text; // 'text' means executable code here
  U32 total_size_inited_data;
  U32 total_size_zeroed_data;
  U32 entry_point_voff;
  U32 text_voff;
  U64 image_base_vaddr;
  U32 section_alignment;
  U32 file_alignment;
  U16 os_version_major;
  U16 os_version_minor;
  U16 image_version_major;
  U16 image_version_minor;
  U16 subsystem_version_major;
  U16 subsystem_version_minor;
  U32 reserved0;
  U32 image_size;
  U32 headers_size;
  U32 checksum;
  PE_WindowsSubsystem subsystem;
  PE_DLLFlags dll_flags;
  U64 stack_size_reserve;
  U64 stack_size_commit;
  U64 heap_size_reserve;
  U64 heap_size_commit;
  U32 reserved1;
  U32 data_directory_count;
} PE_OptionalHeader32Plus;

typedef struct PE_DataDirectory{
  U32 voff;
  U32 size;
} PE_DataDirectory;

#define PE_DataDirectoryIdx_XList(X)\
X(ExportTable,           "Export Table",             0)\
X(ImportTable,           "Import Table",             1)\
X(ResourceTable,         "Resource Table",           2)\
X(ExceptionTable,        "Exception Table",          3)\
X(CertificateTable,      "Certificate Table",        4)\
X(BaseRelocationTable,   "Base Relocation Table",    5)\
X(Debug,                 "Debug Directories",        6)\
X(Architecture,          "Architecture",             7)\
X(GlobalPtr,             "Global Ptr",               8)\
X(TLSTable,              "TLS Table",                9)\
X(LoadConfigTable,       "Load Config Table",       10)\
X(BoundImport,           "Bound Import",            11)\
X(IAT,                   "IAT",                     12)\
X(DelayImportDescriptor, "Delay Import Descriptor", 13)\
X(CLRRuntimeHeader,      "CLR Runtime Header",      14)\
X(Reserved,              "Reserved",                15)

typedef U32 PE_DataDirectoryIdx;
enum{
#define X(N,D,C) PE_DataDirectoryIdx_##N = C,
  PE_DataDirectoryIdx_XList(X)
#undef X
  PE_DataDirectoryIdx_COUNT
};

// section table

#define PE_SectionFlags_XList(X)\
X(TYPE_NO_PAD,            0x00000008)\
X(CNT_CODE,               0x00000020)\
X(CNT_INITIALIZED_DATA,   0x00000040)\
X(CNT_UNINITIALIZED_DATA, 0x00000080)\
X(LNK_OTHER,              0x00000100)\
X(LNK_INFO,               0x00000200)\
X(LNK_REMOVE,             0x00000800)\
X(LNK_COMDAT,             0x00001000)\
X(GPREL,                  0x00008000)\
X(MEM_16BIT,              0x00020000)\
X(MEM_LOCKED,             0x00040000)\
X(MEM_PRELOAD,            0x00080000)\
X(LNK_NRELOC_OVFL,        0x01000000)\
X(MEM_DISCARDABLE,        0x02000000)\
X(MEM_NOT_CACHED,         0x04000000)\
X(MEM_NOT_PAGED,          0x08000000)\
X(MEM_SHARED,             0x10000000)\
X(MEM_EXECUTE,            0x20000000)\
X(MEM_READ,               0x40000000)\
X(MEM_WRITE,              0x80000000)

#define PE_SectionAlignCodeFromSectionFlags(f) ((f)&0x00F00000)

#define PE_SectionAlignCode_XList(X)\
X(1,    0x00100000)\
X(2,    0x00200000)\
X(4,    0x00300000)\
X(8,    0x00400000)\
X(16,   0x00500000)\
X(32,   0x00600000)\
X(64,   0x00700000)\
X(128,  0x00800000)\
X(256,  0x00900000)\
X(512,  0x00A00000)\
X(1024, 0x00B00000)\
X(2048, 0x00C00000)\
X(4096, 0x00D00000)\
X(8192, 0x00E00000)

typedef U32 PE_SectionFlags;
enum{
#define X(N,C) PE_SectionFlag_##N = C,
  PE_SectionFlags_XList(X)
#undef X
  
#define X(N,C) PE_SectionAlign_##N = C,
  PE_SectionAlignCode_XList(X)
#undef X
};

typedef struct PE_SectionHeader{
  U8  name[8];
  U32 vsize;
  U32 voff;
  U32 fsize;
  U32 foff;
  U32 relocations_foff;
  U32 line_numbers_foff;
  U16 relocation_count;
  U16 line_number_count;
  PE_SectionFlags flags;
} PE_SectionHeader;

// coff relocations

// TODO(allen): these are just the "x64" values
//              generalize PE_CoffRelocationType to all machines
#define PE_CoffRelocationType_XList(X)\
X(ABSOLUTE, 0x0000)\
X(ADDR64,   0x0001)\
X(ADDR32,   0x0002)\
X(ADDR32NB, 0x0003)\
X(REL32,    0x0004)\
X(REL32_1,  0x0005)\
X(REL32_2,  0x0006)\
X(REL32_3,  0x0007)\
X(REL32_4,  0x0008)\
X(REL32_5,  0x0009)\
X(SECTION,  0x000A)\
X(SECREL,   0x000B)\
X(SECREL7,  0x000C)\
X(TOKEN,    0x000D)\
X(SREL32,   0x000E)\
X(PAIR,     0x000F)\
X(SSPAN32,  0x0010)

typedef U16 PE_CoffRelocationType;
enum{
#define X(N,C) PE_CoffRelocationType_##N = C,
  PE_CoffRelocationType_XList(X)
#undef X
};

typedef struct PE_CoffRelocation{
  U32 off;
  U32 symbol_idx;
  PE_CoffRelocationType type;
} PE_CoffRelocation;

// coff symbol table

#define PE_CoffSymbolStorageClass_XList(X)\
X(END_OF_FUNCTION, 0xFF)\
X(NULL,               0)\
X(AUTOMATIC,          1)\
X(EXTERNAL,           2)\
X(STATIC,             3)\
X(REGISTER,           4)\
X(EXTERNAL_DEF,       5)\
X(LABEL,              6)\
X(UNDEFINED_LABEL,    7)\
X(MEMBER_OF_STRUCT,   8)\
X(ARGUMENT,           9)\
X(STRUCT_TAG,        10)\
X(MEMBER_OF_UNION,   11)\
X(UNION_TAG,         12)\
X(TYPE_DEFINITION,   13)\
X(UNDEFINED_STATIC,  14)\
X(ENUM_TAG,          15)\
X(MEMBER_OF_ENUM,    16)\
X(REGISTER_PARAM,    17)\
X(BIT_FIELD,         18)\
X(BLOCK,            100)\
X(FUNCTION,         101)\
X(END_OF_STRUCT,    102)\
X(FILE,             103)\
X(SECTION,          104)\
X(WEAK_EXTERNAL,    105)\
X(CLR_TOKEN,        107)

typedef U8 PE_CoffSymbolStorageClass;
enum{
#define X(N,C) PE_CoffSymbolStorageClass_##N = C,
  PE_CoffSymbolStorageClass_XList(X)
#undef X
};

#define PE_CoffSymbolTypeLo_XList(X)\
X(NULL,   0)\
X(VOID,   1)\
X(CHAR,   2)\
X(SHORT,  3)\
X(INT,    4)\
X(LONG,   5)\
X(FLOAT,  6)\
X(DOUBLE, 7)\
X(STRUCT, 8)\
X(UNION,  9)\
X(ENUM,  10)\
X(MOE,   11)\
X(BYTE,  12)\
X(WORD,  13)\
X(UINT,  14)\
X(DWORD, 15)

enum{
#define X(N,C) PE_CoffSymbolTypeLo_##N = C,
  PE_CoffSymbolTypeLo_XList(X)
#undef X
};

#define PE_CoffSymbolTypeHi_XList(X)\
X(NULL,     0)\
X(POINTER,  1)\
X(FUNCTION, 2)\
X(ARRAY,    3)

enum{
#define X(N,C) PE_CoffSymbolTypeHi_##N = C,
  PE_CoffSymbolTypeHi_XList(X)
#undef X
};

#define PE_CoffSymbolType(lo,hi) \
(((PE_CoffSymbolTypeHi_##hi) << 4) | (PE_CoffSymbolTypeLo_##lo))

typedef U16 PE_CoffSymbolType;

typedef struct PE_CoffSymbolRecord{
  union{
    U8 raw[8];
    struct{
      U32 zero;
      U32 string_table_off;
    };
  } name;
  U32 val;
  S16 sec_number;
  PE_CoffSymbolType type;
  PE_CoffSymbolStorageClass storage_class;
  U8 aux_symbol_count;
} PE_CoffSymbolRecord;

/// NOTE(allen): have not observed this in real data
typedef struct PE_CoffAuxFunctionDefinition{
  U32 tag_index;
  U32 total_size;
  U32 foff_to_line_number;
  U32 foff_to_next_function;
  U16 unused;
} PE_CoffAuxFunctionDefinition;

/// NOTE(allen): have not observed this in real data
typedef struct PE_CoffAuxFunctionBeginEnd{
  U16 unused1[2];
  U16 line_number;
  U16 unused2[3];
  U32 foff_to_next_function;
  U16 unused3;
} PE_CoffAuxFunctionBeginEnd;

/// NOTE(allen): have not observed this in real data
typedef struct PE_CoffAuxWeakExternal{
  U32 tag_index;
  U32 characteristics;
  U16 unused[5];
} PE_CoffAuxWeakExternal;

/// NOTE(allen): have not observed this in real data
typedef struct PE_CoffAuxFile{
  U8 file_name[18];
} PE_CoffAuxFile;

#define PE_CoffComdatSelection_XList(X)\
X(NULL,         0)\
X(NODUPLICATES, 1)\
X(ANY,          2)\
X(SAME_SIZE,    3)\
X(EXACT_MATCH,  4)\
X(ASSOCIATIVE,  5)\
X(LARGEST,      6)

typedef U8 PE_CoffComdatSelection;
enum{
#define X(N,C) PE_CoffComdatSelection_##N = C,
  PE_CoffComdatSelection_XList(X)
#undef X
};

typedef struct PE_CoffAuxSection{
  U32 length;
  U16 relocation_count;
  U16 line_number_count;
  U32 checksum;
  U16 linked_section_number;
  PE_CoffComdatSelection comdat_selection;
  U8  unused[3];
} PE_CoffAuxSection;

// TODO(allen): Attribute Certificate Table

// TODO(allen): Delay-Load Import Tables

// export data

typedef struct PE_ExportDirectoryTable{
  U32 reserved0;
  U32 time_stamp;
  U16 version_major;
  U16 version_minor;
  U32 binary_name_voff;
  U32 ordinal_base;
  U32 export_count;
  U32 ordinal_count;
  U32 export_voff;
  U32 name_voff;
  U32 ordinal_voff;
  
  /*
 ** NOTE(allen): Additional Structure Information
** export  : [ export_count] U32;
** name    : [ordinal_count] U32;
** ordinal : [ordinal_count] U16;
**
** for i in [0,ordinal_count):
**  name[i]            : voff to a cstring
**  export[ordinal[i]] : voff to a symbol OR voff to forwarding string
**                       (voffs in export range are fowarding strings)
**  ordinal[i] : "unbiased ordinal"
**  ordinal[i] + ordinal_base : "ordinal"
  */
  
} PE_ExportDirectoryTable;

// import data

typedef struct PE_ImportDirectoryTable{
  U32 import_lookup_table_voff;
  U32 time_stamp;
  U32 first_forwarder_idx;
  U32 name_voff;
  U32 import_address_table_voff;
  
  /*
  ** NOTE(allen): Additional Structure Information
** IF PE32:
**  import_lookup_table : null-terminated [] U32;
** IF PE32Plus:
**  import_lookup_table : null-terminated [] U64;
**
** WHEN lookup = import_lookup_table[i]
**  if (HighBits1(lookup)){
 **   ordinal = LowBits16(lookup)
**  }
**  else{
**   hint_voff = LowBits31(lookup)
**   hint : { U16, null-terminated-cstring }
**  }
  */
  
} PE_ImportDirectoryTable;

// exception handling

#if 0
// NOTE(allen): untested - from documentation
typedef struct PE_ExceptionHandler_MIPS{
  U32 begin_vaddr;
  U32 end_vaddr;
  U32 handler_vaddr;
  U32 handler_data;
  U32 prolog_end_vaddr;
} PE_ExceptionHandler_MIPS;
#endif

#if 0
// NOTE(allen): untested - from documentation
typedef struct PE_ExceptionHandler_ARM{
  U32 begin_vaddr;
  U32 info;
} PE_ExceptionHandler_ARM;

#define PE_ExceptionHandler_ARMInfo_PrologLength(i)  (((i) >>  0)&0xFF)
#define PE_ExceptionHandler_ARMInfo_FuncLength(i)    (((i) >>  8)&0x3FFFFF)
#define PE_ExceptionHandler_ARMInfo_32Bit(i)         (((i) >> 30)&0x1)
#define PE_ExceptionHandler_ARMInfo_ExceptionFlag(i) (((i) >> 31)&0x1)
#endif

typedef struct PE_ExceptionHandler_X86{
  U32 begin_voff;
  U32 end_voff;
  U32 unwind_voff;
} PE_ExceptionHandler_X86;

typedef struct PE_UnwindInfo{
  U8 header;
  U8 prolog_size;
  U8 code_count;
  U8 frame;
} PE_UnwindInfo;

#define PE_UnwindInfo_VersionFromHeader(x) ((x)&7)
#define PE_UnwindInfo_FlagsFromHeader(x) (((x)>>3)&0x1F)

#define PE_UnwindInfoFlags_XList(X)\
X(EHANDLER, (1 << 0))\
X(UHANDLER, (1 << 1))\
X(CHAINED,  (1 << 2))

typedef U8 PE_UnwindInfoFlags;
enum{
#define X(N,C) PE_UnwindInfoFlag_##N = C,
  PE_UnwindInfoFlags_XList(X)
#undef X
};

#define PE_UnwindInfo_RegFromFrame(x) ((x)&0xF)
#define PE_UnwindInfo_OffFromFrame(x) (((x) >> 4)&0xF)

#define PE_UnwindOpCode_XList(X)\
X(PUSH_NONVOL,      0)\
X(ALLOC_LARGE,      1)\
X(ALLOC_SMALL,      2)\
X(SET_FPREG,        3)\
X(SAVE_NONVOL,      4)\
X(SAVE_NONVOL_FAR,  5)\
X(EPILOG,           6)\
X(SPARE,            7)\
X(SAVE_XMM128,      8)\
X(SAVE_XMM128_FAR,  9)\
X(PUSH_MACHFRAME,  10)

typedef U8 PE_UnwindOpCode;
enum{
#define X(N,C) PE_UnwindOpCode_##N = C,
  PE_UnwindOpCode_XList(X)
#undef X
};

#define PE_UnwindCode_GetOffset(x) (((x) >>  0)&0xFF)
#define PE_UnwindCode_GetOpCode(x) (((x) >>  8)&0x0F)
#define PE_UnwindCode_GetOpInfo(x) (((x) >> 12)&0x0F)

#define PE_UnwindCode_FromOffsetOpCodeInfo(off,opcode,opinfo) \
(((off)&0xFF) | (((opcode)&0xF) << 8) | (((opinfo)&0xF) << 12))

#define PE_UnwindRegX64_XList(X)\
X(RAX,  0)\
X(RCX,  1)\
X(RDX,  2)\
X(RBX,  3)\
X(RSP,  4)\
X(RBP,  5)\
X(RSI,  6)\
X(RDI,  7)\
X(R8,   8)\
X(R9,   9)\
X(R10, 10)\
X(R11, 11)\
X(R12, 12)\
X(R13, 13)\
X(R14, 14)\
X(R15, 15)

typedef U8 PE_UnwindRegX64;
enum{
#define X(N,C) PE_UnwindRegX64_##N = C,
  PE_UnwindRegX64_XList(X)
#undef X
};

// base relocations

typedef struct PE_BaseRelocationBlock{
  U32 page_voff;
  U32 block_size;
} PE_BaseRelocationBlock;

typedef U16 PE_BaseRelocationTypeOffset;

#define PE_BaseRelocationType_XList(X)\
X(ABSOLUTE,             0)\
X(HIGH,                 1)\
X(LOW,                  2)\
X(HIGHLOW,              3)\
X(HIGHADJ,              4)\
X(SPLIT1,               5)\
X(SPLIT2,               7)\
X(SPLIT3,               8)\
X(MIPS_JMPADDR16,       9)\
X(DIR64,               10)

typedef U8 PE_BaseRelocationType;
enum{
#define X(N,C) PE_BaseRelocationType_##N = C,
  PE_BaseRelocationType_XList(X)
#undef X
};

// SPLIT1: MIPS  -> JMPADDR, ARM -> MOV32,   RISCV -> HIGH20
// SPLIT2: THUMB -> MOV32,   RISCV -> LOW12I
// SPLIT3: RISCV -> LOW12S,  LOONGARCH32 -> MARK_LA, LOONGARCH64 -> MARK_LA

#define PE_BaseRelocationTypeOffset_GetType(X) (((X) >> 12)&0xF)
#define PE_BaseRelocationTypeOffset_GetOff(X)  (((X) >>  0)&0xFFF)

// debug

#define PE_DebugType_XList(X)\
X(UNKNOWN,       0)\
X(COFF,          1)\
X(CODEVIEW,      2)\
X(FPO,           3)\
X(MISC,          4)\
X(EXCEPTION,     5)\
X(OMAP_TO_SRC,   7)\
X(OMAP_FROM_SRC, 8)\
X(BORLAND,       9)\
X(RESERVED10,   10)\
X(CLSID,        11)\
X(VC_FEATURE,   12)\
X(POGO,         13)\
X(ILTCG,        14)\
X(MPX,          15)\
X(REPRO,        16)\
X(EMBEDDED,     17)\
X(CRYPTOHASH,   19)\
X(EX_DLLCHARACTERISTICS, 20)

typedef U32 PE_DebugType;
enum{
#define X(N,C) PE_DebugType_##N = C,
  PE_DebugType_XList(X)
#undef X
};

typedef struct PE_DebugDirectory{
  U32 reserved0;
  U32 time_stamp;
  U16 version_major;
  U16 version_minor;
  PE_DebugType type;
  U32 size;
  U32 voff;
  U32 foff;
} PE_DebugDirectory;

/* TODO(allen): 
** [ ] Can we gather information about debug info types besides CODEVIEW ?
** [ ] What are the first 24 bytes of the CODEVIEW data range?
*/

// tls table

typedef U32 PE_TlsDirectoryFlags;
#define PE_TLSDirectoryFlags_GetAlign(X) ((X)&0x00F00000)

typedef struct PE_TLSDirectory32{
  U32 raw_data_start_vaddr;
  U32 raw_data_end_vaddr;
  U32 tls_index_vaddr;
  U32 tls_callback_array_vaddr;
  U32 zero_fill_size;
  PE_TlsDirectoryFlags flags;
} PE_TLSDirectory32;

typedef struct PE_TLSDirectory32Plus{
  U64 raw_data_start_vaddr;
  U64 raw_data_end_vaddr;
  U64 tls_index_vaddr;
  U64 tls_callback_array_vaddr;
  U32 zero_fill_size;
  PE_TlsDirectoryFlags flags;
} PE_TLSDirectory32Plus;

// load config table

/* TODO(allen): pneumonics for load config that are missing
** [ ] global flags
** [ ] process affinity mask
** [ ] process heap flags
** [ ] control flow guard flags
*/

typedef struct PE_LoadConfig32{
  U32 reserved0;
  U32 time_stamp;
  U16 version_major;
  U16 version_minor;
  U32 global_flags_clear;
  U32 global_flags_set;
  U32 default_critical_section_timeout;
  U32 decommit_free_block_threshold;
  U32 decommit_total_free_threshold;
  U32 lock_prefix_table_vaddr;
  U32 maximum_allocation_size;
  U32 virtual_memory_threshold;
  U32 process_affinity_mask;
  U32 process_heap_flags;
  U16 csd_version;
  U16 reserved1;
  U32 reserved2;
  U32 security_cookie_vaddr;
  U32 seh_table_vaddr;
  U32 seh_count;
  U32 control_flow_guard_check_function_vaddr;
  U32 control_flow_guard_dispatch_function_vaddr;
  U32 control_flow_guard_function_table_vaddr;
  U32 control_flow_guard_function_count;
  U32 control_flow_guard_flags;
  U8  code_integrity[12];
  U32 guard_address_taken_iat_table_vaddr;
  U32 guard_address_taken_iat_count;
  U32 guard_long_jump_target_table_vaddr;
  U32 guard_long_jump_target_count;
} PE_LoadConfig32;

typedef struct PE_LoadConfig64{
  U32 reserved0;
  U32 time_stamp;
  U16 version_major;
  U16 version_minor;
  U32 global_flags_clear;
  U32 global_flags_set;
  U32 default_critical_section_timeout;
  U64 decommit_free_block_threshold;
  U64 decommit_total_free_threshold;
  U64 lock_prefix_table_vaddr;
  U64 maximum_allocation_size;
  U64 virtual_memory_threshold;
  U64 process_affinity_mask;
  U32 process_heap_flags;
  U16 csd_version;
  U16 reserved1;
  U64 reserved2;
  U64 security_cookie_vaddr;
  U64 seh_table_vaddr;
  U64 seh_count;
  U64 control_flow_guard_check_function_vaddr;
  U64 control_flow_guard_dispatch_function_vaddr;
  U64 control_flow_guard_function_table_vaddr;
  U64 control_flow_guard_function_count;
  U32 control_flow_guard_flags;
  U8  code_integrity[12];
  U64 guard_address_taken_iat_table_vaddr;
  U64 guard_address_taken_iat_count;
  U64 guard_long_jump_target_table_vaddr;
  U64 guard_long_jump_target_count;
} PE_LoadConfig64;

#pragma pack(pop)


////////////////////////////////
// Functions: COFF / PE

// checksums

MR4TH_SYMBOL_STATIC U32 pe_coff_section_aux_checksum_from_section_data(String8 data);

// properties

MR4TH_SYMBOL_STATIC U32 pe_align_value_from_section_flags(PE_SectionFlags flags);
MR4TH_SYMBOL_STATIC U32 pe_unwind_num_code_slot_from_opcode_opinfo(PE_UnwindOpCode opcode, U8 opinfo);

// checks

MR4TH_SYMBOL_STATIC B32 pe_machine_type_is_valid(PE_MachineType machine_type);

// strings

MR4TH_SYMBOL_STATIC String8 pe_str8_from_machine_type(PE_MachineType machine_type);
MR4TH_SYMBOL_STATIC void    pe_str8list_from_flags(Arena *arena, String8List *out, PE_Flags flags);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_flags(Arena *arena, PE_Flags flags);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_optional_header_magic(PE_OptionalHeaderMagic magic);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_windows_subsystem(PE_WindowsSubsystem subsystem);
MR4TH_SYMBOL_STATIC void    pe_str8list_from_dll_flags(Arena *arena, String8List *out, PE_DLLFlags dll_flags);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_dll_flags(Arena *arena, PE_DLLFlags dll_flags);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_data_directory_idx(PE_DataDirectoryIdx idx);
MR4TH_SYMBOL_STATIC String8 pe_str8_display_string_from_data_directory_idx(PE_DataDirectoryIdx idx);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_sec_alignment(U32 alignment_code);
MR4TH_SYMBOL_STATIC void    pe_str8list_from_sec_flags(Arena *arena, String8List *out, PE_SectionFlags sec_flags);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_sec_flags(Arena *arena, PE_SectionFlags sec_flags);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_coff_relocation_type(PE_CoffRelocationType type);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_coff_symbol_type(Arena *arena, PE_CoffSymbolType type);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_coff_symbol_storage_class(PE_CoffSymbolStorageClass storage_class);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_base_relocation_type(PE_BaseRelocationType type);
MR4TH_SYMBOL_STATIC String8 pe_str8_from_debug_type(PE_DebugType debug_type);

#endif //MR4TH_PE_H
