//! The maximum number of sections that a COFF object can have (inclusive).
static const int32_t MaxNumberOfSections16 = 65279;

//! The PE signature bytes that follows the DOS stub header.
static const char PE_Magic[] = { 'P', 'E', '\0', '\0' };

static const char Rich_Magic[] = {'R', 'i', 'c', 'h'};
static const char DanS_Magic[] = {'D', 'a', 'n', 'S'};

static const uint32_t DanS_Magic_number = 0x536E6144;

static const char BigObjMagic[] = {
  '\xc7', '\xa1', '\xba', '\xd1', '\xee', '\xba', '\xa9', '\x4b',
  '\xaf', '\x20', '\xfa', '\xf6', '\x6a', '\xa4', '\xdc', '\xb8',
};

static const uint8_t DEFAULT_NUMBER_DATA_DIRECTORIES = 15;

#pragma pack(push,1)
struct pe_header {
  char     signature[sizeof(PE_Magic)];
  uint16_t Machine;
  uint16_t NumberOfSections;
  uint32_t TimeDateStamp;
  uint32_t PointerToSymbolTable;
  uint32_t NumberOfSymbols;
  uint16_t SizeOfOptionalHeader;
  uint16_t Characteristics;
};


struct pe_relocation {
  uint32_t VirtualAddress;
  uint32_t SymbolTableIndex;
  uint16_t Type;
};

struct pe_base_relocation_block {
  uint32_t PageRVA;
  uint32_t BlockSize;
};


struct pe_symbol {
  union {
    char ShortName[8];
    struct
    {
      uint32_t Zeroes;
      uint32_t Offset;
    } Name;
  } Name;
  uint32_t Value;
  int16_t  SectionNumber;
  uint16_t Type;
  uint8_t  StorageClass;
  uint8_t  NumberOfAuxSymbols;
};


struct pe_section {
  char     Name[8];
  uint32_t VirtualSize;
  uint32_t VirtualAddress;
  uint32_t SizeOfRawData;
  uint32_t PointerToRawData;
  uint32_t PointerToRelocations;
  uint32_t PointerToLineNumbers;
  uint16_t NumberOfRelocations;
  uint16_t NumberOfLineNumbers;
  uint32_t Characteristics;
};

struct AuxiliaryFunctionDefinition {
  uint32_t TagIndex;
  uint32_t TotalSize;
  uint32_t PointerToLinenumber;
  uint32_t PointerToNextFunction;
  char     unused[2];
};

struct AuxiliarybfAndefSymbol {
  uint8_t  unused1[4];
  uint16_t Linenumber;
  uint8_t  unused2[6];
  uint32_t PointerToNextFunction;
  uint8_t  unused3[2];
};

struct AuxiliaryWeakExternal {
  uint32_t TagIndex;
  uint32_t Characteristics;
  uint8_t  unused[10];
};


struct AuxiliarySectionDefinition {
  uint32_t Length;
  uint16_t NumberOfRelocations;
  uint16_t NumberOfLinenumbers;
  uint32_t CheckSum;
  uint32_t Number;
  uint8_t  Selection;
  char     unused;
};

struct AuxiliaryCLRToken {
  uint8_t  AuxType;
  uint8_t  unused1;
  uint32_t SymbolTableIndex;
  char     unused2[12];
};

union Auxiliary {
  AuxiliaryFunctionDefinition FunctionDefinition;
  AuxiliarybfAndefSymbol      bfAndefSymbol;
  AuxiliaryWeakExternal       WeakExternal;
  AuxiliarySectionDefinition  SectionDefinition;
};


/// The Import Directory Table.
///
/// There is a single array of these and one entry per imported DLL.
struct pe_import {
  uint32_t ImportLookupTableRVA;
  uint32_t TimeDateStamp;
  uint32_t ForwarderChain;
  uint32_t NameRVA;
  uint32_t ImportAddressTableRVA;
};


