////////////////////////////////
// Functions: ELF

// normalizing

MR4TH_SYMBOL_STATIC U16
elf_u16_decode(void *ptr, B32 flip){
    U16 result = 0;
    if (flip){
        U8 *buf = (U8*)ptr;
        result = buf[1] | (buf[0] << 8);
    }
    else{
        result = *(U16*)ptr;
    }
    return(result);
}

MR4TH_SYMBOL_STATIC U32
elf_u32_decode(void *ptr, B32 flip){
    U32 result = 0;
    if (flip){
        U8 *buf = (U8*)ptr;
        result = buf[3] | (buf[2] << 8) | (buf[1] << 16) | (buf[0] << 24);
    }
    else{
        result = *(U32*)ptr;
    }
    return(result);
}

MR4TH_SYMBOL_STATIC U64
elf_u64_decode(void *ptr, B32 flip){
    U64 result = 0;
    if (flip){
        U8 *buf = (U8*)ptr;
        result = ((((U64)buf[7]) <<  0) | (((U64)buf[6]) <<  8) |
                  (((U64)buf[5]) << 16) | (((U64)buf[4]) << 24) |
                  (((U64)buf[3]) << 32) | (((U64)buf[2]) << 40) |
                  (((U64)buf[1]) << 48) | (((U64)buf[0]) << 56));
    }
    else{
        result = *(U64*)ptr;
    }
    return(result);
}

MR4TH_SYMBOL_STATIC void
elf_header64_from_header32(ELF_Header64 *dst, ELF_Header32 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    
    MemoryCopyArray(dst->ident, src->ident);
    dst->type                 =      elf_u16_decode(&src->type,                 flip_order);
    dst->machine              =      elf_u16_decode(&src->machine,              flip_order);
    dst->version              =      elf_u32_decode(&src->version,              flip_order);
    dst->entry                = (U64)elf_u32_decode(&src->entry,                flip_order);
    dst->segment_table_foff   = (U64)elf_u32_decode(&src->segment_table_foff,   flip_order);
    dst->section_table_foff   = (U64)elf_u32_decode(&src->section_table_foff,   flip_order);
    dst->flags                =      elf_u32_decode(&src->flags,                flip_order);
    dst->header_size          =      elf_u16_decode(&src->header_size,          flip_order);
    dst->segment_size         =      elf_u16_decode(&src->segment_size,         flip_order);
    dst->segment_count        =      elf_u16_decode(&src->segment_count,        flip_order);
    dst->section_size         =      elf_u16_decode(&src->section_size,         flip_order);
    dst->section_count        =      elf_u16_decode(&src->section_count,        flip_order);
    dst->string_section_index =      elf_u16_decode(&src->string_section_index, flip_order);
}

MR4TH_SYMBOL_STATIC void
elf_header64_from_header64(ELF_Header64 *dst, ELF_Header64 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    
    if (!flip_order){
        MemoryCopyStruct(dst, src);
    }
    else{
        MemoryCopyArray(dst->ident, src->ident);
        dst->type                 = elf_u16_decode(&src->type,                 1);
        dst->machine              = elf_u16_decode(&src->machine,              1);
        dst->version              = elf_u32_decode(&src->version,              1);
        dst->entry                = elf_u32_decode(&src->entry,                1);
        dst->segment_table_foff   = elf_u32_decode(&src->segment_table_foff,   1);
        dst->section_table_foff   = elf_u32_decode(&src->section_table_foff,   1);
        dst->flags                = elf_u32_decode(&src->flags,                1);
        dst->header_size          = elf_u16_decode(&src->header_size,          1);
        dst->segment_size         = elf_u16_decode(&src->segment_size,         1);
        dst->segment_count        = elf_u16_decode(&src->segment_count,        1);
        dst->section_size         = elf_u16_decode(&src->section_size,         1);
        dst->section_count        = elf_u16_decode(&src->section_count,        1);
        dst->string_section_index = elf_u16_decode(&src->string_section_index, 1);
    }
}

