//
// Copyright 2018 Sepehr Taghdisian (septag@github). All rights reserved.
// License: https://github.com/septag/dds-ktx#license-bsd-2-clause
//
// Many parts of this code is taken from bimg library: 
// https://github.com/bkaradzic/bimg 
//
// Copyright 2011-2019 Branimir Karadzic. All rights reserved.
// License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
//
// dds-ktx.h - v1.1.0 - Reader/Writer for DDS/KTX formats
//      Parses DDS and KTX files from a memory blob, written in C99
//      
//      Supported formats:
//          For supported formats, see ddsktx_format enum. 
//          Both KTX/DDS parser supports all formats defined in ddsktx_format
//
//      Overriable macros:
//          DDSKTX_API     Define any function specifier for public functions (default: extern)
//          ddsktx_memcpy  default: memcpy(dst, src, size)
//          ddsktx_memset  default: memset(dst, v, size)
//          ddsktx_assert  default: assert(a)
//          ddsktx_strcpy  default: strcpy(dst, src)
//          ddsktx_memcmp  default: memcmp(ptr1, ptr2, size)
//          
//      API:
//          bool ddsktx_parse(ddsktx_texture_info* tc, const void* file_data, int size, ddsktx_error* err);
//              Parses texture file and fills the ddsktx_texture_info struct
//              Returns true if successfully parsed, false if failed with an error message inside ddsktx_error parameter (optional)
//              After format is parsed, you can read the contents of ddsktx_format and create your GPU texture
//              To get pointer to mips and slices see ddsktx_get_sub function
//          
//          void ddsktx_get_sub(const ddsktx_texture_info* tex, ddsktx_sub_data* buff, 
//                           const void* file_data, int size,
//                           int array_idx, int slice_face_idx, int mip_idx);
//              Gets sub-image data, form a parsed texture file
//              user must provided the container object and the original file data which was passed to ddsktx_parse
//              array_idx: array index (0..num_layers)
//              slice_face_idx: depth-slice or cube-face index. 
//                              if 'flags' have DDSKTX_TEXTURE_FLAG_CUBEMAP bit, then this value represents cube-face-index (0..DDSKTX_CUBE_FACE_COUNT)
//                              else it represents depth slice index (0..depth)
//              mip_idx: mip index (0..num_mips-1 in ddsktx_texture_info)
//          
//          const char* ddsktx_format_str(ddsktx_format format);
//              Converts a format enumeration to string
//
//          bool ddsktx_format_compressed(ddsktx_format format);
//              Returns true if format is compressed
//
//      Example (for 2D textures only): 
//          int size;
//          void* dds_data = load_file("test.dds", &size);
//          assert(dds_data);
//          ddsktx_texture_info tc = {0};
//          if (ddsktx_parse(&tc, dds_data, size, NULL)) {
//              assert(tc.depth == 1);
//              assert(!(tc.flags & DDSKTX_TEXTURE_FLAG_CUBEMAP));
//              assert(tc.num_layers == 1);
//              // Create GPU texture from tc data
//              for (int mip = 0; mip < tc.num_mips; mip++) {
//                  ddsktx_sub_data sub_data;
//                  ddsktx_get_sub(&tc, &sub_data, dds_data, size, 0, 0, mip);
//                  // Fill/Set texture sub resource data (mips in this case)
//              }
//          }
//          free(dds_data);     // memory must be valid during stc_ calls
//
// Version history:
//      0.9.0       Initial release, ktx is incomplete
//      1.0.0       Api change: ddsktx_sub_data
//                  Added KTX support
//      1.0.1       Fixed major bugs in KTX parsing
//      1.1.0       Fixed bugs in get_sub routine, refactored some parts, image-viewer example
//
// TODO
//      Write KTX/DDS
//      Read KTX metadata. currently it just stores the offset/size to the metadata block
//

 #pragma once
// #define DDSKTX_IMPLEMENT
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

#ifndef DDSKTX_API
#   ifdef __cplusplus
#       define DDSKTX_API extern "C" 
#   else
#       define DDSKTX_API  
#   endif
#endif

typedef struct ddsktx_sub_data
{
    const void* buff;
    int         width;
    int         height;
    int         size_bytes;
    int         row_pitch_bytes;
} ddsktx_sub_data;

typedef enum ddsktx_format
{
    DDSKTX_FORMAT_BC1,      // DXT1
    DDSKTX_FORMAT_BC2,      // DXT3
    DDSKTX_FORMAT_BC3,      // DXT5
    DDSKTX_FORMAT_BC4,      // ATI1
    DDSKTX_FORMAT_BC5,      // ATI2
    DDSKTX_FORMAT_BC6H,     // BC6H
    DDSKTX_FORMAT_BC7,      // BC7
    DDSKTX_FORMAT_ETC1,     // ETC1 RGB8
    DDSKTX_FORMAT_ETC2,     // ETC2 RGB8
    DDSKTX_FORMAT_ETC2A,    // ETC2 RGBA8
    DDSKTX_FORMAT_ETC2A1,   // ETC2 RGB8A1
    DDSKTX_FORMAT_PTC12,    // PVRTC1 RGB 2bpp
    DDSKTX_FORMAT_PTC14,    // PVRTC1 RGB 4bpp
    DDSKTX_FORMAT_PTC12A,   // PVRTC1 RGBA 2bpp
    DDSKTX_FORMAT_PTC14A,   // PVRTC1 RGBA 4bpp
    DDSKTX_FORMAT_PTC22,    // PVRTC2 RGBA 2bpp
    DDSKTX_FORMAT_PTC24,    // PVRTC2 RGBA 4bpp
    DDSKTX_FORMAT_ATC,      // ATC RGB 4BPP
    DDSKTX_FORMAT_ATCE,     // ATCE RGBA 8 BPP explicit alpha
    DDSKTX_FORMAT_ATCI,     // ATCI RGBA 8 BPP interpolated alpha
    DDSKTX_FORMAT_ASTC4x4,  // ASTC 4x4 8.00 BPP
    DDSKTX_FORMAT_ASTC5x4,  // ASTC 5x4 6.40 BPP
    DDSKTX_FORMAT_ASTC5x5,  // ASTC 5x5 5.12 BPP
    DDSKTX_FORMAT_ASTC6x5,  // ASTC 6x5 4.27 BPP
    DDSKTX_FORMAT_ASTC6x6,  // ASTC 6x6 3.56 BPP
    DDSKTX_FORMAT_ASTC8x5,  // ASTC 8x5 3.20 BPP
    DDSKTX_FORMAT_ASTC8x6,  // ASTC 8x6 2.67 BPP
    DDSKTX_FORMAT_ASTC10x5, // ASTC 10x5 2.56 BPP
    DDSKTX_FORMAT_ASTC10x6, // ASTC 10x6 2.13 BPP
    DDSKTX_FORMAT_ASTC8x8,  // ASTC 8x8 2.00 BPP
    DDSKTX_FORMAT_ASTC10x8, // ASTC 10x8 1.60 BPP
    DDSKTX_FORMAT_ASTC10x10,// ASTC 10x10 1.28 BPP
    DDSKTX_FORMAT_ASTC12x10,// ASTC 12x10 1.07 BPP
    DDSKTX_FORMAT_ASTC12x12,// ASTC 12x12 0.89 BPP
    _DDSKTX_FORMAT_COMPRESSED,
    DDSKTX_FORMAT_A8,
    DDSKTX_FORMAT_R8,
    DDSKTX_FORMAT_RGBA8,
    DDSKTX_FORMAT_RGBA8S,
    DDSKTX_FORMAT_RG16,
    DDSKTX_FORMAT_RGB8,
    DDSKTX_FORMAT_R16,
    DDSKTX_FORMAT_R32F,
    DDSKTX_FORMAT_R16F,
    DDSKTX_FORMAT_RG16F,
    DDSKTX_FORMAT_RG16S,
    DDSKTX_FORMAT_RGBA16F,
    DDSKTX_FORMAT_RGBA16,
    DDSKTX_FORMAT_BGRA8,
    DDSKTX_FORMAT_RGB10A2,
    DDSKTX_FORMAT_RG11B10F,
    DDSKTX_FORMAT_RG8,
    DDSKTX_FORMAT_RG8S,
    _DDSKTX_FORMAT_COUNT
} ddsktx_format;

typedef enum ddsktx_texture_flags
{
    DDSKTX_TEXTURE_FLAG_CUBEMAP = 0x01,       
    DDSKTX_TEXTURE_FLAG_SRGB    = 0x02,        
    DDSKTX_TEXTURE_FLAG_ALPHA   = 0x04,       // Has alpha channel
    DDSKTX_TEXTURE_FLAG_DDS     = 0x08,       // container was DDS file
    DDSKTX_TEXTURE_FLAG_KTX     = 0x10,       // container was KTX file
    DDSKTX_TEXTURE_FLAG_VOLUME  = 0x20,       // 3D volume
} ddsktx_texture_flags;

typedef struct ddsktx_texture_info
{
    int                 data_offset;   // start offset of pixel data
    int                 size_bytes;
    ddsktx_format       format;
    unsigned int        flags;         // ddsktx_texture_flags
    int                 width;
    int                 height;
    int                 depth;
    int                 num_layers;
    int                 num_mips;
    int                 bpp;
    int                 metadata_offset; // ktx only
    int                 metadata_size;   // ktx only
} ddsktx_texture_info;

typedef enum ddsktx_cube_face
{
    DDSKTX_CUBE_FACE_X_POSITIVE = 0,
    DDSKTX_CUBE_FACE_X_NEGATIVE,
    DDSKTX_CUBE_FACE_Y_POSITIVE,
    DDSKTX_CUBE_FACE_Y_NEGATIVE,
    DDSKTX_CUBE_FACE_Z_POSITIVE,
    DDSKTX_CUBE_FACE_Z_NEGATIVE,
    DDSKTX_CUBE_FACE_COUNT
} ddsktx_cube_face;