struct ImportLookupTableEntry32 {
  uint32_t data;
};

struct ImportLookupTableEntry64 {
  uint64_t data;
};


struct pe32_tls {
  uint32_t RawDataStartVA;
  uint32_t RawDataEndVA;
  uint32_t AddressOfIndex;
  uint32_t AddressOfCallback;
  uint32_t SizeOfZeroFill;
  uint32_t Characteristics;
};


struct pe64_tls {
  uint64_t RawDataStartVA;
  uint64_t RawDataEndVA;
  uint64_t AddressOfIndex;
  uint64_t AddressOfCallback;
  uint32_t SizeOfZeroFill;
  uint32_t Characteristics;
};


/// The DOS compatible header at the front of all PEs.
struct pe_dos_header {
  uint16_t Magic;
  uint16_t UsedBytesInTheLastPage;
  uint16_t FileSizeInPages;
  uint16_t NumberOfRelocationItems;
  uint16_t HeaderSizeInParagraphs;
  uint16_t MinimumExtraParagraphs;
  uint16_t MaximumExtraParagraphs;
  uint16_t InitialRelativeSS;
  uint16_t InitialSP;
  uint16_t Checksum;
  uint16_t InitialIP;
  uint16_t InitialRelativeCS;
  uint16_t AddressOfRelocationTable;
  uint16_t OverlayNumber;
  uint16_t Reserved[4];
  uint16_t OEMid;
  uint16_t OEMinfo;
  uint16_t Reserved2[10];
  uint32_t AddressOfNewExeHeader;
};

struct pe64_optional_header {
  uint16_t Magic;
  uint8_t  MajorLinkerVersion;
  uint8_t  MinorLinkerVersion;
  uint32_t SizeOfCode;
  uint32_t SizeOfInitializedData;
  uint32_t SizeOfUninitializedData;
  uint32_t AddressOfEntryPoint; // RVA
  uint32_t BaseOfCode; // RVA
  //uint32_t BaseOfData; // RVA
  uint64_t ImageBase;
  uint32_t SectionAlignment;
  uint32_t FileAlignment;
  uint16_t MajorOperatingSystemVersion;
  uint16_t MinorOperatingSystemVersion;
  uint16_t MajorImageVersion;
  uint16_t MinorImageVersion;
  uint16_t MajorSubsystemVersion;
  uint16_t MinorSubsystemVersion;
  uint32_t Win32VersionValue;
  uint32_t SizeOfImage;
  uint32_t SizeOfHeaders;
  uint32_t CheckSum;
  uint16_t Subsystem;
  uint16_t DLLCharacteristics;
  uint64_t SizeOfStackReserve;
  uint64_t SizeOfStackCommit;
  uint64_t SizeOfHeapReserve;
  uint64_t SizeOfHeapCommit;
  uint32_t LoaderFlags;
  uint32_t NumberOfRvaAndSize;
};


struct pe32_optional_header {
  uint16_t Magic;
  uint8_t  MajorLinkerVersion;
  uint8_t  MinorLinkerVersion;
  uint32_t SizeOfCode;
  uint32_t SizeOfInitializedData;
  uint32_t SizeOfUninitializedData;
  uint32_t AddressOfEntryPoint; // RVA
  uint32_t BaseOfCode; // RVA
  uint32_t BaseOfData; // RVA
  uint32_t ImageBase;
  uint32_t SectionAlignment;
  uint32_t FileAlignment;
  uint16_t MajorOperatingSystemVersion;
  uint16_t MinorOperatingSystemVersion;
  uint16_t MajorImageVersion;
  uint16_t MinorImageVersion;
  uint16_t MajorSubsystemVersion;
  uint16_t MinorSubsystemVersion;
  uint32_t Win32VersionValue;
  uint32_t SizeOfImage;
  uint32_t SizeOfHeaders;
  uint32_t CheckSum;
  uint16_t Subsystem;
  uint16_t DLLCharacteristics;
  uint32_t SizeOfStackReserve;
  uint32_t SizeOfStackCommit;
  uint32_t SizeOfHeapReserve;
  uint32_t SizeOfHeapCommit;
  uint32_t LoaderFlags;
  uint32_t NumberOfRvaAndSize;
};