MR4TH_SYMBOL_STATIC void
elf_section64_from_section32(ELF_Section64 *dst, ELF_Section32 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    
    dst->name      = elf_u32_decode(&src->name,      flip_order);
    dst->type      = elf_u32_decode(&src->type,      flip_order);
    dst->flags     = elf_u32_decode(&src->flags,     flip_order);
    dst->addr      = elf_u32_decode(&src->addr,      flip_order);
    dst->offset    = elf_u32_decode(&src->offset,    flip_order);
    dst->size      = elf_u32_decode(&src->size,      flip_order);
    dst->link      = elf_u32_decode(&src->link,      flip_order);
    dst->info      = elf_u32_decode(&src->info,      flip_order);
    dst->addralign = elf_u32_decode(&src->addralign, flip_order);
    dst->entsize   = elf_u32_decode(&src->entsize,   flip_order);
}

MR4TH_SYMBOL_STATIC void
elf_section64_from_section64(ELF_Section64 *dst, ELF_Section64 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    
    if (!flip_order){
        MemoryCopyStruct(dst, src);
    }
    else{
        dst->name      = elf_u32_decode(&src->name,      1);
        dst->type      = elf_u32_decode(&src->type,      1);
        dst->flags     = elf_u64_decode(&src->flags,     1);
        dst->addr      = elf_u64_decode(&src->addr,      1);
        dst->offset    = elf_u64_decode(&src->offset,    1);
        dst->size      = elf_u64_decode(&src->size,      1);
        dst->link      = elf_u32_decode(&src->link,      1);
        dst->info      = elf_u32_decode(&src->info,      1);
        dst->addralign = elf_u64_decode(&src->addralign, 1);
        dst->entsize   = elf_u64_decode(&src->entsize,   1);
    }
}

MR4TH_SYMBOL_STATIC void
elf_segment64_from_segment32(ELF_Segment64 *dst, ELF_Segment32 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    
    dst->type        = elf_u32_decode(&src->type,        flip_order);
    dst->offset      = elf_u32_decode(&src->offset,      flip_order);
    dst->vaddr       = elf_u32_decode(&src->vaddr,       flip_order);
    dst->paddr       = elf_u32_decode(&src->paddr,       flip_order);
    dst->file_size   = elf_u32_decode(&src->file_size,   flip_order);
    dst->memory_size = elf_u32_decode(&src->memory_size, flip_order);
    dst->flags       = elf_u32_decode(&src->flags,       flip_order);
    dst->align       = elf_u32_decode(&src->align,       flip_order);
}

MR4TH_SYMBOL_STATIC void
elf_segment64_from_segment64(ELF_Segment64 *dst, ELF_Segment64 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    
    if (!flip_order){
        MemoryCopyStruct(dst, src);
    }
    else{
        dst->type        = elf_u32_decode(&src->type,        1);
        dst->flags       = elf_u32_decode(&src->flags,       1);
        dst->offset      = elf_u64_decode(&src->offset,      1);
        dst->vaddr       = elf_u64_decode(&src->vaddr,       1);
        dst->paddr       = elf_u64_decode(&src->paddr,       1);
        dst->file_size   = elf_u64_decode(&src->file_size,   1);
        dst->memory_size = elf_u64_decode(&src->memory_size, 1);
        dst->align       = elf_u64_decode(&src->align,       1);
    }
}

MR4TH_SYMBOL_STATIC void
elf_symbol64_from_symbol32(ELF_Symbol64 *dst, ELF_Symbol32 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    
    dst->name          = elf_u32_decode(&src->name,  flip_order);
    dst->value         = elf_u32_decode(&src->value, flip_order);
    dst->size          = elf_u32_decode(&src->size,  flip_order);
    dst->info          = src->info;
    dst->other         = src->other;
    dst->section_index = elf_u16_decode(&src->section_index, flip_order);
}

MR4TH_SYMBOL_STATIC void
elf_symbol64_from_symbol64(ELF_Symbol64 *dst, ELF_Symbol64 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    
    if (!flip_order){
        MemoryCopyStruct(dst, src);
    }
    else{
        dst->name          = elf_u32_decode(&src->name, 1);
        dst->info          = src->info;
        dst->other         = src->other;
        dst->section_index = elf_u16_decode(&src->section_index, 1);
        dst->value         = elf_u64_decode(&src->value, 1);
        dst->size          = elf_u64_decode(&src->size, 1);
    }
}

MR4TH_SYMBOL_STATIC void
elf_reloca64_from_reloc32(ELF_RelocationAdd64 *dst, ELF_Relocation32 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    dst->offset = elf_u32_decode(&src->offset, flip_order);
    dst->info   = elf_u32_decode(&src->info, flip_order);
    dst->addend = 0;
}