typedef struct ddsktx_error
{
    char msg[256];
} ddsktx_error;

#ifdef __cplusplus
#   define ddsktx_default(_v) =_v
#else
#   define ddsktx_default(_v)
#endif

DDSKTX_API bool ddsktx_parse(ddsktx_texture_info* tc, const void* file_data, int size, ddsktx_error* err ddsktx_default(NULL));
DDSKTX_API void ddsktx_get_sub(const ddsktx_texture_info* tex, ddsktx_sub_data* buff, 
                         const void* file_data, int size,
                         int array_idx, int slice_face_idx, int mip_idx);
DDSKTX_API const char* ddsktx_format_str(ddsktx_format format);
DDSKTX_API bool        ddsktx_format_compressed(ddsktx_format format);

////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
#ifdef DDSKTX_IMPLEMENT

#define stc__makefourcc(_a, _b, _c, _d) ( ( (uint32_t)(_a) | ( (uint32_t)(_b) << 8) | \
                                        ( (uint32_t)(_c) << 16) | ( (uint32_t)(_d) << 24) ) )

// DDS: https://docs.microsoft.com/en-us/windows/desktop/direct3ddds/dx-graphics-dds-pguide
#define DDSKTX__DDS_HEADER_SIZE 124
#define DDSKTX__DDS_MAGIC       stc__makefourcc('D', 'D', 'S', ' ')
#define DDSKTX__DDS_DXT1        stc__makefourcc('D', 'X', 'T', '1')
#define DDSKTX__DDS_DXT2        stc__makefourcc('D', 'X', 'T', '2')
#define DDSKTX__DDS_DXT3        stc__makefourcc('D', 'X', 'T', '3')
#define DDSKTX__DDS_DXT4        stc__makefourcc('D', 'X', 'T', '4')
#define DDSKTX__DDS_DXT5        stc__makefourcc('D', 'X', 'T', '5')
#define DDSKTX__DDS_ATI1        stc__makefourcc('A', 'T', 'I', '1')
#define DDSKTX__DDS_BC4U        stc__makefourcc('B', 'C', '4', 'U')
#define DDSKTX__DDS_ATI2        stc__makefourcc('A', 'T', 'I', '2')
#define DDSKTX__DDS_BC5U        stc__makefourcc('B', 'C', '5', 'U')
#define DDSKTX__DDS_DX10        stc__makefourcc('D', 'X', '1', '0')

#define DDSKTX__DDS_ETC1        stc__makefourcc('E', 'T', 'C', '1')
#define DDSKTX__DDS_ETC2        stc__makefourcc('E', 'T', 'C', '2')
#define DDSKTX__DDS_ET2A        stc__makefourcc('E', 'T', '2', 'A')
#define DDSKTX__DDS_PTC2        stc__makefourcc('P', 'T', 'C', '2')
#define DDSKTX__DDS_PTC4        stc__makefourcc('P', 'T', 'C', '4')
#define DDSKTX__DDS_ATC         stc__makefourcc('A', 'T', 'C', ' ')
#define DDSKTX__DDS_ATCE        stc__makefourcc('A', 'T', 'C', 'E')
#define DDSKTX__DDS_ATCI        stc__makefourcc('A', 'T', 'C', 'I')
#define DDSKTX__DDS_ASTC4x4     stc__makefourcc('A', 'S', '4', '4')
#define DDSKTX__DDS_ASTC5x4     stc__makefourcc('A', 'S', '5', '4')
#define DDSKTX__DDS_ASTC5x5     stc__makefourcc('A', 'S', '5', '5')
#define DDSKTX__DDS_ASTC6x5     stc__makefourcc('A', 'S', '6', '5')
#define DDSKTX__DDS_ASTC6x6     stc__makefourcc('A', 'S', '6', '6')
#define DDSKTX__DDS_ASTC8x5     stc__makefourcc('A', 'S', '8', '5')
#define DDSKTX__DDS_ASTC8x6     stc__makefourcc('A', 'S', '8', '6')
#define DDSKTX__DDS_ASTC10x5    stc__makefourcc('A', 'S', ':', '5')
#define DDSKTX__DDS_ASTC10x6    stc__makefourcc('A', 'S', ':', '6')
#define DDSKTX__DDS_ASTC8x8     stc__makefourcc('A', 'S', '8', '8')
#define DDSKTX__DDS_ASTC10x8    stc__makefourcc('A', 'S', ':', '8')
#define DDSKTX__DDS_ASTC10x10   stc__makefourcc('A', 'S', ':', ':')
#define DDSKTX__DDS_ASTC12x10   stc__makefourcc('A', 'S', '<', ':')
#define DDSKTX__DDS_ASTC12x12   stc__makefourcc('A', 'S', '<', '<')

#define DDSKTX__DDS_R8G8B8         20
#define DDSKTX__DDS_A8R8G8B8       21
#define DDSKTX__DDS_R5G6B5         23
#define DDSKTX__DDS_A1R5G5B5       25
#define DDSKTX__DDS_A4R4G4B4       26
#define DDSKTX__DDS_A2B10G10R10    31
#define DDSKTX__DDS_G16R16         34
#define DDSKTX__DDS_A2R10G10B10    35
#define DDSKTX__DDS_A16B16G16R16   36
#define DDSKTX__DDS_A8L8           51
#define DDSKTX__DDS_R16F           111
#define DDSKTX__DDS_G16R16F        112
#define DDSKTX__DDS_A16B16G16R16F  113
#define DDSKTX__DDS_R32F           114
#define DDSKTX__DDS_G32R32F        115
#define DDSKTX__DDS_A32B32G32R32F  116

#define DDSKTX__DDS_FORMAT_R32G32B32A32_FLOAT  2
#define DDSKTX__DDS_FORMAT_R32G32B32A32_UINT   3
#define DDSKTX__DDS_FORMAT_R16G16B16A16_FLOAT  10
#define DDSKTX__DDS_FORMAT_R16G16B16A16_UNORM  11
#define DDSKTX__DDS_FORMAT_R16G16B16A16_UINT   12
#define DDSKTX__DDS_FORMAT_R32G32_FLOAT        16
#define DDSKTX__DDS_FORMAT_R32G32_UINT         17
#define DDSKTX__DDS_FORMAT_R10G10B10A2_UNORM   24
#define DDSKTX__DDS_FORMAT_R11G11B10_FLOAT     26
#define DDSKTX__DDS_FORMAT_R8G8B8A8_UNORM      28
#define DDSKTX__DDS_FORMAT_R8G8B8A8_UNORM_SRGB 29
#define DDSKTX__DDS_FORMAT_R16G16_FLOAT        34
#define DDSKTX__DDS_FORMAT_R16G16_UNORM        35
#define DDSKTX__DDS_FORMAT_R32_FLOAT           41
#define DDSKTX__DDS_FORMAT_R32_UINT            42
#define DDSKTX__DDS_FORMAT_R8G8_UNORM          49
#define DDSKTX__DDS_FORMAT_R16_FLOAT           54
#define DDSKTX__DDS_FORMAT_R16_UNORM           56
#define DDSKTX__DDS_FORMAT_R8_UNORM            61
#define DDSKTX__DDS_FORMAT_R1_UNORM            66
#define DDSKTX__DDS_FORMAT_BC1_UNORM           71
#define DDSKTX__DDS_FORMAT_BC1_UNORM_SRGB      72
#define DDSKTX__DDS_FORMAT_BC2_UNORM           74
#define DDSKTX__DDS_FORMAT_BC2_UNORM_SRGB      75
#define DDSKTX__DDS_FORMAT_BC3_UNORM           77
#define DDSKTX__DDS_FORMAT_BC3_UNORM_SRGB      78
#define DDSKTX__DDS_FORMAT_BC4_UNORM           80
#define DDSKTX__DDS_FORMAT_BC5_UNORM           83
#define DDSKTX__DDS_FORMAT_B5G6R5_UNORM        85
#define DDSKTX__DDS_FORMAT_B5G5R5A1_UNORM      86
#define DDSKTX__DDS_FORMAT_B8G8R8A8_UNORM      87
#define DDSKTX__DDS_FORMAT_B8G8R8A8_UNORM_SRGB 91
#define DDSKTX__DDS_FORMAT_BC6H_SF16           96
#define DDSKTX__DDS_FORMAT_BC7_UNORM           98
#define DDSKTX__DDS_FORMAT_BC7_UNORM_SRGB      99
#define DDSKTX__DDS_FORMAT_B4G4R4A4_UNORM      115

#define DDSKTX__DDS_DX10_DIMENSION_TEXTURE2D 3
#define DDSKTX__DDS_DX10_DIMENSION_TEXTURE3D 4
#define DDSKTX__DDS_DX10_MISC_TEXTURECUBE    4

#define DDSKTX__DDSD_CAPS                  0x00000001
#define DDSKTX__DDSD_HEIGHT                0x00000002
#define DDSKTX__DDSD_WIDTH                 0x00000004
#define DDSKTX__DDSD_PITCH                 0x00000008
#define DDSKTX__DDSD_PIXELFORMAT           0x00001000
#define DDSKTX__DDSD_MIPMAPCOUNT           0x00020000
#define DDSKTX__DDSD_LINEARSIZE            0x00080000
#define DDSKTX__DDSD_DEPTH                 0x00800000

#define DDSKTX__DDPF_ALPHAPIXELS           0x00000001
#define DDSKTX__DDPF_ALPHA                 0x00000002
#define DDSKTX__DDPF_FOURCC                0x00000004
#define DDSKTX__DDPF_INDEXED               0x00000020
#define DDSKTX__DDPF_RGB                   0x00000040
#define DDSKTX__DDPF_YUV                   0x00000200
#define DDSKTX__DDPF_LUMINANCE             0x00020000
#define DDSKTX__DDPF_BUMPDUDV              0x00080000

