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

#ifndef COFF_PARSE_H
#define COFF_PARSE_H

typedef struct COFF_FileHeaderInfo
{
  B32              is_big_obj;
  COFF_MachineType machine;
  U64              header_size;
  U64              section_count_no_null;
  U64              symbol_size;
  U64              symbol_count;
  Rng1U64          header_range;
  Rng1U64          section_table_range;
  Rng1U64          symbol_table_range;
  Rng1U64          string_table_range;
} COFF_FileHeaderInfo;

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

typedef struct COFF_SectionHeaderArray
{
  U64                 count;
  COFF_SectionHeader *v;
} COFF_SectionHeaderArray;

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

typedef struct COFF_Symbol16Node
{
  struct COFF_Symbol16Node *next;
  COFF_Symbol16             data;
} COFF_Symbol16Node;

typedef struct COFF_Symbol16List
{
  U64                count;
  COFF_Symbol16Node *first;
  COFF_Symbol16Node *last;
} COFF_Symbol16List;

typedef struct COFF_Symbol32Array
{
  U64            count;
  COFF_Symbol32 *v;
} COFF_Symbol32Array;

typedef struct COFF_ParsedSymbol
{
  String8               name;
  U64                   value;
  U32                   section_number;
  COFF_SymbolType       type;
  COFF_SymStorageClass  storage_class;
  U8                    aux_symbol_count;
  void                 *raw_symbol;
} COFF_ParsedSymbol;

typedef U32 COFF_SymbolValueInterpType;
enum
{
  COFF_SymbolValueInterp_Regular,   // symbol has section and offset.
  COFF_SymbolValueInterp_Weak,      // symbol is overridable.
  COFF_SymbolValueInterp_Undefined, // symbol doesn't have a reference section.
  COFF_SymbolValueInterp_Common,    // symbol has no section but still has size.
  COFF_SymbolValueInterp_Abs,       // symbol has an absolute (non-relocatable) value and is not an address.
  COFF_SymbolValueInterp_Debug      // symbol is used to provide general type of debugging information.
};

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

typedef struct COFF_RelocNode
{
  struct COFF_RelocNode *next;
  COFF_Reloc             data;
} COFF_RelocNode;

typedef struct COFF_RelocList
{
  U64             count;
  COFF_RelocNode *first;
  COFF_RelocNode *last;
} COFF_RelocList;

typedef struct COFF_RelocArray
{
  U64         count;
  COFF_Reloc *v;
} COFF_RelocArray;

typedef struct COFF_RelocInfo
{
  U64 array_off;
  U64 count;
} COFF_RelocInfo;

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

typedef U32 COFF_ResourceIDType;
enum  COFF_ResourceIDTypeEnum
{
  COFF_ResourceIDType_Null,
  COFF_ResourceIDType_Number,
  COFF_ResourceIDType_String,
  COFF_ResourceIDType_Count
};

typedef struct COFF_ResourceID16
{
  COFF_ResourceIDType type;
  union {
    U16      number;
    String16 string;
  } u;
} COFF_ResourceID16;

typedef struct COFF_ResourceID
{
  COFF_ResourceIDType type;
  union {
    U16     number;
    String8 string;
  } u;
} COFF_ResourceID;

typedef struct COFF_ParsedResource
{
  COFF_ResourceID          type;
  COFF_ResourceID          name;
  U32                      data_version;
  COFF_ResourceMemoryFlags memory_flags;
  U16                      language_id;
  U32                      version;
  U32                      characteristics;
  String8                  data;
} COFF_ParsedResource;

typedef struct COFF_ParsedResourceNode
{
  struct COFF_ParsedResourceNode *next;
  COFF_ParsedResource             data;
} COFF_ParsedResourceNode;

typedef struct COFF_ParsedResourceList
{
  U64                      count;
  COFF_ParsedResourceNode *first;
  COFF_ParsedResourceNode *last;
} COFF_ParsedResourceList;

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

typedef enum
{
  COFF_DataType_Null,
  COFF_DataType_Obj,
  COFF_DataType_BigObj,
  COFF_DataType_Import
} COFF_DataType;

typedef enum
{
  COFF_Archive_Null,
  COFF_Archive_Regular,
  COFF_Archive_Thin
} COFF_ArchiveType;

typedef struct COFF_ParsedArchiveMemberHeader
{
  String8        name;           // padded to 16 bytes with spaces
  COFF_TimeStamp time_stamp;
  U32            user_id;        // unix artifact that does not have meaning on windows
  U32            group_id;       // unix artifact that does not have meaning on windows
  String8        mode;           // octal representation the members file mode
  B32            is_end_correct; // set to true if found correct signature after header
  Rng1U64        data_range;
} COFF_ParsedArchiveMemberHeader;

typedef struct COFF_ParsedArchiveImportHeader
{
  B32               is_sig_correct;
  U16               version;
  COFF_MachineType  machine;
  COFF_TimeStamp    time_stamp;
  U32               data_size;
  U16               hint_or_ordinal;
  COFF_ImportType   type;
  COFF_ImportByType import_by;
  String8           func_name;
  String8           dll_name;
} COFF_ParsedArchiveImportHeader;

typedef struct COFF_ArchiveMember
{
  COFF_ParsedArchiveMemberHeader header;
  U64                            offset;
  String8                        data;
} COFF_ArchiveMember;

typedef struct COFF_ArchiveFirstMember
{
  U32      symbol_count;
  U64      member_offset_count;
  U32     *member_offsets;
  String8  string_table;
} COFF_ArchiveFirstMember;

