// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)

#ifndef COFF_H
#define COFF_H

////////////////////////////////
//~ rjf: COFF Format Types

read_only global U8 g_coff_big_header_magic[] =
{
  0xc7, 0xa1, 0xba, 0xd1, 0xee, 0xba, 0xa9, 0x4b, 0xaf, 0x20, 0xfa, 0xf6, 0x6a, 0xa4, 0xdc, 0xb8
};
read_only global U8 g_coff_res_magic[] =
{
  0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
read_only global U8 g_coff_archive_sig[8]      = "!<arch>\n";
read_only global U8 g_coff_thin_archive_sig[8] = "!<thin>\n";

#pragma pack(push, 1)

#define COFF_TimeStamp_Max max_U32
typedef U32 COFF_TimeStamp;

typedef U16 COFF_FileHeaderFlags;
enum
{
  COFF_FileHeaderFlag_RelocStripped        = (1 << 0),
  COFF_FileHeaderFlag_ExecutableImage      = (1 << 1),
  COFF_FileHeaderFlag_LineNumbersStripped  = (1 << 2),
  COFF_FileHeaderFlag_SymbolsStripped      = (1 << 3),
  COFF_FileHeaderFlag_Reserved0            = (1 << 4),
  COFF_FileHeaderFlag_LargeAddressAware    = (1 << 5),
  COFF_FileHeaderFlag_Reserved1            = (1 << 6),
  COFF_FileHeaderFlag_Reserved2            = (1 << 7),
  COFF_FileHeaderFlag_32BitMachine         = (1 << 8),
  COFF_FileHeaderFlag_DebugStripped        = (1 << 9),
  COFF_FileHeaderFlag_RemovableRunFromSwap = (1 << 10),
  COFF_FileHeaderFlag_NetRunFromSwap       = (1 << 11),
  COFF_FileHeaderFlag_System               = (1 << 12),
  COFF_FileHeaderFlag_Dll                  = (1 << 13),
  COFF_FileHeaderFlag_UpSystemOnly         = (1 << 14),
  COFF_FileHeaderFlag_BytesReservedHi      = (1 << 15)
};

typedef U16 COFF_MachineType;
enum
{
  COFF_MachineType_Unknown    = 0x0,
  COFF_MachineType_X86        = 0x14c,
  COFF_MachineType_X64        = 0x8664,
  COFF_MachineType_Am33       = 0x1d3,
  COFF_MachineType_Arm        = 0x1c0,
  COFF_MachineType_Arm64      = 0xaa64,
  COFF_MachineType_ArmNt      = 0x1c4,
  COFF_MachineType_Ebc        = 0xebc,
  COFF_MachineType_Ia64       = 0x200,
  COFF_MachineType_M32R       = 0x9041,
  COFF_MachineType_Mips16     = 0x266,
  COFF_MachineType_MipsFpu    = 0x366,
  COFF_MachineType_MipsFpu16  = 0x466,
  COFF_MachineType_PowerPc    = 0x1f0,
  COFF_MachineType_PowerPcFp  = 0x1f1,
  COFF_MachineType_R4000      = 0x166,
  COFF_MachineType_RiscV32    = 0x5032,
  COFF_MachineType_RiscV64    = 0x5064,
  COFF_MachineType_RiscV128   = 0x5128,
  COFF_MachineType_Sh3        = 0x1a2,
  COFF_MachineType_Sh3Dsp     = 0x1a3,
  COFF_MachineType_Sh4        = 0x1a6,
  COFF_MachineType_Sh5        = 0x1a8,
  COFF_MachineType_Thumb      = 0x1c2,
  COFF_MachineType_WceMipsV2  = 0x169
};

typedef struct COFF_FileHeader
{
  COFF_MachineType     machine;
  U16                  section_count;
  COFF_TimeStamp       time_stamp;
  U32                  symbol_table_foff;
  U32                  symbol_count;
  U16                  optional_header_size;
  COFF_FileHeaderFlags flags;
} COFF_FileHeader;

typedef struct COFF_BigObjHeader
{
  U16              sig1;              // COFF_MachineType_Unknown
  U16              sig2;              // max_U16
  U16              version;           // 2
  COFF_MachineType machine;
  COFF_TimeStamp   time_stamp;
  U8               magic[16];         // g_coff_big_header_magic
  U8               unused[16];
  U32              section_count;
  U32              symbol_table_foff;
  U32              symbol_count;
} COFF_BigObjHeader;

typedef U32 COFF_SectionAlign;
enum
{
  COFF_SectionAlign_None      = 0x0,
  COFF_SectionAlign_1Bytes    = 0x1,
  COFF_SectionAlign_2Bytes    = 0x2,
  COFF_SectionAlign_4Bytes    = 0x3,
  COFF_SectionAlign_8Bytes    = 0x4,
  COFF_SectionAlign_16Bytes   = 0x5,
  COFF_SectionAlign_32Bytes   = 0x6,
  COFF_SectionAlign_64Bytes   = 0x7,
  COFF_SectionAlign_128Bytes  = 0x8,
  COFF_SectionAlign_256Bytes  = 0x9,
  COFF_SectionAlign_512Bytes  = 0xa,
  COFF_SectionAlign_1024Bytes = 0xb,
  COFF_SectionAlign_2048Bytes = 0xc,
  COFF_SectionAlign_4096Bytes = 0xd,
  COFF_SectionAlign_8192Bytes = 0xe
};

typedef U32 COFF_SectionFlags;
enum
{
  COFF_SectionFlag_TypeNoPad            = (1 << 3),
  COFF_SectionFlag_CntCode              = (1 << 5),
  COFF_SectionFlag_CntInitializedData   = (1 << 6),
  COFF_SectionFlag_CntUninitializedData = (1 << 7),
  COFF_SectionFlag_LnkOther             = (1 << 8),
  COFF_SectionFlag_LnkInfo              = (1 << 9),
  COFF_SectionFlag_LnkRemove            = (1 << 11),
  COFF_SectionFlag_LnkCOMDAT            = (1 << 12),
  COFF_SectionFlag_GpRel                = (1 << 15),
  COFF_SectionFlag_Mem16Bit             = (1 << 17),
  COFF_SectionFlag_MemLocked            = (1 << 18),
  COFF_SectionFlag_MemPreload           = (1 << 19),
  COFF_SectionFlag_AlignShift           = 20,
  COFF_SectionFlag_AlignMask            = 0xf,
  COFF_SectionFlag_Align1Bytes          = (COFF_SectionAlign_1Bytes    << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align2Bytes          = (COFF_SectionAlign_2Bytes    << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align4Bytes          = (COFF_SectionAlign_4Bytes    << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align8Bytes          = (COFF_SectionAlign_8Bytes    << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align16Bytes         = (COFF_SectionAlign_16Bytes   << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align32Bytes         = (COFF_SectionAlign_32Bytes   << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align64Bytes         = (COFF_SectionAlign_64Bytes   << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align128Bytes        = (COFF_SectionAlign_128Bytes  << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align256Bytes        = (COFF_SectionAlign_256Bytes  << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align512Bytes        = (COFF_SectionAlign_512Bytes  << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align1024Bytes       = (COFF_SectionAlign_1024Bytes << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align2048Bytes       = (COFF_SectionAlign_2048Bytes << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align4096Bytes       = (COFF_SectionAlign_4096Bytes << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_Align8192Bytes       = (COFF_SectionAlign_8192Bytes << COFF_SectionFlag_AlignShift),
  COFF_SectionFlag_LnkNRelocOvfl        = (1 << 24),
  COFF_SectionFlag_MemDiscardable       = (1 << 25),
  COFF_SectionFlag_MemNotCached         = (1 << 26),
  COFF_SectionFlag_MemNotPaged          = (1 << 27),
  COFF_SectionFlag_MemShared            = (1 << 28),
  COFF_SectionFlag_MemExecute           = (1 << 29),
  COFF_SectionFlag_MemRead              = (1 << 30),
  COFF_SectionFlag_MemWrite             = (1 << 31)
};
#define COFF_SectionFlags_ExtractAlign(f) (COFF_SectionAlign)(((f) >> COFF_SectionFlag_AlignShift) & COFF_SectionFlag_AlignMask)
#define COFF_SectionFlags_LnkFlags        ((COFF_SectionFlag_AlignMask << COFF_SectionFlag_AlignShift) | COFF_SectionFlag_LnkCOMDAT | COFF_SectionFlag_LnkInfo | COFF_SectionFlag_LnkOther | COFF_SectionFlag_LnkRemove | COFF_SectionFlag_LnkNRelocOvfl)

typedef struct COFF_SectionHeader
{
  U8                name[8];
  U32               vsize;
  U32               voff;
  U32               fsize;
  U32               foff;
  U32               relocs_foff;
  U32               lines_foff;
  U16               reloc_count;
  U16               line_count;
  COFF_SectionFlags flags;
} COFF_SectionHeader;

////////////////////////////////

typedef U8 COFF_SymType;
enum
{
  COFF_SymType_Null,
  COFF_SymType_Void,
  COFF_SymType_Char,
  COFF_SymType_Short,
  COFF_SymType_Int,
  COFF_SymType_Long,
  COFF_SymType_Float,
  COFF_SymType_Double,
  COFF_SymType_Struct,
  COFF_SymType_Union,
  COFF_SymType_Enum,
  COFF_SymType_MemberOfEnumeration,
  COFF_SymType_Byte,
  COFF_SymType_Word,
  COFF_SymType_UInt,
  COFF_SymType_DWord
};

typedef U8 COFF_SymStorageClass;
enum
{
  COFF_SymStorageClass_Null            = 0x00,
  COFF_SymStorageClass_Automatic       = 0x01,
  COFF_SymStorageClass_External        = 0x02,
  COFF_SymStorageClass_Static          = 0x03,
  COFF_SymStorageClass_Register        = 0x04,
  COFF_SymStorageClass_ExternalDef     = 0x05,
  COFF_SymStorageClass_Label           = 0x06,
  COFF_SymStorageClass_UndefinedLabel  = 0x07,
  COFF_SymStorageClass_MemberOfStruct  = 0x08,
  COFF_SymStorageClass_Argument        = 0x09,
  COFF_SymStorageClass_StructTag       = 0x0a,
  COFF_SymStorageClass_MemberOfUnion   = 0x0b,
  COFF_SymStorageClass_UnionTag        = 0x0c,
  COFF_SymStorageClass_TypeDefinition  = 0x0d,
  COFF_SymStorageClass_UndefinedStatic = 0x0e,
  COFF_SymStorageClass_EnumTag         = 0x0f,
  COFF_SymStorageClass_MemberOfEnum    = 0x10,
  COFF_SymStorageClass_RegisterParam   = 0x11,
  COFF_SymStorageClass_BitField        = 0x12,
  COFF_SymStorageClass_Block           = 0x64,
  COFF_SymStorageClass_Function        = 0x65,
  COFF_SymStorageClass_EndOfStruct     = 0x66,
  COFF_SymStorageClass_File            = 0x67,
  COFF_SymStorageClass_Section         = 0x68,
  COFF_SymStorageClass_WeakExternal    = 0x69,
  COFF_SymStorageClass_CLRToken        = 0x6b,
  COFF_SymStorageClass_EndOfFunction   = 0xff
};

typedef U8 COFF_SymDType;
enum
{
  COFF_SymDType_Null  = 0x00,
  COFF_SymDType_Ptr   = 0x10,
  COFF_SymDType_Func  = 0x20,
  COFF_SymDType_Array = 0x30
};

// Special values for section number field in coff symbol
#define COFF_Symbol_UndefinedSection 0
#define COFF_Symbol_AbsSection32     ((U32)-1)
#define COFF_Symbol_DebugSection32   ((U32)-2)
#define COFF_Symbol_AbsSection16     ((U16)-1)
#define COFF_Symbol_DebugSection16   ((U16)-2)

typedef union COFF_SymbolName
{
  U8 short_name[8];
  struct {
    // if this field is filled with zeroes we have a long name,
    // which means name is stored in the string table
    // and we need to use the offset to look it up...
    U32 zeroes;
    U32 string_table_offset;
  } long_name;
} COFF_SymbolName;

#define COFF_SymbolType_IsFunc(x) ((x).u.lsb == COFF_SymType_Null && (x).u.msb == COFF_SymDType_Func)

typedef union COFF_SymbolType
{
  struct {
    COFF_SymDType msb;
    COFF_SymType lsb;
  } u;
  U16 v;
} COFF_SymbolType;

typedef struct COFF_Symbol16
{
  COFF_SymbolName      name;
  U32                  value;
  U16                  section_number;
  COFF_SymbolType      type;
  COFF_SymStorageClass storage_class;
  U8                   aux_symbol_count;
} COFF_Symbol16;

typedef struct COFF_Symbol32
{
  COFF_SymbolName      name;
  U32                  value;
  U32                  section_number;
  COFF_SymbolType      type;
  COFF_SymStorageClass storage_class;
  U8                   aux_symbol_count;
} COFF_Symbol32;

typedef U32 COFF_WeakExtType;
enum
{
  COFF_WeakExt_NoLibrary     = 1,
  COFF_WeakExt_SearchLibrary = 2,
  COFF_WeakExt_SearchAlias   = 3
};

// storage class: External
typedef struct COFF_SymbolFuncDef
{
  U32 tag_index;
  U32 total_size;
  U32 ptr_to_ln;
  U32 ptr_to_next_func;
  U8  unused[2];
} COFF_SymbolFuncDef;

// storage class: Function
typedef struct COFF_SymbolFunc
{
  U8  unused[4];
  U16 ln;
  U8  unused2[2];
  U32 ptr_to_next_func;
  U8  unused3[2];
} COFF_SymbolFunc;

// storage class: WeakExternal
typedef struct COFF_SymbolWeakExt
{
  U32              tag_index;
  COFF_WeakExtType characteristics;
  U8               unused[10];
} COFF_SymbolWeakExt;

typedef struct COFF_SymbolFile 
{
  U8 name[18];
} COFF_SymbolFile;

typedef U8 COFF_ComdatSelectType;
enum
{
  COFF_ComdatSelect_Null         = 0, 
  COFF_ComdatSelect_NoDuplicates = 1, // Only one symbol is allowed to be in global symbol table, otherwise multiply defintion error is thrown.
  COFF_ComdatSelect_Any          = 2, // Select any symbol, even if there are multiple definitions. (we default to first declaration)
  COFF_ComdatSelect_SameSize     = 3, // Sections that symbols reference must match in size, otherwise multiply definition error is thrown.
  COFF_ComdatSelect_ExactMatch   = 4, // Sections that symbols reference must have identical checksums, otherwise multiply defintion error is thrown.
  COFF_ComdatSelect_Associative  = 5, // Symbols with associative type form a chain of sections are related to each other. (next link is indicated in COFF_SecDef in 'number')
  COFF_ComdatSelect_Largest      = 6  // Linker selects section with largest size.
};

// provides information about section to which symbol refers to.
// storage class: Static
typedef struct COFF_SymbolSecDef
{
  U32                   length;
  U16                   number_of_relocations;
  U16                   number_of_ln;
  U32                   check_sum;
  U16                   number_lo;  // low 16 bits of one-based section index
  COFF_ComdatSelectType selection;
  U8                    unused;
  U16                   number_hi;
} COFF_SymbolSecDef;

////////////////////////////////

typedef U16 COFF_RelocType;

typedef COFF_RelocType COFF_Reloc_X64;
enum
{
  COFF_Reloc_X64_Abs        = 0x0,
  COFF_Reloc_X64_Addr64     = 0x1,
  COFF_Reloc_X64_Addr32     = 0x2,
  COFF_Reloc_X64_Addr32Nb   = 0x3,  // NB => No Base
  COFF_Reloc_X64_Rel32      = 0x4,
  COFF_Reloc_X64_Rel32_1    = 0x5,
  COFF_Reloc_X64_Rel32_2    = 0x6,
  COFF_Reloc_X64_Rel32_3    = 0x7,
  COFF_Reloc_X64_Rel32_4    = 0x8,
  COFF_Reloc_X64_Rel32_5    = 0x9,
  COFF_Reloc_X64_Section    = 0xA,
  COFF_Reloc_X64_SecRel     = 0xB,
  COFF_Reloc_X64_SecRel7    = 0xC,  // TODO(nick): MSDN doesn't specify size for CLR token
  COFF_Reloc_X64_Token      = 0xD,
  COFF_Reloc_X64_SRel32     = 0xE,  // TODO(nick): MSDN doesn't specify size for PAIR
  COFF_Reloc_X64_Pair       = 0xF,
  COFF_Reloc_X64_SSpan32    = 0x10,
  COFF_Reloc_X64_Unknown_11 = 0x11,
  COFF_Reloc_X64_Last       = COFF_Reloc_X64_Unknown_11,
};

typedef COFF_RelocType COFF_Reloc_X86;
enum
{
  COFF_Reloc_X86_Abs      = 0x0,  //  relocation is ignored
  COFF_Reloc_X86_Dir16    = 0x1,  //  no support
  COFF_Reloc_X86_Rel16    = 0x2,  //  no support
  COFF_Reloc_X86_Unknown0 = 0x3,
  COFF_Reloc_X86_Unknown2 = 0x4,
  COFF_Reloc_X86_Unknown3 = 0x5,
  COFF_Reloc_X86_Dir32    = 0x6,  //  32-bit virtual address
  COFF_Reloc_X86_Dir32Nb  = 0x7,  //  32-bit virtual offset
  COFF_Reloc_X86_Seg12    = 0x9,  //  no support
  COFF_Reloc_X86_Section  = 0xa,  //  16-bit section index, used for debug info purposes
  COFF_Reloc_X86_SecRel   = 0xb,  //  32-bit offset from start of a section
  COFF_Reloc_X86_Token    = 0xc,  //  CLR token? (for managed languages)
  COFF_Reloc_X86_SecRel7  = 0xd,  //  7-bit offset from the base of the section that contains the target.
  COFF_Reloc_X86_Unknown4 = 0xe,
  COFF_Reloc_X86_Unknown5 = 0xf,
  COFF_Reloc_X86_Unknown6 = 0x10,
  COFF_Reloc_X86_Unknown7 = 0x11,
  COFF_Reloc_X86_Unknown8 = 0x12,
  COFF_Reloc_X86_Unknown9 = 0x13,
  COFF_Reloc_X86_Rel32    = 0x14
};

typedef COFF_RelocType COFF_Reloc_Arm;
enum
{
  COFF_Reloc_Arm_Abs           = 0x0,
  COFF_Reloc_Arm_Addr32        = 0x1,
  COFF_Reloc_Arm_Addr32Nb      = 0x2,
  COFF_Reloc_Arm_Branch24      = 0x3,
  COFF_Reloc_Arm_Branch11      = 0x4,
  COFF_Reloc_Arm_Unknown1      = 0x5,
  COFF_Reloc_Arm_Unknown2      = 0x6,
  COFF_Reloc_Arm_Unknown3      = 0x7,
  COFF_Reloc_Arm_Unknown4      = 0x8,
  COFF_Reloc_Arm_Unknown5      = 0x9,
  COFF_Reloc_Arm_Rel32         = 0xa,
  COFF_Reloc_Arm_Section       = 0xe,
  COFF_Reloc_Arm_SecRel        = 0xf,
  COFF_Reloc_Arm_Mov32         = 0x10,
  COFF_Reloc_Arm_ThumbMov32    = 0x11,
  COFF_Reloc_Arm_ThumbBranch20 = 0x12,
  COFF_Reloc_Arm_Unused        = 0x13,
  COFF_Reloc_Arm_ThumbBranch24 = 0x14,
  COFF_Reloc_Arm_ThumbBlx23    = 0x15,
  COFF_Reloc_Arm_Pair          = 0x16
};

typedef COFF_RelocType COFF_Reloc_Arm64;
enum
{
  COFF_Reloc_Arm64_Abs           = 0x0,
  COFF_Reloc_Arm64_Addr32        = 0x1,
  COFF_Reloc_Arm64_Addr32Nb      = 0x2,
  COFF_Reloc_Arm64_Branch26      = 0x3,
  COFF_Reloc_Arm64_PageBaseRel21 = 0x4,
  COFF_Reloc_Arm64_Rel21         = 0x5,
  COFF_Reloc_Arm64_PageOffset12a = 0x6,
  COFF_Reloc_Arm64_SecRel        = 0x8,
  COFF_Reloc_Arm64_SecRelLow12a  = 0x9,
  COFF_Reloc_Arm64_SecRelHigh12a = 0xa,
  COFF_Reloc_Arm64_SecRelLow12l  = 0xb,
  COFF_Reloc_Arm64_Token         = 0xc,
  COFF_Reloc_Arm64_Section       = 0xd,
  COFF_Reloc_Arm64_Addr64        = 0xe,
  COFF_Reloc_Arm64_Branch19      = 0xf,
  COFF_Reloc_Arm64_Branch14      = 0x10,
  COFF_Reloc_Arm64_Rel32         = 0x11
};

typedef struct COFF_Reloc
{
  U32            apply_off; // section relative offset where relocation is placed
  U32            isymbol;   // zero based index into coff symbol table
  COFF_RelocType type;      // relocation type that depends on the arch
} COFF_Reloc;

////////////////////////////////

#define COFF_ResourceAlign 4u

typedef struct COFF_ResourceHeaderPrefix
{
  U32 data_size;
  U32 header_size;
} COFF_ResourceHeaderPrefix;

typedef U16 COFF_ResourceMemoryFlags;
enum
{
  COFF_ResourceMemoryFlag_Moveable    = 0x10,
  COFF_ResourceMemoryFlag_Pure        = 0x20,
  COFF_ResourceMemoryFlag_PreLoad     = 0x40,
  COFF_ResourceMemoryFlag_Discardable = 0x1000
};

typedef struct COFF_ResourceDataEntry
{
  U32 data_voff;
  U32 data_size;
  U32 code_page;
  U32 reserved;
} COFF_ResourceDataEntry;

typedef struct COFF_ResourceDirTable
{
  U32            characteristics;
  COFF_TimeStamp time_stamp;
  U16            major_version;
  U16            minor_version;
  U16            name_entry_count;
  U16            id_entry_count;
} COFF_ResourceDirTable;

#define COFF_Resource_SubDirFlag (1u << 31u)
typedef struct COFF_ResourceDirEntry
{
  union {
    U32 offset;
    U32 id;
  } name;
  union {
    U32 data_entry_offset;
    U32 sub_dir_offset;
  } id;
} COFF_ResourceDirEntry;

////////////////////////////////

#define COFF_Archive_MemberAlign      2
#define COFF_Archive_MaxShortNameSize 15

typedef struct COFF_ArchiveMemberHeader
{
  U8 name[16];
  U8 date[12];
  U8 user_id[6];
  U8 group_id[6];
  U8 mode[8];
  U8 size[10];
  U8 end[2];
} COFF_ArchiveMemberHeader;

#define COFF_ImportType_Invalid max_U16
typedef U16 COFF_ImportType;
enum
{
  COFF_ImportHeader_Code  = 0,
  COFF_ImportHeader_Data  = 1,
  COFF_ImportHeader_Const = 2
};

typedef U32 COFF_ImportByType;
enum
{
  COFF_ImportBy_Ordinal      = 0,
  COFF_ImportBy_Name         = 1,
  COFF_ImportBy_NameNoPrefix = 2,
  COFF_ImportBy_Undecorate   = 3
};

typedef U16 COFF_ImportHeaderFlags;
enum
{
  COFF_ImportHeader_TypeShift = 0,
  COFF_ImportHeader_TypeMask  = 3,
  COFF_ImportHeader_ImportByShift = 2,
  COFF_ImportHeader_ImportByMask  = 3,
};
#define COFF_ImportHeader_ExtractType(x)     (((x) >> COFF_ImportHeader_TypeShift    ) & COFF_ImportHeader_TypeMask    )
#define COFF_ImportHeader_ExtractImportBy(x) (((x) >> COFF_ImportHeader_ImportByShift) & COFF_ImportHeader_ImportByMask)

typedef struct COFF_ImportHeader
{
  U16                    sig1;     // COFF_MachineType_Unknown
  U16                    sig2;     // max_U16
  U16                    version;  // 0
  COFF_MachineType       machine;
  COFF_TimeStamp         time_stamp;
  U32                    data_size;
  U16                    hint_or_ordinal;
  COFF_ImportHeaderFlags flags;
  // char *func_name;
  // char *dll_name;
} COFF_ImportHeader;

#pragma pack(pop)

typedef struct COFF_RelocValue
{
  U64 size;
  S64 value;
} COFF_RelocValue;

////////////////////////////////
// Section

internal U64               coff_align_size_from_section_flags(COFF_SectionFlags flags);
internal COFF_SectionFlags coff_section_flag_from_align_size (U64 align);

internal String8 coff_name_from_section_header(String8 string_table, COFF_SectionHeader *header);
internal void    coff_parse_section_name      (String8 full_name, String8 *name_out, String8 *postfix_out);

////////////////////////////////
// Symbol

internal String8 coff_read_symbol_name(String8 string_table, COFF_SymbolName *name);

////////////////////////////////
// Reloc

internal U64 coff_apply_size_from_reloc_x64(COFF_Reloc_X64 x);
internal U64 coff_apply_size_from_reloc_x86(COFF_Reloc_X86 x);

internal COFF_RelocValue coff_pick_reloc_value_x64(COFF_Reloc_X64 type, U64 image_base, U64 reloc_virtual_offset, U32 symbol_section_number, U32 symbol_section_offset, S64 symbol_virtual_offset);

////////////////////////////////
// Import

internal U32 coff_make_ordinal32(U16 hint);
internal U64 coff_make_ordinal64(U16 hint);
internal String8 coff_ordinal_data_from_hint(Arena *arena, COFF_MachineType machine, U16 hint);

internal String8 coff_make_import_lookup(Arena *arena, U16 hint, String8 name);
internal String8 coff_make_import_header(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportByType import_by, String8 name, U16 hint_or_ordinal, COFF_ImportType type);

////////////////////////////////
// Misc

internal U64 coff_word_size_from_machine       (COFF_MachineType machine);
internal U64 coff_default_exe_base_from_machine(COFF_MachineType machine);
internal U64 coff_default_dll_base_from_machine(COFF_MachineType machine);

internal Arch arch_from_coff_machine(COFF_MachineType machine);
internal U64  coff_foff_from_voff(COFF_SectionHeader *sections, U64 section_count, U64 voff);

////////////////////////////////
//~ rjf: Enum <=> String

internal String8 coff_string_from_time_stamp(Arena *arena, COFF_TimeStamp time_stamp);
internal String8 coff_string_from_comdat_select_type(COFF_ComdatSelectType type);
internal String8 coff_string_from_machine_type(COFF_MachineType machine);
internal String8 coff_string_from_flags(Arena *arena, COFF_FileHeaderFlags flags);
internal String8 coff_string_from_section_flags(Arena *arena, COFF_SectionFlags flags);
internal String8 coff_string_from_resource_memory_flags(Arena *arena, COFF_ResourceMemoryFlags flags);
internal String8 coff_string_from_import_header_type(COFF_ImportType type);
internal String8 coff_string_from_sym_dtype(COFF_SymDType x);
internal String8 coff_string_from_sym_type(COFF_SymType x);
internal String8 coff_string_from_sym_storage_class(COFF_SymStorageClass x);
internal String8 coff_string_from_weak_ext_type(COFF_WeakExtType x);
internal String8 coff_string_from_reloc_x86(COFF_Reloc_X86 x);
internal String8 coff_string_from_reloc_x64(COFF_Reloc_X64 x);
internal String8 coff_string_from_reloc_arm(COFF_Reloc_Arm x);
internal String8 coff_string_from_reloc_arm64(COFF_Reloc_Arm64 x);
internal String8 coff_string_from_reloc(COFF_MachineType machine, COFF_RelocType x);

internal COFF_MachineType coff_machine_from_string(String8 string);
internal COFF_ImportType  coff_import_header_type_from_string(String8 name);
internal String8 coff_make_lib_member_header(Arena *arena, String8 name, COFF_TimeStamp time_stamp, U16 user_id, U16 group_id, U16 mode, U32 size);

#endif // COFF_H