#define DDSKTX__DDSCAPS_COMPLEX            0x00000008
#define DDSKTX__DDSCAPS_TEXTURE            0x00001000
#define DDSKTX__DDSCAPS_MIPMAP             0x00400000

#define DDSKTX__DDSCAPS2_VOLUME            0x00200000
#define DDSKTX__DDSCAPS2_CUBEMAP           0x00000200
#define DDSKTX__DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
#define DDSKTX__DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
#define DDSKTX__DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
#define DDSKTX__DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
#define DDSKTX__DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
#define DDSKTX__DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000

#define DDSKTX__DDSCAPS2_CUBEMAP_ALLSIDES (0      \
            | DDSKTX__DDSCAPS2_CUBEMAP_POSITIVEX \
            | DDSKTX__DDSCAPS2_CUBEMAP_NEGATIVEX \
            | DDSKTX__DDSCAPS2_CUBEMAP_POSITIVEY \
            | DDSKTX__DDSCAPS2_CUBEMAP_NEGATIVEY \
            | DDSKTX__DDSCAPS2_CUBEMAP_POSITIVEZ \
            | DDSKTX__DDSCAPS2_CUBEMAP_NEGATIVEZ )

#pragma pack(push, 1)
typedef struct ddsktx__dds_pixel_format
{
    uint32_t size;
    uint32_t flags;
    uint32_t fourcc;
    uint32_t rgb_bit_count;
    uint32_t bit_mask[4];
} ddsktx__dds_pixel_format;

// https://docs.microsoft.com/en-us/windows/desktop/direct3ddds/dds-header
typedef struct ddsktx__dds_header
{
    uint32_t                size;
    uint32_t                flags;
    uint32_t                height;
    uint32_t                width;
    uint32_t                pitch_lin_size;
    uint32_t                depth;
    uint32_t                mip_count;
    uint32_t                reserved1[11];
    ddsktx__dds_pixel_format   pixel_format;
    uint32_t                caps1;
    uint32_t                caps2;
    uint32_t                caps3;
    uint32_t                caps4;
    uint32_t                reserved2;
} ddsktx__dds_header;

// https://docs.microsoft.com/en-us/windows/desktop/direct3ddds/dds-header-dxt10
typedef struct ddsktx__dds_header_dxgi
{
    uint32_t dxgi_format;
    uint32_t dimension;
    uint32_t misc_flags;
    uint32_t array_size;
    uint32_t misc_flags2;
} ddsktx__dds_header_dxgi;

typedef struct ddsktx__ktx_header
{
    uint8_t  id[8];
    uint32_t endianess;
    uint32_t type;
    uint32_t type_size;
    uint32_t format;
    uint32_t internal_format;
    uint32_t base_internal_format;
    uint32_t width;
    uint32_t height;
    uint32_t depth;
    uint32_t array_count;
    uint32_t face_count;
    uint32_t mip_count;
    uint32_t metadata_size;
} ddsktx__ktx_header;
#pragma pack(pop)

typedef struct ddsktx__dds_translate_fourcc_format
{
    uint32_t            dds_format;
    ddsktx_format  format;
    bool                srgb;
} ddsktx__dds_translate_fourcc_format;

typedef struct ddsktx__dds_translate_pixel_format
{
    uint32_t            bit_count;
    uint32_t            flags;
    uint32_t            bit_mask[4];
    ddsktx_format  format;
} ddsktx__dds_translate_pixel_format;

typedef struct ddsktx__mem_reader
{
    const uint8_t* buff;
    int            total;
    int            offset;
} ddsktx__mem_reader;

typedef struct ddsktx__block_info
{
    uint8_t bpp;
    uint8_t block_width;
    uint8_t block_height;
    uint8_t block_size;
    uint8_t min_block_x;
    uint8_t min_block_y;
    uint8_t depth_bits;
    uint8_t stencil_bits;
    uint8_t r_bits;
    uint8_t g_bits;
    uint8_t b_bits;
    uint8_t a_bits;
    uint8_t encoding;    
} ddsktx__block_info;

#ifndef ddsktx_memcpy
#   include <string.h>
#   define ddsktx_memcpy(_dst, _src, _size)    memcpy((_dst), (_src), (_size))
#endif

#ifndef ddsktx_memset
#   include <string.h>
#   define ddsktx_memset(_dst, _v, _size)      memset((_dst), (_v), (_size))
#endif

#ifndef ddsktx_assert
#   include <assert.h>
#   define ddsktx_assert(_a)       assert(_a)
#endif

#ifndef ddsktx_strcpy
#   include <string.h>
#   ifdef _MSC_VER
#       define ddsktx_strcpy(_dst, _src)   strcpy_s((_dst), sizeof(_dst), (_src))
#   else
#       define ddsktx_strcpy(_dst, _src)   strcpy((_dst), (_src))
#   endif
#endif

#ifndef ddsktx_memcmp
#   include <string.h>
#   define ddsktx_memcmp(_ptr1, _ptr2, _num) memcmp((_ptr1), (_ptr2), (_num))
#endif

#define ddsktx__max(a, b)                  ((a) > (b) ? (a) : (b))
#define ddsktx__min(a, b)                  ((a) < (b) ? (a) : (b))
#define ddsktx__align_mask(_value, _mask)  (((_value)+(_mask)) & ((~0)&(~(_mask))))
#define ddsktx__err(_err, _msg)            if (_err)  ddsktx_strcpy(_err->msg, _msg);   return false

static const ddsktx__dds_translate_fourcc_format k__translate_dds_fourcc[] = {
    { DDSKTX__DDS_DXT1,                  DDSKTX_FORMAT_BC1,     false },
    { DDSKTX__DDS_DXT2,                  DDSKTX_FORMAT_BC2,     false },
    { DDSKTX__DDS_DXT3,                  DDSKTX_FORMAT_BC2,     false },
    { DDSKTX__DDS_DXT4,                  DDSKTX_FORMAT_BC3,     false },
    { DDSKTX__DDS_DXT5,                  DDSKTX_FORMAT_BC3,     false },
    { DDSKTX__DDS_ATI1,                  DDSKTX_FORMAT_BC4,     false },
    { DDSKTX__DDS_BC4U,                  DDSKTX_FORMAT_BC4,     false },
    { DDSKTX__DDS_ATI2,                  DDSKTX_FORMAT_BC5,     false },
    { DDSKTX__DDS_BC5U,                  DDSKTX_FORMAT_BC5,     false },
    { DDSKTX__DDS_ETC1,                  DDSKTX_FORMAT_ETC1,     false },
    { DDSKTX__DDS_ETC2,                  DDSKTX_FORMAT_ETC2,     false },
    { DDSKTX__DDS_ET2A,                  DDSKTX_FORMAT_ETC2A,    false },
    { DDSKTX__DDS_PTC2,                  DDSKTX_FORMAT_PTC12A,   false },
    { DDSKTX__DDS_PTC4,                  DDSKTX_FORMAT_PTC14A,   false },
    { DDSKTX__DDS_ATC ,                  DDSKTX_FORMAT_ATC,      false },
    { DDSKTX__DDS_ATCE,                  DDSKTX_FORMAT_ATCE,     false },
    { DDSKTX__DDS_ATCI,                  DDSKTX_FORMAT_ATCI,     false },
    { DDSKTX__DDS_ASTC4x4,               DDSKTX_FORMAT_ASTC4x4,  false },
    { DDSKTX__DDS_ASTC5x4,               DDSKTX_FORMAT_ASTC5x4,  false },
    { DDSKTX__DDS_ASTC5x5,               DDSKTX_FORMAT_ASTC5x5,  false },
    { DDSKTX__DDS_ASTC6x5,               DDSKTX_FORMAT_ASTC6x5,  false },
    { DDSKTX__DDS_ASTC6x6,               DDSKTX_FORMAT_ASTC6x6,  false },
    { DDSKTX__DDS_ASTC8x5,               DDSKTX_FORMAT_ASTC8x5,  false },
    { DDSKTX__DDS_ASTC8x6,               DDSKTX_FORMAT_ASTC8x6,  false },
    { DDSKTX__DDS_ASTC10x5,              DDSKTX_FORMAT_ASTC10x5,  false },
    { DDSKTX__DDS_ASTC10x6,              DDSKTX_FORMAT_ASTC10x6,  false },
    { DDSKTX__DDS_ASTC8x8,               DDSKTX_FORMAT_ASTC8x8,  false },
    { DDSKTX__DDS_ASTC10x8,              DDSKTX_FORMAT_ASTC10x8,  false },
    { DDSKTX__DDS_ASTC10x10,             DDSKTX_FORMAT_ASTC10x10,  false },
    { DDSKTX__DDS_ASTC12x10,             DDSKTX_FORMAT_ASTC12x10,  false },
    { DDSKTX__DDS_ASTC12x12,             DDSKTX_FORMAT_ASTC12x12,  false },
    { DDSKTX__DDS_A16B16G16R16,          DDSKTX_FORMAT_RGBA16,  false },
    { DDSKTX__DDS_A16B16G16R16F,         DDSKTX_FORMAT_RGBA16F, false },
    { DDSKTX__DDPF_RGB|DDSKTX__DDPF_ALPHAPIXELS, DDSKTX_FORMAT_BGRA8,   false },
    { DDSKTX__DDPF_INDEXED,              DDSKTX_FORMAT_R8,      false },
    { DDSKTX__DDPF_LUMINANCE,            DDSKTX_FORMAT_R8,      false },
    { DDSKTX__DDPF_ALPHA,                DDSKTX_FORMAT_R8,      false },
    { DDSKTX__DDS_R16F,                  DDSKTX_FORMAT_R16F,    false },
    { DDSKTX__DDS_R32F,                  DDSKTX_FORMAT_R32F,    false },
    { DDSKTX__DDS_A8L8,                  DDSKTX_FORMAT_RG8,     false },
    { DDSKTX__DDS_G16R16,                DDSKTX_FORMAT_RG16,    false },
    { DDSKTX__DDS_G16R16F,               DDSKTX_FORMAT_RG16F,   false },
    { DDSKTX__DDS_R8G8B8,                DDSKTX_FORMAT_RGB8,    false },
    { DDSKTX__DDS_A8R8G8B8,              DDSKTX_FORMAT_BGRA8,   false },
    { DDSKTX__DDS_A16B16G16R16,          DDSKTX_FORMAT_RGBA16,  false },
    { DDSKTX__DDS_A16B16G16R16F,         DDSKTX_FORMAT_RGBA16F, false },
    { DDSKTX__DDS_A2B10G10R10,           DDSKTX_FORMAT_RGB10A2, false },
};