typedef struct COFF_ArchiveSecondMember
{
  U32      member_count;
  U32      symbol_count;
  U64      member_offset_count;
  U32     *member_offsets;
  U64      symbol_index_count;
  U16     *symbol_indices;
  String8  string_table;
} COFF_ArchiveSecondMember;

typedef struct COFF_ArchiveMemberNode
{
  struct COFF_ArchiveMemberNode *next;
  COFF_ArchiveMember             data;
} COFF_ArchiveMemberNode;

typedef struct COFF_ArchiveMemberList
{
  U64                     count;
  COFF_ArchiveMemberNode *first;
  COFF_ArchiveMemberNode *last;
} COFF_ArchiveMemberList;

typedef struct COFF_ArchiveParse
{
  B32                      has_second_header;
  B32                      has_long_names;
  COFF_ArchiveFirstMember  first_member;
  COFF_ArchiveSecondMember second_member;
  String8                  long_names;
  String8                  error;
} COFF_ArchiveParse;

////////////////////////////////
// Obj Header

internal B32 coff_is_big_obj(String8 raw_coff);
internal B32 coff_is_obj    (String8 raw_coff);
internal COFF_FileHeaderInfo coff_file_header_info_from_data(String8 raw_coff);

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

internal COFF_SectionHeader ** coff_section_table_from_data(Arena *arena, String8 data, Rng1U64 section_table_range);

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

internal COFF_ParsedSymbol coff_parse_symbol32(String8 string_table, COFF_Symbol32 *sym32);
internal COFF_ParsedSymbol coff_parse_symbol16(String8 string_table, COFF_Symbol16 *sym16);
internal COFF_ParsedSymbol coff_parse_symbol(COFF_FileHeaderInfo header, String8 string_table, String8 symbol_table, U32 symbol_idx);

internal COFF_Symbol32Array coff_symbol_array_from_data_16(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count);
internal COFF_Symbol32Array coff_symbol_array_from_data_32(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count);
internal COFF_Symbol32Array coff_symbol_array_from_data   (Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count, U64 symbol_size);

internal COFF_Symbol16Node *coff_symbol16_list_push(Arena *arena, COFF_Symbol16List *list, COFF_Symbol16 symbol);

internal COFF_SymbolValueInterpType coff_interp_symbol(U32 section_number, U32 value, COFF_SymStorageClass storage_class);

internal void coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out);
internal COFF_SymbolWeakExt * coff_parse_weak_tag(COFF_ParsedSymbol symbol, B32 is_big_obj);

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

internal COFF_RelocInfo coff_reloc_info_from_section_header(String8 data, COFF_SectionHeader *header);

////////////////////////////////
// Resource

internal String8         coff_resource_string_from_str16 (Arena *arena, String16 string);
internal String8         coff_resource_string_from_str8  (Arena *arena, String8 string);
internal String8         coff_resource_number_from_u16   (Arena *arena, U16 number);
internal COFF_ResourceID coff_utf8_resource_id_from_utf16(Arena *arena, COFF_ResourceID16 *id_16);

internal U64                     coff_read_resource_id_utf16 (String8 raw_res, U64 off, COFF_ResourceID16 *id_out);
internal U64                     coff_read_resource          (Arena *arena, String8 raw_res, U64 off, COFF_ParsedResource *res_out);
internal COFF_ParsedResourceList coff_resource_list_from_data(Arena *arena, String8 data);

internal String8 coff_write_resource_id(Arena *arena, COFF_ResourceID id);
internal String8 coff_write_resource   (Arena *arena, COFF_ResourceID type, COFF_ResourceID name, U32 data_version, COFF_ResourceMemoryFlags memory_flags, U16 language_id, U32 version, U32 characteristics, String8 data);

internal int coff_resource_id_compar(void *raw_a, void *raw_b); // COFF_ResourceID

////////////////////////////////
// Archive

internal B32              coff_is_import             (String8 raw_archive_member);
internal COFF_DataType    coff_data_type_from_data   (String8 raw_archive_member);
internal B32              coff_is_regular_archive    (String8 raw_archive);
internal B32              coff_is_thin_archive       (String8 raw_archive);
internal COFF_ArchiveType coff_archive_type_from_data(String8 raw_archive);

internal U64                      coff_parse_archive_member_header(String8 raw_archive, U64 offset, COFF_ParsedArchiveMemberHeader *header_out);
internal COFF_ArchiveFirstMember  coff_parse_first_archive_member (COFF_ArchiveMember *member);
internal COFF_ArchiveSecondMember coff_parse_second_archive_member(COFF_ArchiveMember *member);
internal String8                  coff_parse_long_name            (String8 long_names, String8 name);
internal U64                      coff_parse_import               (String8 raw_archive_member, U64 offset, COFF_ParsedArchiveImportHeader *header_out);

internal COFF_ArchiveMember             coff_archive_member_from_offset(String8 raw_archive, U64 offset);
internal COFF_ArchiveMember             coff_archive_member_from_data  (String8 raw_archive_member);
internal COFF_ParsedArchiveImportHeader coff_archive_import_from_data  (String8 raw_archive_member);

internal U64 coff_regular_archive_member_iter_init(String8 raw_archive);
internal B32 coff_regular_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out);

internal U64 coff_thin_archive_member_iter_init(String8 raw_archive);
internal B32 coff_thin_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out);

internal COFF_ArchiveParse coff_regular_archive_parse_from_member_list(COFF_ArchiveMemberList list);
internal COFF_ArchiveParse coff_thin_archive_parse_from_data          (String8 raw_archive);
internal COFF_ArchiveParse coff_regular_archive_parse_from_data       (String8 raw_archive);
internal COFF_ArchiveParse coff_archive_parse_from_data               (String8 raw_archive);

#endif // COFF_PARSE_H