struct pe_data_directory {
  uint32_t RelativeVirtualAddress;
  uint32_t Size;
};


struct pe_debug {
  uint32_t Characteristics;
  uint32_t TimeDateStamp;
  uint16_t MajorVersion;
  uint16_t MinorVersion;
  uint32_t Type;
  uint32_t SizeOfData;
  uint32_t AddressOfRawData;
  uint32_t PointerToRawData;
};


struct pe_pdb_70 {
  uint32_t cv_signature;
  uint8_t  signature[16];
  uint32_t age;
  char*    filename;
};

struct pe_pdb_20 {
  uint32_t cv_signature;
  uint32_t offset;
  uint32_t signature;
  uint32_t age;
  char*    filename;
};

struct pe_pogo {
  uint32_t start_rva;
  uint32_t size;
  char     name[1];
};


struct pe_resource_directory_table {
  uint32_t Characteristics;
  uint32_t TimeDateStamp;
  uint16_t MajorVersion;
  uint16_t MinorVersion;
  uint16_t NumberOfNameEntries;
  uint16_t NumberOfIDEntries;
};

struct pe_resource_directory_entries {
  union {
    uint32_t NameRVA;
    uint32_t IntegerID;
  } NameID;
  uint32_t RVA;
};

struct pe_resource_data_entry {
  uint32_t DataRVA;
  uint32_t Size;
  uint32_t Codepage;
  uint32_t Reserved;
};

struct pe_resource_string {
  int16_t Length;
  uint16_t Name[1];
};

struct pe_resource_acceltableentry {
  int16_t fFlags;
  int16_t wAnsi;
  int16_t wId;
  int16_t padding;
};

//
// Export structures
//
struct pe_export_directory_table {
  uint32_t ExportFlags;           ///< Reserverd must be 0
  uint32_t Timestamp;             ///< The time and date that the export data was created
  uint16_t MajorVersion;          ///< The Major version number
  uint16_t MinorVersion;          ///< The Minor version number
  uint32_t NameRVA;               ///< The address of the ASCII DLL's name (RVA)
  uint32_t OrdinalBase;           ///< The starting ordinal number for exports. (Usually 1)
  uint32_t AddressTableEntries;   ///< Number of entries in the export address table
  uint32_t NumberOfNamePointers;  ///< Number of entries in the name pointer table
  uint32_t ExportAddressTableRVA; ///< Address of the export address table (RVA)
  uint32_t NamePointerRVA;        ///< Address of the name pointer table (RVA)
  uint32_t OrdinalTableRVA;       ///< Address of the ordinal table (RVA)
};


struct pe_resource_fixed_file_info {
  uint32_t signature;          // e.g.  0xfeef04bd
  uint32_t struct_version;      // e.g.  0x00000042 = "0.42"
  uint32_t file_version_MS;    // e.g.  0x00030075 = "3.75"
  uint32_t file_version_LS;    // e.g.  0x00000031 = "0.31"
  uint32_t product_version_MS; // e.g.  0x00030010 = "3.10"
  uint32_t product_version_LS; // e.g.  0x00000031 = "0.31"
  uint32_t file_flags_mask;    // = 0x3F for version "0.42"
  uint32_t file_flags;         // e.g.  VFF_DEBUG | VFF_PRERELEASE
  uint32_t file_OS;            // e.g.  VOS_DOS_WINDOWS16
  uint32_t file_type;          // e.g.  VFT_DRIVER
  uint32_t file_subtype;       // e.g.  VFT2_DRV_KEYBOARD
  uint32_t file_date_MS;       // e.g.  0
  uint32_t file_date_LS;       // e.g.  0
};