static const ddsktx__dds_translate_fourcc_format k__translate_dxgi[] = {
    { DDSKTX__DDS_FORMAT_BC1_UNORM,           DDSKTX_FORMAT_BC1,        false },
    { DDSKTX__DDS_FORMAT_BC1_UNORM_SRGB,      DDSKTX_FORMAT_BC1,        true  },
    { DDSKTX__DDS_FORMAT_BC2_UNORM,           DDSKTX_FORMAT_BC2,        false },
    { DDSKTX__DDS_FORMAT_BC2_UNORM_SRGB,      DDSKTX_FORMAT_BC2,        true  },
    { DDSKTX__DDS_FORMAT_BC3_UNORM,           DDSKTX_FORMAT_BC3,        false },
    { DDSKTX__DDS_FORMAT_BC3_UNORM_SRGB,      DDSKTX_FORMAT_BC3,        true  },
    { DDSKTX__DDS_FORMAT_BC4_UNORM,           DDSKTX_FORMAT_BC4,        false },
    { DDSKTX__DDS_FORMAT_BC5_UNORM,           DDSKTX_FORMAT_BC5,        false },
    { DDSKTX__DDS_FORMAT_BC6H_SF16,           DDSKTX_FORMAT_BC6H,       false },
    { DDSKTX__DDS_FORMAT_BC7_UNORM,           DDSKTX_FORMAT_BC7,        false },
    { DDSKTX__DDS_FORMAT_BC7_UNORM_SRGB,      DDSKTX_FORMAT_BC7,        true  },

    { DDSKTX__DDS_FORMAT_R8_UNORM,            DDSKTX_FORMAT_R8,         false },
    { DDSKTX__DDS_FORMAT_R16_UNORM,           DDSKTX_FORMAT_R16,        false },
    { DDSKTX__DDS_FORMAT_R16_FLOAT,           DDSKTX_FORMAT_R16F,       false },
    { DDSKTX__DDS_FORMAT_R32_FLOAT,           DDSKTX_FORMAT_R32F,       false },
    { DDSKTX__DDS_FORMAT_R8G8_UNORM,          DDSKTX_FORMAT_RG8,        false },
    { DDSKTX__DDS_FORMAT_R16G16_UNORM,        DDSKTX_FORMAT_RG16,       false },
    { DDSKTX__DDS_FORMAT_R16G16_FLOAT,        DDSKTX_FORMAT_RG16F,      false },
    { DDSKTX__DDS_FORMAT_B8G8R8A8_UNORM,      DDSKTX_FORMAT_BGRA8,      false },
    { DDSKTX__DDS_FORMAT_B8G8R8A8_UNORM_SRGB, DDSKTX_FORMAT_BGRA8,      true  },
    { DDSKTX__DDS_FORMAT_R8G8B8A8_UNORM,      DDSKTX_FORMAT_RGBA8,      false },
    { DDSKTX__DDS_FORMAT_R8G8B8A8_UNORM_SRGB, DDSKTX_FORMAT_RGBA8,      true  },
    { DDSKTX__DDS_FORMAT_R16G16B16A16_UNORM,  DDSKTX_FORMAT_RGBA16,     false },
    { DDSKTX__DDS_FORMAT_R16G16B16A16_FLOAT,  DDSKTX_FORMAT_RGBA16F,    false },
    { DDSKTX__DDS_FORMAT_R10G10B10A2_UNORM,   DDSKTX_FORMAT_RGB10A2,    false },
    { DDSKTX__DDS_FORMAT_R11G11B10_FLOAT,     DDSKTX_FORMAT_RG11B10F,   false },
};

static const ddsktx__dds_translate_pixel_format k__translate_dds_pixel[] = {
    {  8, DDSKTX__DDPF_LUMINANCE,            { 0x000000ff, 0x00000000, 0x00000000, 0x00000000 }, DDSKTX_FORMAT_R8      },
    { 16, DDSKTX__DDPF_BUMPDUDV,             { 0x000000ff, 0x0000ff00, 0x00000000, 0x00000000 }, DDSKTX_FORMAT_RG8S    },
    { 24, DDSKTX__DDPF_RGB,                  { 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }, DDSKTX_FORMAT_RGB8    },
    { 24, DDSKTX__DDPF_RGB,                  { 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 }, DDSKTX_FORMAT_RGB8    },
    { 32, DDSKTX__DDPF_RGB,                  { 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }, DDSKTX_FORMAT_BGRA8   },
    { 32, DDSKTX__DDPF_RGB|DDSKTX__DDPF_ALPHAPIXELS, { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }, DDSKTX_FORMAT_RGBA8   },
    { 32, DDSKTX__DDPF_BUMPDUDV,             { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }, DDSKTX_FORMAT_RGBA8S  },
    { 32, DDSKTX__DDPF_RGB,                  { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }, DDSKTX_FORMAT_BGRA8   },
    { 32, DDSKTX__DDPF_RGB|DDSKTX__DDPF_ALPHAPIXELS, { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }, DDSKTX_FORMAT_BGRA8   }, // D3DFMT_A8R8G8B8
    { 32, DDSKTX__DDPF_RGB|DDSKTX__DDPF_ALPHAPIXELS, { 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }, DDSKTX_FORMAT_BGRA8   }, // D3DFMT_X8R8G8B8
    { 32, DDSKTX__DDPF_RGB|DDSKTX__DDPF_ALPHAPIXELS, { 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000 }, DDSKTX_FORMAT_RGB10A2 },
    { 32, DDSKTX__DDPF_RGB,                  { 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }, DDSKTX_FORMAT_RG16    },
    { 32, DDSKTX__DDPF_BUMPDUDV,             { 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }, DDSKTX_FORMAT_RG16S   }
};

typedef enum ddsktx__encode_type
{
    DDSKTX__ENCODE_UNORM,
    DDSKTX__ENCODE_SNORM,
    DDSKTX__ENCODE_FLOAT,
    DDSKTX__ENCODE_INT,
    DDSKTX__ENCODE_UINT,
    DDSKTX__ENCODE_COUNT
} ddsktx__encode_type;