MR4TH_SYMBOL_STATIC void
elf_reloca64_from_reloc64(ELF_RelocationAdd64 *dst, ELF_Relocation64 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    dst->offset = elf_u64_decode(&src->offset, flip_order);
    dst->info   = elf_u64_decode(&src->info, flip_order);
    dst->addend = 0;
}

MR4TH_SYMBOL_STATIC void
elf_reloca64_from_reloca32(ELF_RelocationAdd64 *dst, ELF_RelocationAdd32 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    dst->offset = elf_u32_decode(&src->offset, flip_order);
    dst->info   = elf_u32_decode(&src->info, flip_order);
    dst->addend = (S32)elf_u32_decode(&src->addend, flip_order);
}

MR4TH_SYMBOL_STATIC void
elf_reloca64_from_reloca64(ELF_RelocationAdd64 *dst, ELF_RelocationAdd64 *src, ELF_Encoding src_encoding){
    B32 src_big_endian = (src_encoding == ELF_Encoding_2MSB);
    B32 flip_order = (src_big_endian != ARCH_BIG_ENDIAN);
    dst->offset = elf_u64_decode(&src->offset, flip_order);
    dst->info   = elf_u64_decode(&src->info, flip_order);
    dst->addend = (S64)elf_u64_decode(&src->addend, flip_order);
}

// strings