struct pe_resource_version_info {
  uint16_t length;
  uint16_t sizeof_value;
  uint16_t type;
  char16_t key[16];
  // uint16_t padding;
  //
  // uint16_t padding;
  // uint16_t children
};

//! Resource icons directory structure
//! Based on https://docs.microsoft.com/en-us/windows/win32/menurc/resources-reference
//!
//! This is the begining of the RESOURCE_TYPES::GROUP_ICON content
struct pe_resource_icon_dir {
  uint16_t reserved; ///< Reserved
  uint16_t type;     ///< Resource type (1 for icons)
  uint16_t count;    ///< Number of icons
  // GRPICONDIRENTRY idEntries[];
};


//! Structure that follows pe_resource_icon_dir in a resource entry
struct pe_resource_icon_group {
  uint8_t width;        ///< Width, in pixels, of the image
  uint8_t height;       ///< Height, in pixels, of the image
  uint8_t color_count;  ///< Number of colors in image (0 if >=8bpp)
  uint8_t reserved;     ///< Reserved (must be 0)
  uint16_t planes;      ///< Color Planes
  uint16_t bit_count;   ///< Bits per pixel
  uint32_t size;        ///< Size of the image in bytes
  uint16_t ID;          ///< The associated ID
};

//! Structure that follows pe_resource_icon_dir in a icon **file**
struct pe_icon_header {
  uint8_t width;        ///< Width, in pixels, of the image
  uint8_t height;       ///< Height, in pixels, of the image
  uint8_t color_count;  ///< Number of colors in image (0 if >=8bpp)
  uint8_t reserved;     ///< Reserved (must be 0)
  uint16_t planes;      ///< Color Planes
  uint16_t bit_count;   ///< Bits per pixel
  uint32_t size;        ///< Size of the image in bytes
  uint32_t offset;      ///< Offset to the pixels
};

//! Extended dialog box template
struct pe_dialog_template_ext {
  uint16_t version;
  uint16_t signature;
  uint32_t help_id;
  uint32_t ext_style;
  uint32_t style;
  uint16_t nbof_items;
  int16_t x;
  int16_t y;
  int16_t cx;
  int16_t cy;
  // sz_Or_Ord menu;
  // sz_Or_Ord windowClass;
  // char16_t  title[titleLen];
  // uint16_t  pointsize;
  // uint16_t  weight;
  // uint8_t   italic;
  // uint8_t   charset;
  // char16_t  typeface[stringLen];
};

//! Dialog box template
struct pe_dialog_template {
  uint32_t style;
  uint32_t ext_style;
  uint16_t nbof_items;
  int16_t x;
  int16_t y;
  int16_t cx;
  int16_t cy;
};


//! Extended dialog box template item
struct pe_dialog_item_template_ext {
  uint32_t help_id;
  uint32_t ext_style;
  uint32_t style;
  int16_t x;
  int16_t y;
  int16_t cx;
  int16_t cy;
  uint32_t id;
  // sz_Or_Ord windowClass;
  // sz_Or_Ord title;
  // uint16_t extra_count;
};


//! Dialog box template item
struct pe_dialog_item_template {
  uint32_t style;
  uint32_t ext_style;
  int16_t x;
  int16_t y;
  int16_t cx;
  int16_t cy;
  uint16_t id;
};

struct pe_code_integrity {
  uint16_t Flags;
  uint16_t Catalog;
  uint32_t CatalogOffset;
  uint32_t Reserved;
};

struct pe_exception_entry_x64 {
  uint32_t address_start_rva;
  uint32_t address_end_rva;
  uint32_t unwind_info_rva;
};


struct pe_exception_entry_mips {
  uint32_t address_start_va;
  uint32_t address_end_va;
  uint32_t exception_handler;
  uint32_t handler_data;
  uint32_t prolog_end_address;
};

struct pe_exception_entry_arm {
  uint32_t address_start_va;
  uint32_t data;
};

#pragma pack(pop)