static const ddsktx__block_info k__block_info[] =
{
    //  +-------------------------------------------- bits per pixel
    //  |  +----------------------------------------- block width
    //  |  |  +-------------------------------------- block height
    //  |  |  |   +---------------------------------- block size
    //  |  |  |   |  +------------------------------- min blocks x
    //  |  |  |   |  |  +---------------------------- min blocks y
    //  |  |  |   |  |  |   +------------------------ depth bits
    //  |  |  |   |  |  |   |  +--------------------- stencil bits
    //  |  |  |   |  |  |   |  |   +---+---+---+----- r, g, b, a bits
    //  |  |  |   |  |  |   |  |   r   g   b   a  +-- encoding type
    //  |  |  |   |  |  |   |  |   |   |   |   |  |
    {   4, 4, 4,  8, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // BC1
    {   8, 4, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // BC2
    {   8, 4, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // BC3
    {   4, 4, 4,  8, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // BC4
    {   8, 4, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // BC5
    {   8, 4, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_FLOAT) }, // BC6H
    {   8, 4, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // BC7
    {   4, 4, 4,  8, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ETC1
    {   4, 4, 4,  8, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ETC2
    {   8, 4, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ETC2A
    {   4, 4, 4,  8, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ETC2A1
    {   2, 8, 4,  8, 2, 2,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // PTC12
    {   4, 4, 4,  8, 2, 2,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // PTC14
    {   2, 8, 4,  8, 2, 2,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // PTC12A
    {   4, 4, 4,  8, 2, 2,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // PTC14A
    {   2, 8, 4,  8, 2, 2,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // PTC22
    {   4, 4, 4,  8, 2, 2,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // PTC24
    {   4, 4, 4,  8, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ATC
    {   8, 4, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ATCE
    {   8, 4, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ATCI
    {   8, 4, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC4x4
    {   6, 5, 4, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC5x4
    {   5, 5, 5, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC5x5
    {   4, 6, 5, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC6x5
    {   4, 6, 6, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC6x6
    {   3, 8, 5, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC8x5
    {   3, 8, 6, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC8x6
    {   3, 10,5, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC10x5
    {   2, 10,6, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC10x6
    {   2, 8, 8, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC8x8
    {   2, 10,8, 16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC10x8
    {   1, 10,10,16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC10x10
    {   1, 12,10,16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC12x10
    {   1, 12,12,16, 1, 1,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // ASTC12x12
    {   0, 0, 0,  0, 0, 0,  0, 0,  0,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_COUNT) }, // Unknown
    {   8, 1, 1,  1, 1, 1,  0, 0,  0,  0,  0,  8, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // A8
    {   8, 1, 1,  1, 1, 1,  0, 0,  8,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // R8
    {  32, 1, 1,  4, 1, 1,  0, 0,  8,  8,  8,  8, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // RGBA8
    {  32, 1, 1,  4, 1, 1,  0, 0,  8,  8,  8,  8, (uint8_t)(DDSKTX__ENCODE_SNORM) }, // RGBA8S
    {  32, 1, 1,  4, 1, 1,  0, 0, 16, 16,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // RG16
    {  24, 1, 1,  3, 1, 1,  0, 0,  8,  8,  8,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // RGB8
    {  16, 1, 1,  2, 1, 1,  0, 0, 16,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // R16
    {  32, 1, 1,  4, 1, 1,  0, 0, 32,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_FLOAT) }, // R32F
    {  16, 1, 1,  2, 1, 1,  0, 0, 16,  0,  0,  0, (uint8_t)(DDSKTX__ENCODE_FLOAT) }, // R16F
    {  32, 1, 1,  4, 1, 1,  0, 0, 16, 16,  0,  0, (uint8_t)(DDSKTX__ENCODE_FLOAT) }, // RG16F
    {  32, 1, 1,  4, 1, 1,  0, 0, 16, 16,  0,  0, (uint8_t)(DDSKTX__ENCODE_SNORM) }, // RG16S
    {  64, 1, 1,  8, 1, 1,  0, 0, 16, 16, 16, 16, (uint8_t)(DDSKTX__ENCODE_FLOAT) }, // RGBA16F
    {  64, 1, 1,  8, 1, 1,  0, 0, 16, 16, 16, 16, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // RGBA16
    {  32, 1, 1,  4, 1, 1,  0, 0,  8,  8,  8,  8, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // BGRA8
    {  32, 1, 1,  4, 1, 1,  0, 0, 10, 10, 10,  2, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // RGB10A2
    {  32, 1, 1,  4, 1, 1,  0, 0, 11, 11, 10,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // RG11B10F
    {  16, 1, 1,  2, 1, 1,  0, 0,  8,  8,  0,  0, (uint8_t)(DDSKTX__ENCODE_UNORM) }, // RG8
    {  16, 1, 1,  2, 1, 1,  0, 0,  8,  8,  0,  0, (uint8_t)(DDSKTX__ENCODE_SNORM) }  // RG8S
};

// KTX: https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
#define DDSKTX__KTX_MAGIC       stc__makefourcc(0xAB, 'K', 'T', 'X')
#define DDSKTX__KTX_HEADER_SIZE 60     // actual header size is 64, but we read 4 bytes for the 'magic'

#define DDSKTX__KTX_ETC1_RGB8_OES                             0x8D64
#define DDSKTX__KTX_COMPRESSED_R11_EAC                        0x9270
#define DDSKTX__KTX_COMPRESSED_SIGNED_R11_EAC                 0x9271
#define DDSKTX__KTX_COMPRESSED_RG11_EAC                       0x9272
#define DDSKTX__KTX_COMPRESSED_SIGNED_RG11_EAC                0x9273
#define DDSKTX__KTX_COMPRESSED_RGB8_ETC2                      0x9274
#define DDSKTX__KTX_COMPRESSED_SRGB8_ETC2                     0x9275
#define DDSKTX__KTX_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2  0x9276
#define DDSKTX__KTX_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277
#define DDSKTX__KTX_COMPRESSED_RGBA8_ETC2_EAC                 0x9278
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC          0x9279
#define DDSKTX__KTX_COMPRESSED_RGB_PVRTC_4BPPV1_IMG           0x8C00
#define DDSKTX__KTX_COMPRESSED_RGB_PVRTC_2BPPV1_IMG           0x8C01
#define DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG          0x8C02
#define DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG          0x8C03
#define DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG          0x9137
#define DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG          0x9138
#define DDSKTX__KTX_COMPRESSED_RGB_S3TC_DXT1_EXT              0x83F0
#define DDSKTX__KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT             0x83F1
#define DDSKTX__KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT             0x83F2
#define DDSKTX__KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT             0x83F3
#define DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT       0x8C4D
#define DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT       0x8C4E
#define DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT       0x8C4F
#define DDSKTX__KTX_COMPRESSED_LUMINANCE_LATC1_EXT            0x8C70
#define DDSKTX__KTX_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT      0x8C72
#define DDSKTX__KTX_COMPRESSED_RGBA_BPTC_UNORM_ARB            0x8E8C
#define DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB      0x8E8D
#define DDSKTX__KTX_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB      0x8E8E
#define DDSKTX__KTX_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB    0x8E8F
#define DDSKTX__KTX_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT          0x8A54
#define DDSKTX__KTX_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT          0x8A55
#define DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT    0x8A56
#define DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT    0x8A57
#define DDSKTX__KTX_ATC_RGB_AMD                               0x8C92
#define DDSKTX__KTX_ATC_RGBA_EXPLICIT_ALPHA_AMD               0x8C93
#define DDSKTX__KTX_ATC_RGBA_INTERPOLATED_ALPHA_AMD           0x87EE
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_4x4_KHR                 0x93B0
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_5x4_KHR                 0x93B1
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_5x5_KHR                 0x93B2
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_6x5_KHR                 0x93B3
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_6x6_KHR                 0x93B4
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_8x5_KHR                 0x93B5
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_8x6_KHR                 0x93B6
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_8x8_KHR                 0x93B7
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x5_KHR                0x93B8
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x6_KHR                0x93B9
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x8_KHR                0x93BA
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x10_KHR               0x93BB
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_12x10_KHR               0x93BC
#define DDSKTX__KTX_COMPRESSED_RGBA_ASTC_12x12_KHR               0x93BD
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR         0x93D0
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR         0x93D1
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR         0x93D2
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR         0x93D3
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR         0x93D4
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR         0x93D5
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR         0x93D6
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR         0x93D7
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR        0x93D8
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR        0x93D9
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR        0x93DA
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR       0x93DB
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR       0x93DC
#define DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR       0x93DD

#define DDSKTX__KTX_A8                                        0x803C
#define DDSKTX__KTX_R8                                        0x8229
#define DDSKTX__KTX_R16                                       0x822A
#define DDSKTX__KTX_RG8                                       0x822B
#define DDSKTX__KTX_RG16                                      0x822C
#define DDSKTX__KTX_R16F                                      0x822D
#define DDSKTX__KTX_R32F                                      0x822E
#define DDSKTX__KTX_RG16F                                     0x822F
#define DDSKTX__KTX_RG32F                                     0x8230
#define DDSKTX__KTX_RGBA8                                     0x8058
#define DDSKTX__KTX_RGBA16                                    0x805B
#define DDSKTX__KTX_RGBA16F                                   0x881A
#define DDSKTX__KTX_R32UI                                     0x8236
#define DDSKTX__KTX_RG32UI                                    0x823C
#define DDSKTX__KTX_RGBA32UI                                  0x8D70
#define DDSKTX__KTX_RGBA32F                                   0x8814
#define DDSKTX__KTX_RGB565                                    0x8D62
#define DDSKTX__KTX_RGBA4                                     0x8056
#define DDSKTX__KTX_RGB5_A1                                   0x8057
#define DDSKTX__KTX_RGB10_A2                                  0x8059
#define DDSKTX__KTX_R8I                                       0x8231
#define DDSKTX__KTX_R8UI                                      0x8232
#define DDSKTX__KTX_R16I                                      0x8233
#define DDSKTX__KTX_R16UI                                     0x8234
#define DDSKTX__KTX_R32I                                      0x8235
#define DDSKTX__KTX_R32UI                                     0x8236
#define DDSKTX__KTX_RG8I                                      0x8237
#define DDSKTX__KTX_RG8UI                                     0x8238
#define DDSKTX__KTX_RG16I                                     0x8239
#define DDSKTX__KTX_RG16UI                                    0x823A
#define DDSKTX__KTX_RG32I                                     0x823B
#define DDSKTX__KTX_RG32UI                                    0x823C
#define DDSKTX__KTX_R8_SNORM                                  0x8F94
#define DDSKTX__KTX_RG8_SNORM                                 0x8F95
#define DDSKTX__KTX_RGB8_SNORM                                0x8F96
#define DDSKTX__KTX_RGBA8_SNORM                               0x8F97
#define DDSKTX__KTX_R16_SNORM                                 0x8F98
#define DDSKTX__KTX_RG16_SNORM                                0x8F99
#define DDSKTX__KTX_RGB16_SNORM                               0x8F9A
#define DDSKTX__KTX_RGBA16_SNORM                              0x8F9B
#define DDSKTX__KTX_SRGB8                                     0x8C41
#define DDSKTX__KTX_SRGB8_ALPHA8                              0x8C43
#define DDSKTX__KTX_RGBA32UI                                  0x8D70
#define DDSKTX__KTX_RGB32UI                                   0x8D71
#define DDSKTX__KTX_RGBA16UI                                  0x8D76
#define DDSKTX__KTX_RGB16UI                                   0x8D77
#define DDSKTX__KTX_RGBA8UI                                   0x8D7C
#define DDSKTX__KTX_RGB8UI                                    0x8D7D
#define DDSKTX__KTX_RGBA32I                                   0x8D82
#define DDSKTX__KTX_RGB32I                                    0x8D83
#define DDSKTX__KTX_RGBA16I                                   0x8D88
#define DDSKTX__KTX_RGB16I                                    0x8D89
#define DDSKTX__KTX_RGBA8I                                    0x8D8E
#define DDSKTX__KTX_RGB8                                      0x8051
#define DDSKTX__KTX_RGB8I                                     0x8D8F
#define DDSKTX__KTX_RGB9_E5                                   0x8C3D
#define DDSKTX__KTX_R11F_G11F_B10F                            0x8C3A

#define DDSKTX__KTX_ZERO                                      0
#define DDSKTX__KTX_RED                                       0x1903
#define DDSKTX__KTX_ALPHA                                     0x1906
#define DDSKTX__KTX_RGB                                       0x1907
#define DDSKTX__KTX_RGBA                                      0x1908
#define DDSKTX__KTX_BGRA                                      0x80E1
#define DDSKTX__KTX_RG                                        0x8227

#define DDSKTX__KTX_BYTE                                      0x1400
#define DDSKTX__KTX_UNSIGNED_BYTE                             0x1401
#define DDSKTX__KTX_SHORT                                     0x1402
#define DDSKTX__KTX_UNSIGNED_SHORT                            0x1403
#define DDSKTX__KTX_INT                                       0x1404
#define DDSKTX__KTX_UNSIGNED_INT                              0x1405
#define DDSKTX__KTX_FLOAT                                     0x1406
#define DDSKTX__KTX_HALF_FLOAT                                0x140B
#define DDSKTX__KTX_UNSIGNED_INT_5_9_9_9_REV                  0x8C3E
#define DDSKTX__KTX_UNSIGNED_SHORT_5_6_5                      0x8363
#define DDSKTX__KTX_UNSIGNED_SHORT_4_4_4_4                    0x8033
#define DDSKTX__KTX_UNSIGNED_SHORT_5_5_5_1                    0x8034
#define DDSKTX__KTX_UNSIGNED_INT_2_10_10_10_REV               0x8368
#define DDSKTX__KTX_UNSIGNED_INT_10F_11F_11F_REV              0x8C3B

typedef struct ddsktx__ktx_format_info
{
    uint32_t internal_fmt;
    uint32_t internal_fmt_srgb;
    uint32_t fmt;
    uint32_t type;    
} ddsktx__ktx_format_info;

typedef struct ddsktx__ktx_format_info2
{
    uint32_t       internal_fmt;
    ddsktx_format  format;    
} ddsktx__ktx_format_info2;

static const ddsktx__ktx_format_info k__translate_ktx_fmt[] = {
        { DDSKTX__KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT,            DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,        DDSKTX__KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT,            DDSKTX__KTX_ZERO,                         }, // BC1
        { DDSKTX__KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT,            DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,        DDSKTX__KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT,            DDSKTX__KTX_ZERO,                         }, // BC2
        { DDSKTX__KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT,            DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,        DDSKTX__KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT,            DDSKTX__KTX_ZERO,                         }, // BC3
        { DDSKTX__KTX_COMPRESSED_LUMINANCE_LATC1_EXT,           DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_COMPRESSED_LUMINANCE_LATC1_EXT,           DDSKTX__KTX_ZERO,                         }, // BC4
        { DDSKTX__KTX_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT,     DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT,     DDSKTX__KTX_ZERO,                         }, // BC5
        { DDSKTX__KTX_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB,     DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB,     DDSKTX__KTX_ZERO,                         }, // BC6H
        { DDSKTX__KTX_COMPRESSED_RGBA_BPTC_UNORM_ARB,           DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_COMPRESSED_RGBA_BPTC_UNORM_ARB,           DDSKTX__KTX_ZERO,                         }, // BC7
        { DDSKTX__KTX_ETC1_RGB8_OES,                            DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_ETC1_RGB8_OES,                            DDSKTX__KTX_ZERO,                         }, // ETC1
        { DDSKTX__KTX_COMPRESSED_RGB8_ETC2,                     DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_COMPRESSED_RGB8_ETC2,                     DDSKTX__KTX_ZERO,                         }, // ETC2
        { DDSKTX__KTX_COMPRESSED_RGBA8_ETC2_EAC,                DDSKTX__KTX_COMPRESSED_SRGB8_ETC2,                      DDSKTX__KTX_COMPRESSED_RGBA8_ETC2_EAC,                DDSKTX__KTX_ZERO,                         }, // ETC2A
        { DDSKTX__KTX_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, DDSKTX__KTX_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,  DDSKTX__KTX_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, DDSKTX__KTX_ZERO,                         }, // ETC2A1
        { DDSKTX__KTX_COMPRESSED_RGB_PVRTC_2BPPV1_IMG,          DDSKTX__KTX_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT,           DDSKTX__KTX_COMPRESSED_RGB_PVRTC_2BPPV1_IMG,          DDSKTX__KTX_ZERO,                         }, // PTC12
        { DDSKTX__KTX_COMPRESSED_RGB_PVRTC_4BPPV1_IMG,          DDSKTX__KTX_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT,           DDSKTX__KTX_COMPRESSED_RGB_PVRTC_4BPPV1_IMG,          DDSKTX__KTX_ZERO,                         }, // PTC14
        { DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG,         DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT,     DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG,         DDSKTX__KTX_ZERO,                         }, // PTC12A
        { DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG,         DDSKTX__KTX_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT,     DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG,         DDSKTX__KTX_ZERO,                         }, // PTC14A
        { DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG,         DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG,         DDSKTX__KTX_ZERO,                         }, // PTC22
        { DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG,         DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG,         DDSKTX__KTX_ZERO,                         }, // PTC24
        { DDSKTX__KTX_ATC_RGB_AMD,                              DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_ATC_RGB_AMD,                              DDSKTX__KTX_ZERO,                         }, // ATC
        { DDSKTX__KTX_ATC_RGBA_EXPLICIT_ALPHA_AMD,              DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_ATC_RGBA_EXPLICIT_ALPHA_AMD,              DDSKTX__KTX_ZERO,                         }, // ATCE
        { DDSKTX__KTX_ATC_RGBA_INTERPOLATED_ALPHA_AMD,          DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_ATC_RGBA_INTERPOLATED_ALPHA_AMD,          DDSKTX__KTX_ZERO,                         }, // ATCI
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_4x4_KHR,             DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR,       DDSKTX__KTX_COMPRESSED_RGBA_ASTC_4x4_KHR,             DDSKTX__KTX_ZERO,                         }, // ASTC4x4
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_5x4_KHR,             DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR,       DDSKTX__KTX_COMPRESSED_RGBA_ASTC_5x4_KHR,             DDSKTX__KTX_ZERO,                         }, // ASTC5x4
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_5x5_KHR,             DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR,       DDSKTX__KTX_COMPRESSED_RGBA_ASTC_5x5_KHR,             DDSKTX__KTX_ZERO,                         }, // ASTC5x5
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_6x5_KHR,             DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR,       DDSKTX__KTX_COMPRESSED_RGBA_ASTC_6x5_KHR,             DDSKTX__KTX_ZERO,                         }, // ASTC6x5
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_6x6_KHR,             DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR,       DDSKTX__KTX_COMPRESSED_RGBA_ASTC_6x6_KHR,             DDSKTX__KTX_ZERO,                         }, // ASTC6x6
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_8x5_KHR,             DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR,       DDSKTX__KTX_COMPRESSED_RGBA_ASTC_8x5_KHR,             DDSKTX__KTX_ZERO,                         }, // ASTC8x5
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_8x6_KHR,             DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR,       DDSKTX__KTX_COMPRESSED_RGBA_ASTC_8x6_KHR,             DDSKTX__KTX_ZERO,                         }, // ASTC8x6
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x5_KHR,            DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR,      DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x5_KHR,            DDSKTX__KTX_ZERO,                         }, // ASTC10x5
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x6_KHR,            DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR,      DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x6_KHR,            DDSKTX__KTX_ZERO,                         }, // ASTC10x6
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_8x8_KHR,             DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR,       DDSKTX__KTX_COMPRESSED_RGBA_ASTC_8x8_KHR,             DDSKTX__KTX_ZERO,                         }, // ASTC8x8
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x8_KHR,            DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR,      DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x8_KHR,            DDSKTX__KTX_ZERO,                         }, // ASTC10x8
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x10_KHR,           DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR,     DDSKTX__KTX_COMPRESSED_RGBA_ASTC_10x10_KHR,           DDSKTX__KTX_ZERO,                         }, // ASTC10x10
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_12x10_KHR,           DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR,     DDSKTX__KTX_COMPRESSED_RGBA_ASTC_12x10_KHR,           DDSKTX__KTX_ZERO,                         }, // ASTC12x10
        { DDSKTX__KTX_COMPRESSED_RGBA_ASTC_12x12_KHR,           DDSKTX__KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR,     DDSKTX__KTX_COMPRESSED_RGBA_ASTC_12x12_KHR,           DDSKTX__KTX_ZERO,                         }, // ASTC12x12
        { DDSKTX__KTX_ZERO,                                     DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_ZERO,                                     DDSKTX__KTX_ZERO,                         }, // Unknown
        { DDSKTX__KTX_ALPHA,                                    DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_ALPHA,                                    DDSKTX__KTX_UNSIGNED_BYTE,                }, // A8
        { DDSKTX__KTX_R8,                                       DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RED,                                      DDSKTX__KTX_UNSIGNED_BYTE,                }, // R8
        { DDSKTX__KTX_RGBA8,                                    DDSKTX__KTX_SRGB8_ALPHA8,                               DDSKTX__KTX_RGBA,                                     DDSKTX__KTX_UNSIGNED_BYTE,                }, // RGBA8
        { DDSKTX__KTX_RGBA8_SNORM,                              DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RGBA,                                     DDSKTX__KTX_BYTE,                         }, // RGBA8S
        { DDSKTX__KTX_RG16,                                     DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RG,                                       DDSKTX__KTX_UNSIGNED_SHORT,               }, // RG16
        { DDSKTX__KTX_RGB8,                                     DDSKTX__KTX_SRGB8,                                      DDSKTX__KTX_RGB,                                      DDSKTX__KTX_UNSIGNED_BYTE,                }, // RGB8
        { DDSKTX__KTX_R16,                                      DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RED,                                      DDSKTX__KTX_UNSIGNED_SHORT,               }, // R16
        { DDSKTX__KTX_R32F,                                     DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RED,                                      DDSKTX__KTX_FLOAT,                        }, // R32F
        { DDSKTX__KTX_R16F,                                     DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RED,                                      DDSKTX__KTX_HALF_FLOAT,                   }, // R16F
        { DDSKTX__KTX_RG16F,                                    DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RG,                                       DDSKTX__KTX_FLOAT,                        }, // RG16F
        { DDSKTX__KTX_RG16_SNORM,                               DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RG,                                       DDSKTX__KTX_SHORT,                        }, // RG16S
        { DDSKTX__KTX_RGBA16F,                                  DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RGBA,                                     DDSKTX__KTX_HALF_FLOAT,                   }, // RGBA16F
        { DDSKTX__KTX_RGBA16,                                   DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RGBA,                                     DDSKTX__KTX_UNSIGNED_SHORT,               }, // RGBA16
        { DDSKTX__KTX_BGRA,                                     DDSKTX__KTX_SRGB8_ALPHA8,                               DDSKTX__KTX_BGRA,                                     DDSKTX__KTX_UNSIGNED_BYTE,                }, // BGRA8
        { DDSKTX__KTX_RGB10_A2,                                 DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RGBA,                                     DDSKTX__KTX_UNSIGNED_INT_2_10_10_10_REV,  }, // RGB10A2
        { DDSKTX__KTX_R11F_G11F_B10F,                           DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RGB,                                      DDSKTX__KTX_UNSIGNED_INT_10F_11F_11F_REV, }, // RG11B10F
        { DDSKTX__KTX_RG8,                                      DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RG,                                       DDSKTX__KTX_UNSIGNED_BYTE,                }, // RG8
        { DDSKTX__KTX_RG8_SNORM,                                DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RG,                                       DDSKTX__KTX_BYTE,                         }, // RG8S
        { DDSKTX__KTX_R16I,                                     DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RED,                                      DDSKTX__KTX_UNSIGNED_SHORT,               }, // R16I
        { DDSKTX__KTX_R16UI,                                    DDSKTX__KTX_ZERO,                                       DDSKTX__KTX_RED,                                      DDSKTX__KTX_UNSIGNED_SHORT,               }, // R16UI
};

static const ddsktx__ktx_format_info2 k__translate_ktx_fmt2[] =
{
    { DDSKTX__KTX_A8,                           DDSKTX_FORMAT_A8    },
    { DDSKTX__KTX_RED,                          DDSKTX_FORMAT_R8    },
    { DDSKTX__KTX_RGB,                          DDSKTX_FORMAT_RGB8  },
    { DDSKTX__KTX_RGBA,                         DDSKTX_FORMAT_RGBA8 },
    { DDSKTX__KTX_COMPRESSED_RGB_S3TC_DXT1_EXT, DDSKTX_FORMAT_BC1   },
};

typedef struct ddsktx__format_info
{
    const char* name;
    bool        has_alpha;
} ddsktx__format_info;

static const ddsktx__format_info k__formats_info[] = {
    {"BC1", false},
    {"BC2", true},
    {"BC3", true},
    {"BC4", false},
    {"BC5", false},
    {"BC6H", false},
    {"BC7", true},
    {"ETC1", false},
    {"ETC2", false},
    {"ETC2A", true},
    {"ETC2A1", true},
    {"PTC12", false},
    {"PTC14", false},
    {"PTC12A", true},
    {"PTC14A", true},
    {"PTC22", true},
    {"PTC24", true},
    {"ATC", false},
    {"ATCE", false},
    {"ATCI", false},
    {"ASTC4x4", true},
    {"ASTC5x4", true},
    {"ASTC5x5", true},
    {"ASTC6x5", true},
    {"ASTC6x6", true},
    {"ASTC8x5", true},
    {"ASTC8x6", true},
    {"ASTC10x5", true},
    {"ASTC10x6", true},
    {"ASTC8x8", true},
    {"ASTC10x8", true},
    {"ASTC10x10", true},
    {"ASTC12x10", true},
    {"ASTC12x12", true},
    {"<unknown>", false},
    {"A8", true},
    {"R8", false},
    {"RGBA8", true},
    {"RGBA8S", true},
    {"RG16", false},
    {"RGB8", false},
    {"R16", false},
    {"R32F", false},
    {"R16F", false},
    {"RG16F", false},
    {"RG16S", false},
    {"RGBA16F", true},
    {"RGBA16",true},
    {"BGRA8", true},
    {"RGB10A2", true},
    {"RG11B10F", false},
    {"RG8", false},
    {"RG8S", false}
};


static inline int ddsktx__read(ddsktx__mem_reader* reader, void* buff, int size)
{
    int read_bytes = (reader->offset + size) <= reader->total ? size : (reader->total - reader->offset);
    ddsktx_memcpy(buff, reader->buff + reader->offset, read_bytes);
    reader->offset += read_bytes;
    return read_bytes;
}

static bool ddsktx__parse_ktx(ddsktx_texture_info* tc, const void* file_data, int size, ddsktx_error* err)
{
    static const uint8_t ktx__id[] = { 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };

    ddsktx_memset(tc, 0x0, sizeof(ddsktx_texture_info));

    ddsktx__mem_reader r = {(const uint8_t*)file_data, size, sizeof(uint32_t)};
    ddsktx__ktx_header header;
    if (ddsktx__read(&r, &header, sizeof(header)) != DDSKTX__KTX_HEADER_SIZE) {
        ddsktx__err(err, "ktx; header size does not match");
    }

    if (ddsktx_memcmp(header.id, ktx__id, sizeof(header.id)) != 0) {
        ddsktx__err(err, "ktx: invalid file header");
    }

    // TODO: support big endian
    if (header.endianess != 0x04030201) {
        ddsktx__err(err, "ktx: big-endian format is not supported");
    }

    tc->metadata_offset = r.offset;
    tc->metadata_size = (int)header.metadata_size;
    r.offset += (int)header.metadata_size;

    ddsktx_format format = _DDSKTX_FORMAT_COUNT;

    int count = sizeof(k__translate_ktx_fmt)/sizeof(ddsktx__ktx_format_info);
    for (int i = 0; i < count; i++) {
        if (k__translate_ktx_fmt[i].internal_fmt == header.internal_format) {
            format = (ddsktx_format)i;
            break;
        }
    }

    if (format == _DDSKTX_FORMAT_COUNT) {
        count = sizeof(k__translate_ktx_fmt2)/sizeof(ddsktx__ktx_format_info2);
        for (int i = 0; i < count; i++) {
            if (k__translate_ktx_fmt2[i].internal_fmt == header.internal_format) {
                format = (ddsktx_format)k__translate_ktx_fmt2[i].format;
                break;
            }
        }
    }

    if (format == _DDSKTX_FORMAT_COUNT) {
        ddsktx__err(err, "ktx: unsupported format");
    } 

    if (header.face_count > 1 && header.face_count != DDSKTX_CUBE_FACE_COUNT) {
        ddsktx__err(err, "ktx: incomplete cubemap");
    }

    tc->data_offset = r.offset;
    tc->size_bytes = r.total - r.offset;
    tc->format = format;
    tc->width = (int)header.width;
    tc->height = (int)header.height;
    tc->depth = ddsktx__max((int)header.depth, 1);
    tc->num_layers = ddsktx__max((int)header.array_count, 1);
    tc->num_mips = ddsktx__max((int)header.mip_count, 1);
    tc->bpp = k__block_info[format].bpp;

    if (header.face_count == 6)
        tc->flags |= DDSKTX_TEXTURE_FLAG_CUBEMAP;
    tc->flags |= k__formats_info[format].has_alpha ? DDSKTX_TEXTURE_FLAG_ALPHA : 0;
    tc->flags |= DDSKTX_TEXTURE_FLAG_KTX;

    return true;
}

static bool ddsktx__parse_dds(ddsktx_texture_info* tc, const void* file_data, int size, ddsktx_error* err)
{
    ddsktx__mem_reader r = {(const uint8_t*)file_data, size, sizeof(uint32_t)};
    ddsktx__dds_header header;
    if (ddsktx__read(&r, &header, sizeof(header)) < DDSKTX__DDS_HEADER_SIZE ||
        header.size != DDSKTX__DDS_HEADER_SIZE)
    {
        ddsktx__err(err, "dds: header size does not match");
    }

    uint32_t required_flags = (DDSKTX__DDSD_HEIGHT|DDSKTX__DDSD_WIDTH);
    if ((header.flags & required_flags) != required_flags) {
        ddsktx__err(err, "dds: have invalid flags");
    }

    if (header.pixel_format.size != sizeof(ddsktx__dds_pixel_format)) {
        ddsktx__err(err, "dds: pixel format header is invalid");
    }

    uint32_t dxgi_format = 0;
    uint32_t array_size = 1;
    if (DDSKTX__DDPF_FOURCC == (header.flags & DDSKTX__DDPF_FOURCC) && 
        header.pixel_format.fourcc == DDSKTX__DDS_DX10)
    {
        ddsktx__dds_header_dxgi dxgi_header;
        ddsktx__read(&r, &dxgi_header, sizeof(dxgi_header));
        dxgi_format = dxgi_header.dxgi_format;
        array_size = dxgi_header.array_size;
    }

    if ((header.caps1 & DDSKTX__DDSCAPS_TEXTURE) == 0) {
        ddsktx__err(err, "dds: unsupported caps");
    }

    bool cubemap = (header.caps2 & DDSKTX__DDSCAPS2_CUBEMAP) != 0;
    if (cubemap && (header.caps2 & DDSKTX__DDSCAPS2_CUBEMAP_ALLSIDES) != DDSKTX__DDSCAPS2_CUBEMAP_ALLSIDES) {
        ddsktx__err(err, "dds: incomplete cubemap");
    }
    bool volume = (header.caps2 & DDSKTX__DDSCAPS2_VOLUME) != 0;

    ddsktx_format format = _DDSKTX_FORMAT_COUNT;
    bool has_alpha = (header.pixel_format.flags & DDSKTX__DDPF_ALPHA) != 0;
    bool srgb = false;

    if (dxgi_format == 0) {
        if ((header.pixel_format.flags & DDSKTX__DDPF_FOURCC) == DDSKTX__DDPF_FOURCC) {
            int count = sizeof(k__translate_dds_fourcc)/sizeof(ddsktx__dds_translate_fourcc_format);
            for (int i = 0; i < count; i++) {
                if (k__translate_dds_fourcc[i].dds_format == header.pixel_format.fourcc) {
                    format = k__translate_dds_fourcc[i].format;
                    break;
                }
            }
        } else {
            int count = sizeof(k__translate_dds_pixel)/sizeof(ddsktx__dds_translate_pixel_format);
            for (int i = 0; i < count; i++) {
                const ddsktx__dds_translate_pixel_format* f = &k__translate_dds_pixel[i];
                if (f->bit_count == header.pixel_format.rgb_bit_count &&
                    f->flags == header.pixel_format.flags &&
                    f->bit_mask[0] == header.pixel_format.bit_mask[0] &&
                    f->bit_mask[1] == header.pixel_format.bit_mask[1] &&
                    f->bit_mask[2] == header.pixel_format.bit_mask[2] &&
                    f->bit_mask[3] == header.pixel_format.bit_mask[3])
                {
                    format = f->format;
                    break;
                }
            }
        }
    } else {
        int count = sizeof(k__translate_dxgi)/sizeof(ddsktx__dds_translate_fourcc_format);
        for (int i = 0; i < count; i++) {
            if (k__translate_dxgi[i].dds_format == dxgi_format) {
                format = k__translate_dxgi[i].format;
                srgb = k__translate_dxgi[i].srgb;
                break;
            }
        }
    }

    if (format == _DDSKTX_FORMAT_COUNT) {
        ddsktx__err(err, "dds: unknown format");
    }

    ddsktx_memset(tc, 0x0, sizeof(ddsktx_texture_info));
    tc->data_offset = r.offset;
    tc->size_bytes = r.total - r.offset;
    tc->format = format;
    tc->width = (int)header.width;
    tc->height = (int)header.height;
    tc->depth = ddsktx__max(1, (int)header.depth);
    tc->num_layers = ddsktx__max(1, (int)array_size);
    tc->num_mips = (header.caps1 & DDSKTX__DDSCAPS_MIPMAP) ? (int)header.mip_count : 1;
    tc->bpp = k__block_info[format].bpp;
    if (has_alpha || k__formats_info[format].has_alpha)
        tc->flags |= DDSKTX_TEXTURE_FLAG_ALPHA;
    if (cubemap)
        tc->flags |= DDSKTX_TEXTURE_FLAG_CUBEMAP;
    if (volume)
        tc->flags |= DDSKTX_TEXTURE_FLAG_VOLUME;
    if (srgb) 
        tc->flags |= DDSKTX_TEXTURE_FLAG_SRGB;
    tc->flags |= DDSKTX_TEXTURE_FLAG_DDS;

    return true;
}   

void ddsktx_get_sub(const ddsktx_texture_info* tc, ddsktx_sub_data* sub_data, 
                 const void* file_data, int size,
                 int array_idx, int slice_face_idx, int mip_idx)
{
    ddsktx_assert(tc);
    ddsktx_assert(sub_data);
    ddsktx_assert(file_data);
    ddsktx_assert(size > 0);
    ddsktx_assert(array_idx < tc->num_layers);
    ddsktx_assert(!((tc->flags&DDSKTX_TEXTURE_FLAG_CUBEMAP) && (slice_face_idx >= DDSKTX_CUBE_FACE_COUNT)) && "invalid cube-face index");
    ddsktx_assert(!(!(tc->flags&DDSKTX_TEXTURE_FLAG_CUBEMAP) && (slice_face_idx >= tc->depth)) && "invalid depth-slice index");
    ddsktx_assert(mip_idx < tc->num_mips);

    ddsktx__mem_reader r = { (uint8_t*)file_data, size, tc->data_offset };
    ddsktx_format format = tc->format;

    ddsktx_assert(format < _DDSKTX_FORMAT_COUNT && format != _DDSKTX_FORMAT_COMPRESSED);
    const ddsktx__block_info* binfo = &k__block_info[format];
    const int bpp          = binfo->bpp;
    const int block_width  = binfo->block_width;
    const int block_height = binfo->block_height;
    const int block_size   = binfo->block_size;
    const int min_block_x  = binfo->min_block_x;
    const int min_block_y  = binfo->min_block_y;

    int num_faces;

    ddsktx_assert(!((tc->flags & DDSKTX_TEXTURE_FLAG_CUBEMAP) && tc->depth > 1) && "textures must be either Cube or 3D");
    int slice_idx, face_idx, num_slices;
    if (tc->flags & DDSKTX_TEXTURE_FLAG_CUBEMAP) {
        slice_idx = 0;
        face_idx = slice_face_idx;
        num_faces = DDSKTX_CUBE_FACE_COUNT;
        num_slices = 1;
    } else {
        slice_idx = slice_face_idx;
        face_idx = 0;
        num_faces = 1;
        num_slices = tc->depth;
    }    

    if (tc->flags & DDSKTX_TEXTURE_FLAG_DDS) {
        for (int layer = 0, num_layers = tc->num_layers; layer < num_layers; layer++) {
            for (int face = 0; face < num_faces; face++) {
                int width = tc->width;
                int height = tc->height;

                for (int mip = 0, mip_count = tc->num_mips; mip < mip_count; mip++) {
                    int row_bytes, mip_size;
                    
                    if (format < _DDSKTX_FORMAT_COMPRESSED) {
                        int num_blocks_wide = width > 0 ? ddsktx__max(1, (width + (block_width-1)) / block_width) : 0;
                        num_blocks_wide = ddsktx__max(min_block_x, num_blocks_wide);

                        int num_blocks_high = height > 0 ? ddsktx__max(1, (height + (block_height-1)) / block_height) : 0;
                        num_blocks_high = ddsktx__max(min_block_y, num_blocks_high);

                        row_bytes = num_blocks_wide * block_size;
                        mip_size = row_bytes * num_blocks_high;
                    } else {
                        row_bytes = (width*bpp + 7)/8;  // round to nearest byte
                        mip_size = row_bytes * height;
                    }
                   
                    for (int slice = 0; slice < num_slices; slice++) {
                        if (layer == array_idx && mip == mip_idx && 
                            slice == slice_idx && face_idx == face) 
                        {
                            sub_data->buff = r.buff + r.offset;
                            sub_data->width = width;
                            sub_data->height = height;
                            sub_data->size_bytes = mip_size;
                            sub_data->row_pitch_bytes = row_bytes;
                            return;
                        }

                        r.offset += mip_size;
                        ddsktx_assert(r.offset <= r.total && "texture buffer overflow");
                    } // foreach slice

                    width >>= 1;
                    height >>= 1;

                    if (width == 0) {
                        width = 1;
                    }
                    if (height == 0) {
                        height = 1;
                    }
                }   // foreach mip
            }   // foreach face
        } // foreach array-item
    } else if (tc->flags & DDSKTX_TEXTURE_FLAG_KTX) {
        int width = tc->width;
        int height = tc->height;

        for (int mip = 0, c = tc->num_mips; mip < c; mip++) {
            int row_bytes, mip_size;

            if (format < _DDSKTX_FORMAT_COMPRESSED) {
                int num_blocks_wide = width > 0 ? ddsktx__max(1, (width + (block_width-1)) / block_width) : 0;
                num_blocks_wide = ddsktx__max(min_block_x, num_blocks_wide);

                int num_blocks_high = height > 0 ? ddsktx__max(1, (height + (block_height-1)) / block_height) : 0;
                num_blocks_high = ddsktx__max(min_block_y, num_blocks_high);

                row_bytes = num_blocks_wide * block_size;
                mip_size = row_bytes * num_blocks_high;
            }
            else {
                row_bytes = (width*bpp + 7)/8;  // round to nearest byte
                mip_size = row_bytes * height;
            }

            int image_size;
            ddsktx__read(&r, &image_size, sizeof(image_size)); 
            ddsktx_assert(image_size == (mip_size*num_faces*num_slices) && "image size mismatch");

            for (int layer = 0, num_layers = tc->num_layers; layer < num_layers; layer++) {
                for (int face = 0; face < num_faces; face++) {
                    for (int slice = 0; slice < num_slices; slice++) {
                        if (layer == array_idx && mip == mip_idx &&
                            slice == slice_idx && face_idx == face) 
                        {
                            sub_data->buff = r.buff + r.offset;
                            sub_data->width = width;
                            sub_data->height = height;
                            sub_data->size_bytes = mip_size;
                            sub_data->row_pitch_bytes = row_bytes;
                            return;
                        }

                        r.offset += mip_size;
                        ddsktx_assert(r.offset <= r.total && "texture buffer overflow");
                    }   // foreach slice

                    
                    r.offset = ddsktx__align_mask(r.offset, 3); // cube-padding
                }   // foreach face
            }   // foreach array-item

            width >>= 1;
            height >>= 1;

            if (width == 0) {
                width = 1;
            }
            if (height == 0) {
                height = 1;
            }
            
            r.offset = ddsktx__align_mask(r.offset, 3); // mip-padding
        }   // foreach mip     
    } else {
        ddsktx_assert(0 && "invalid file format");
    }
}

bool ddsktx_parse(ddsktx_texture_info* tc, const void* file_data, int size, ddsktx_error* err)
{
    ddsktx_assert(tc);
    ddsktx_assert(file_data);
    ddsktx_assert(size > 0);

    ddsktx__mem_reader r = {(const uint8_t*)file_data, size, 0};
    
    // Read file flag and determine the file type
    uint32_t file_flag = 0;
    if (ddsktx__read(&r, &file_flag, sizeof(file_flag)) != sizeof(file_flag)) {
        ddsktx__err(err, "invalid texture file");
    }

    switch (file_flag) {
    case DDSKTX__DDS_MAGIC:
        return ddsktx__parse_dds(tc, file_data, size, err);
    case DDSKTX__KTX_MAGIC:
        return ddsktx__parse_ktx(tc, file_data, size, err);
    default:
        ddsktx__err(err, "unknown texture format");
    }
}

const char* ddsktx_format_str(ddsktx_format format)
{
    return k__formats_info[format].name;
}

bool ddsktx_format_compressed(ddsktx_format format)
{
    ddsktx_assert(format != _DDSKTX_FORMAT_COMPRESSED && format != _DDSKTX_FORMAT_COUNT);
    return format < _DDSKTX_FORMAT_COMPRESSED;
}

#endif  // DDSKTX_IMPLEMENT