MR4TH_SYMBOL_STATIC String8
elf_str8_from_identification_idx(ELF_IdentificationIdx idx){
    String8 result = str8_lit("ERROR");
    switch (idx){
#define X(N,C) case C: result = str8_lit(#N); break;
        ELF_IdentificationIdx_XList(X)
#undef X
    }
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_class(ELF_Class elf_class){
    String8 result = str8_lit("ERROR");
    switch (elf_class){
#define X(N,C) case C: result = str8_lit(#N); break;
        ELF_Class_XList(X)
#undef X
    }
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_encoding(ELF_Encoding elf_encoding){
    String8 result = str8_lit("ERROR");
    switch (elf_encoding){
#define X(N,C) case C: result = str8_lit(#N); break;
        ELF_Encoding_XList(X)
#undef X
    }
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_osabi(ELF_OsAbiExtension elf_osabi){
    String8 result = str8_lit("ERROR");
    switch (elf_osabi){
#define X(N,C) case C: result = str8_lit(#N); break;
        ELF_OsAbiExtension_XList(X)
#undef X
    }
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_file_type(ELF_FileType file_type){
    String8 result = str8_lit("ERROR");
    switch (file_type){
#define X(N,C) case C: result = str8_lit(#N); break;
        ELF_FileType_XList(X)
#undef X
    }
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_machine_type(ELF_FileType machine_type){
    String8 result = str8_lit("ERROR");
    switch (machine_type){
#define X(N,C) case C: result = str8_lit(#N); break;
        ELF_MachineType_XList(X)
#undef X
    }
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_section_type(ELF_SectionType section_type){
    String8 result = str8_lit("ERROR");
    
    if (ELF_SectionType_LOOS <= section_type &&
        section_type <= ELF_SectionType_HIOS){
        result = str8_lit("OS-CUSTOM-TYPE");
    }
    
    else if (ELF_SectionType_LOPROC <= section_type &&
             section_type <= ELF_SectionType_HIPROC){
        result = str8_lit("PROC-CUSTOM-TYPE");
    }
    
    else if (ELF_SectionType_LOUSER <= section_type &&
             section_type <= ELF_SectionType_HIUSER){
        result = str8_lit("USER-CUSTOM-TYPE");
    }
    
    else{
        switch (section_type){
#define X(N,C) case C: result = str8_lit(#N); break;
            ELF_SectionType_XList(X)
#undef X
        }
    }
    
    return(result);
}

MR4TH_SYMBOL_STATIC void
elf_str8list_from_section_flags(Arena *arena, String8List *out, U32 flags){
    U32 f = 1;
    for (U32 i = 0; i < 20; i += 1){
        if ((flags & f) != 0){
            switch (f){
#define X(N,C) case C: str8_list_push(arena, out, str8_lit(#N)); break;
                ELF_SectionFlags_XList(X)
#undef X
            }
        }
        f <<= 1;
    }
    if ((flags & ELF_SectionFlag_MASKOS) != 0){
        str8_list_pushf(arena, out, "OS-CUSTOM(%02x)", (flags >> 20)&0xFF);
    }
    if ((flags & ELF_SectionFlag_MASKPROC) != 0){
        str8_list_pushf(arena, out, "PROC-CUSTOM(%01x)", (flags >> 28)&0xF);
    }
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_section_flags(Arena *arena, U32 flags){
    ArenaTemp scratch = arena_get_scratch(&arena, 1);
    String8List list = {0};
    elf_str8list_from_section_flags(scratch.arena, &list, flags);
    String8 result = str8_join_flags(arena, &list);
    arena_release_scratch(&scratch);
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_segment_type(ELF_SegmentType segment_type){
    String8 result = str8_lit("ERROR");
    
    if (ELF_SegmentType_LOOS <= segment_type &&
        segment_type <= ELF_SegmentType_HIOS){
        result = str8_lit("OS-CUSTOM-TYPE");
    }
    
    else if (ELF_SegmentType_LOPROC <= segment_type &&
             segment_type <= ELF_SegmentType_HIPROC){
        result = str8_lit("PROC-CUSTOM-TYPE");
    }
    
    else{
        switch (segment_type){
#define X(N,C) case C: result = str8_lit(#N); break;
            ELF_SegmentType_XList(X)
#undef X
        }
    }
    
    return(result);
}

MR4TH_SYMBOL_STATIC void
elf_str8list_from_segment_flags(Arena *arena, String8List *out, ELF_SegmentFlags flags){
    U32 f = 1;
    for (U32 i = 0; i < 20; i += 1){
        if ((flags & f) != 0){
            switch (f){
#define X(N,C) case C: str8_list_push(arena, out, str8_lit(#N)); break;
                ELF_SegmentFlags_XList(X)
#undef X
            }
        }
        f <<= 1;
    }
    if ((flags & ELF_SegmentFlag_MASKOS) != 0){
        str8_list_pushf(arena, out, "OS-CUSTOM(%02x)", (flags >> 20)&0xFF);
    }
    if ((flags & ELF_SegmentFlag_MASKPROC) != 0){
        str8_list_pushf(arena, out, "PROC-CUSTOM(%01x)", (flags >> 28)&0xF);
    }
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_segment_flags(Arena *arena, ELF_SegmentFlags flags){
    ArenaTemp scratch = arena_get_scratch(&arena, 1);
    String8List list = {0};
    elf_str8list_from_segment_flags(scratch.arena, &list, flags);
    String8 result = str8_join_flags(arena, &list);
    arena_release_scratch(&scratch);
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_symbol_binding(ELF_SymbolBinding bind){
    String8 result = str8_lit("ERROR");
    
    if (ELF_SymbolBinding_LOOS <= bind &&
        bind <= ELF_SymbolBinding_HIOS){
        result = str8_lit("OS-CUSTOM-TYPE");
    }
    
    else if (ELF_SymbolBinding_LOPROC <= bind &&
             bind <= ELF_SymbolBinding_HIPROC){
        result = str8_lit("PROC-CUSTOM-TYPE");
    }
    
    else{
        switch (bind){
#define X(N,C) case C: result = str8_lit(#N); break;
            ELF_SymbolBinding_XList(X)
#undef X
        }
    }
    
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_symbol_type(ELF_SymbolType type){
    String8 result = str8_lit("ERROR");
    
    if (ELF_SymbolType_LOOS <= type &&
        type <= ELF_SymbolType_HIOS){
        result = str8_lit("OS-CUSTOM-TYPE");
    }
    
    else if (ELF_SymbolType_LOPROC <= type &&
             type <= ELF_SymbolType_HIPROC){
        result = str8_lit("PROC-CUSTOM-TYPE");
    }
    
    else{
        switch (type){
#define X(N,C) case C: result = str8_lit(#N); break;
            ELF_SymbolType_XList(X)
#undef X
        }
    }
    
    return(result);
}

MR4TH_SYMBOL_STATIC String8
elf_str8_from_symbol_vis(ELF_SymbolVis vis){
    String8 result = str8_lit("ERROR");
    switch (vis){
#define X(N,C) case C: result = str8_lit(#N); break;
        ELF_SymbolVis_XList(X)
#undef X
    }
    return(result);
}
