//--------------------------------------------------------------------------------------
// File: Texdiag.cpp
//
// DirectX Texture diagnostic tool
//
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// http://go.microsoft.com/fwlink/?LinkId=248926
//--------------------------------------------------------------------------------------

#pragma warning(push)
#pragma warning(disable : 4005)
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define NODRAWTEXT
#define NOGDI
#define NOMCX
#define NOSERVICE
#define NOHELP
#pragma warning(pop)

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cwchar>
#include <cwctype>
#include <fstream>
#include <iterator>
#include <list>
#include <locale>
#include <memory>
#include <set>
#include <string>
#include <new>
#include <vector>

#include <dxgiformat.h>

#pragma warning(disable : 4619 4616 26812)

#include "DirectXTex.h"

#include <DirectXPackedVector.h>

//Uncomment to add support for OpenEXR (.exr)
//#define USE_OPENEXR

#ifdef USE_OPENEXR
// See <https://github.com/Microsoft/DirectXTex/wiki/Adding-OpenEXR> for details
#include "DirectXTexEXR.h"
#endif

using namespace DirectX;

enum COMMANDS
{
    CMD_INFO = 1,
    CMD_ANALYZE,
    CMD_COMPARE,
    CMD_DIFF,
    CMD_DUMPBC,
    CMD_DUMPDDS,
    CMD_MAX
};

enum OPTIONS : uint32_t
{
    OPT_RECURSIVE = 1,
    OPT_FORMAT,
    OPT_FILTER,
    OPT_DDS_DWORD_ALIGN,
    OPT_DDS_BAD_DXTN_TAILS,
    OPT_OUTPUTFILE,
    OPT_TOLOWER,
    OPT_OVERWRITE,
    OPT_FILETYPE,
    OPT_NOLOGO,
    OPT_TYPELESS_UNORM,
    OPT_TYPELESS_FLOAT,
    OPT_EXPAND_LUMINANCE,
    OPT_TARGET_PIXELX,
    OPT_TARGET_PIXELY,
    OPT_DIFF_COLOR,
    OPT_THRESHOLD,
    OPT_FILELIST,
    OPT_MAX
};

static_assert(OPT_MAX <= 32, "dwOptions is a unsigned int bitfield");

struct SConversion
{
    wchar_t szSrc[MAX_PATH];
};

struct SValue
{
    const wchar_t*  name;
    uint32_t        value;
};

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

const SValue g_pCommands[] =
{
    { L"info",      CMD_INFO },
    { L"analyze",   CMD_ANALYZE },
    { L"compare",   CMD_COMPARE },
    { L"diff",      CMD_DIFF },
    { L"dumpbc",    CMD_DUMPBC },
    { L"dumpdds",   CMD_DUMPDDS },
    { nullptr,      0 }
};

const SValue g_pOptions[] =
{
    { L"r",         OPT_RECURSIVE },
    { L"f",         OPT_FORMAT },
    { L"if",        OPT_FILTER },
    { L"dword",     OPT_DDS_DWORD_ALIGN },
    { L"badtails",  OPT_DDS_BAD_DXTN_TAILS },
    { L"nologo",    OPT_NOLOGO },
    { L"o",         OPT_OUTPUTFILE },
    { L"l",         OPT_TOLOWER },
    { L"y",         OPT_OVERWRITE },
    { L"ft",        OPT_FILETYPE },
    { L"tu",        OPT_TYPELESS_UNORM },
    { L"tf",        OPT_TYPELESS_FLOAT },
    { L"xlum",      OPT_EXPAND_LUMINANCE },
    { L"targetx",   OPT_TARGET_PIXELX },
    { L"targety",   OPT_TARGET_PIXELY },
    { L"c",         OPT_DIFF_COLOR },
    { L"t",         OPT_THRESHOLD },
    { L"flist",     OPT_FILELIST },
    { nullptr,      0 }
};

#define DEFFMT(fmt) { L## #fmt, DXGI_FORMAT_ ## fmt }

const SValue g_pFormats[] =
{
    // List does not include _TYPELESS, depth/stencil, or BC formats
    DEFFMT(R32G32B32A32_FLOAT),
    DEFFMT(R32G32B32A32_UINT),
    DEFFMT(R32G32B32A32_SINT),
    DEFFMT(R32G32B32_FLOAT),
    DEFFMT(R32G32B32_UINT),
    DEFFMT(R32G32B32_SINT),
    DEFFMT(R16G16B16A16_FLOAT),
    DEFFMT(R16G16B16A16_UNORM),
    DEFFMT(R16G16B16A16_UINT),
    DEFFMT(R16G16B16A16_SNORM),
    DEFFMT(R16G16B16A16_SINT),
    DEFFMT(R32G32_FLOAT),
    DEFFMT(R32G32_UINT),
    DEFFMT(R32G32_SINT),
    DEFFMT(R10G10B10A2_UNORM),
    DEFFMT(R10G10B10A2_UINT),
    DEFFMT(R11G11B10_FLOAT),
    DEFFMT(R8G8B8A8_UNORM),
    DEFFMT(R8G8B8A8_UNORM_SRGB),
    DEFFMT(R8G8B8A8_UINT),
    DEFFMT(R8G8B8A8_SNORM),
    DEFFMT(R8G8B8A8_SINT),
    DEFFMT(R16G16_FLOAT),
    DEFFMT(R16G16_UNORM),
    DEFFMT(R16G16_UINT),
    DEFFMT(R16G16_SNORM),
    DEFFMT(R16G16_SINT),
    DEFFMT(R32_FLOAT),
    DEFFMT(R32_UINT),
    DEFFMT(R32_SINT),
    DEFFMT(R8G8_UNORM),
    DEFFMT(R8G8_UINT),
    DEFFMT(R8G8_SNORM),
    DEFFMT(R8G8_SINT),
    DEFFMT(R16_FLOAT),
    DEFFMT(R16_UNORM),
    DEFFMT(R16_UINT),
    DEFFMT(R16_SNORM),
    DEFFMT(R16_SINT),
    DEFFMT(R8_UNORM),
    DEFFMT(R8_UINT),
    DEFFMT(R8_SNORM),
    DEFFMT(R8_SINT),
    DEFFMT(A8_UNORM),
    DEFFMT(R9G9B9E5_SHAREDEXP),
    DEFFMT(R8G8_B8G8_UNORM),
    DEFFMT(G8R8_G8B8_UNORM),
    DEFFMT(B5G6R5_UNORM),
    DEFFMT(B5G5R5A1_UNORM),

    // DXGI 1.1 formats
    DEFFMT(B8G8R8A8_UNORM),
    DEFFMT(B8G8R8X8_UNORM),
    DEFFMT(R10G10B10_XR_BIAS_A2_UNORM),
    DEFFMT(B8G8R8A8_UNORM_SRGB),
    DEFFMT(B8G8R8X8_UNORM_SRGB),

    // DXGI 1.2 formats
    DEFFMT(AYUV),
    DEFFMT(Y410),
    DEFFMT(Y416),
    DEFFMT(YUY2),
    DEFFMT(Y210),
    DEFFMT(Y216),
    DEFFMT(B4G4R4A4_UNORM),

    { nullptr, DXGI_FORMAT_UNKNOWN }
};

const SValue g_pFormatAliases[] =
{
    { L"RGBA", DXGI_FORMAT_R8G8B8A8_UNORM },
    { L"BGRA", DXGI_FORMAT_B8G8R8A8_UNORM },
    { L"BGR",  DXGI_FORMAT_B8G8R8X8_UNORM },

    { L"FP16", DXGI_FORMAT_R16G16B16A16_FLOAT },
    { L"FP32", DXGI_FORMAT_R32G32B32A32_FLOAT },

    { nullptr, DXGI_FORMAT_UNKNOWN }
};

const SValue g_pReadOnlyFormats[] =
{
    DEFFMT(R32G32B32A32_TYPELESS),
    DEFFMT(R32G32B32_TYPELESS),
    DEFFMT(R16G16B16A16_TYPELESS),
    DEFFMT(R32G32_TYPELESS),
    DEFFMT(R32G8X24_TYPELESS),
    DEFFMT(D32_FLOAT_S8X24_UINT),
    DEFFMT(R32_FLOAT_X8X24_TYPELESS),
    DEFFMT(X32_TYPELESS_G8X24_UINT),
    DEFFMT(R10G10B10A2_TYPELESS),
    DEFFMT(R8G8B8A8_TYPELESS),
    DEFFMT(R16G16_TYPELESS),
    DEFFMT(R32_TYPELESS),
    DEFFMT(D32_FLOAT),
    DEFFMT(R24G8_TYPELESS),
    DEFFMT(D24_UNORM_S8_UINT),
    DEFFMT(R24_UNORM_X8_TYPELESS),
    DEFFMT(X24_TYPELESS_G8_UINT),
    DEFFMT(R8G8_TYPELESS),
    DEFFMT(R16_TYPELESS),
    DEFFMT(R8_TYPELESS),
    DEFFMT(BC1_TYPELESS),
    DEFFMT(BC1_UNORM),
    DEFFMT(BC1_UNORM_SRGB),
    DEFFMT(BC2_TYPELESS),
    DEFFMT(BC2_UNORM),
    DEFFMT(BC2_UNORM_SRGB),
    DEFFMT(BC3_TYPELESS),
    DEFFMT(BC3_UNORM),
    DEFFMT(BC3_UNORM_SRGB),
    DEFFMT(BC4_TYPELESS),
    DEFFMT(BC4_UNORM),
    DEFFMT(BC4_SNORM),
    DEFFMT(BC5_TYPELESS),
    DEFFMT(BC5_UNORM),
    DEFFMT(BC5_SNORM),

    // DXGI 1.1 formats
    DEFFMT(B8G8R8A8_TYPELESS),
    DEFFMT(B8G8R8X8_TYPELESS),
    DEFFMT(BC6H_TYPELESS),
    DEFFMT(BC6H_UF16),
    DEFFMT(BC6H_SF16),
    DEFFMT(BC7_TYPELESS),
    DEFFMT(BC7_UNORM),
    DEFFMT(BC7_UNORM_SRGB),

    // DXGI 1.2 formats
    DEFFMT(AI44),
    DEFFMT(IA44),
    DEFFMT(P8),
    DEFFMT(A8P8),
    DEFFMT(NV12),
    DEFFMT(P010),
    DEFFMT(P016),
    DEFFMT(420_OPAQUE),
    DEFFMT(NV11),

    // DXGI 1.3 formats
    { L"P208", DXGI_FORMAT(130) },
    { L"V208", DXGI_FORMAT(131) },
    { L"V408", DXGI_FORMAT(132) },

    // Xbox-specific formats
    { L"R10G10B10_7E3_A2_FLOAT (Xbox)", DXGI_FORMAT(116) },
    { L"R10G10B10_6E4_A2_FLOAT (Xbox)", DXGI_FORMAT(117) },
    { L"D16_UNORM_S8_UINT (Xbox)", DXGI_FORMAT(118) },
    { L"R16_UNORM_X8_TYPELESS (Xbox)", DXGI_FORMAT(119) },
    { L"X16_TYPELESS_G8_UINT (Xbox)", DXGI_FORMAT(120) },
    { L"R10G10B10_SNORM_A2_UNORM (Xbox)", DXGI_FORMAT(189) },
    { L"R4G4_UNORM (Xbox)", DXGI_FORMAT(190) },

    { nullptr, DXGI_FORMAT_UNKNOWN }
};

const SValue g_pFilters[] =
{
    { L"POINT",                     TEX_FILTER_POINT },
    { L"LINEAR",                    TEX_FILTER_LINEAR },
    { L"CUBIC",                     TEX_FILTER_CUBIC },
    { L"FANT",                      TEX_FILTER_FANT },
    { L"BOX",                       TEX_FILTER_BOX },
    { L"TRIANGLE",                  TEX_FILTER_TRIANGLE },
    { L"POINT_DITHER",              TEX_FILTER_POINT | TEX_FILTER_DITHER },
    { L"LINEAR_DITHER",             TEX_FILTER_LINEAR | TEX_FILTER_DITHER },
    { L"CUBIC_DITHER",              TEX_FILTER_CUBIC | TEX_FILTER_DITHER },
    { L"FANT_DITHER",               TEX_FILTER_FANT | TEX_FILTER_DITHER },
    { L"BOX_DITHER",                TEX_FILTER_BOX | TEX_FILTER_DITHER },
    { L"TRIANGLE_DITHER",           TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER },
    { L"POINT_DITHER_DIFFUSION",    TEX_FILTER_POINT | TEX_FILTER_DITHER_DIFFUSION },
    { L"LINEAR_DITHER_DIFFUSION",   TEX_FILTER_LINEAR | TEX_FILTER_DITHER_DIFFUSION },
    { L"CUBIC_DITHER_DIFFUSION",    TEX_FILTER_CUBIC | TEX_FILTER_DITHER_DIFFUSION },
    { L"FANT_DITHER_DIFFUSION",     TEX_FILTER_FANT | TEX_FILTER_DITHER_DIFFUSION },
    { L"BOX_DITHER_DIFFUSION",      TEX_FILTER_BOX | TEX_FILTER_DITHER_DIFFUSION },
    { L"TRIANGLE_DITHER_DIFFUSION", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER_DIFFUSION },
    { nullptr,                      TEX_FILTER_DEFAULT }
};

#define CODEC_DDS 0xFFFF0001
#define CODEC_TGA 0xFFFF0002
#define CODEC_HDR 0xFFFF0005

#ifdef USE_OPENEXR
#define CODEC_EXR 0xFFFF0006
#endif

const SValue g_pDumpFileTypes[] =
{
    { L"BMP",   WIC_CODEC_BMP  },
    { L"JPG",   WIC_CODEC_JPEG },
    { L"JPEG",  WIC_CODEC_JPEG },
    { L"PNG",   WIC_CODEC_PNG  },
    { L"TGA",   CODEC_TGA      },
    { L"HDR",   CODEC_HDR      },
    { L"TIF",   WIC_CODEC_TIFF },
    { L"TIFF",  WIC_CODEC_TIFF },
    { L"JXR",   WIC_CODEC_WMP  },
#ifdef USE_OPENEXR
    { L"EXR",   CODEC_EXR      },
#endif
    { nullptr,  CODEC_DDS      }
};

const SValue g_pExtFileTypes[] =
{
    { L".BMP",  WIC_CODEC_BMP },
    { L".JPG",  WIC_CODEC_JPEG },
    { L".JPEG", WIC_CODEC_JPEG },
    { L".PNG",  WIC_CODEC_PNG },
    { L".DDS",  CODEC_DDS },
    { L".TGA",  CODEC_TGA },
    { L".HDR",  CODEC_HDR },
    { L".TIF",  WIC_CODEC_TIFF },
    { L".TIFF", WIC_CODEC_TIFF },
    { L".WDP",  WIC_CODEC_WMP },
    { L".HDP",  WIC_CODEC_WMP },
    { L".JXR",  WIC_CODEC_WMP },
#ifdef USE_OPENEXR
    { L"EXR",   CODEC_EXR },
#endif
    { nullptr,  CODEC_DDS }
};

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

namespace
{
    inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }

    struct find_closer { void operator()(HANDLE h) noexcept { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } };

    using ScopedFindHandle = std::unique_ptr<void, find_closer>;

#ifdef _PREFAST_
#pragma prefast(disable : 26018, "Only used with static internal arrays")
#endif

    uint32_t LookupByName(const wchar_t *pName, const SValue *pArray)
    {
        while (pArray->name)
        {
            if (!_wcsicmp(pName, pArray->name))
                return pArray->value;

            pArray++;
        }

        return 0;
    }

    const wchar_t* LookupByValue(uint32_t pValue, const SValue *pArray)
    {
        while (pArray->name)
        {
            if (pValue == pArray->value)
                return pArray->name;

            pArray++;
        }

        return L"";
    }

    void SearchForFiles(const wchar_t* path, std::list<SConversion>& files, bool recursive)
    {
        // Process files
        WIN32_FIND_DATAW findData = {};
        ScopedFindHandle hFile(safe_handle(FindFirstFileExW(path,
            FindExInfoBasic, &findData,
            FindExSearchNameMatch, nullptr,
            FIND_FIRST_EX_LARGE_FETCH)));
        if (hFile)
        {
            for (;;)
            {
                if (!(findData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY)))
                {
                    wchar_t drive[_MAX_DRIVE] = {};
                    wchar_t dir[_MAX_DIR] = {};
                    _wsplitpath_s(path, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0);

                    SConversion conv = {};
                    _wmakepath_s(conv.szSrc, drive, dir, findData.cFileName, nullptr);
                    files.push_back(conv);
                }

                if (!FindNextFileW(hFile.get(), &findData))
                    break;
            }
        }

        // Process directories
        if (recursive)
        {
            wchar_t searchDir[MAX_PATH] = {};
            {
                wchar_t drive[_MAX_DRIVE] = {};
                wchar_t dir[_MAX_DIR] = {};
                _wsplitpath_s(path, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0);
                _wmakepath_s(searchDir, drive, dir, L"*", nullptr);
            }

            hFile.reset(safe_handle(FindFirstFileExW(searchDir,
                FindExInfoBasic, &findData,
                FindExSearchLimitToDirectories, nullptr,
                FIND_FIRST_EX_LARGE_FETCH)));
            if (!hFile)
                return;

            for (;;)
            {
                if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                {
                    if (findData.cFileName[0] != L'.')
                    {
                        wchar_t subdir[MAX_PATH] = {};

                        {
                            wchar_t drive[_MAX_DRIVE] = {};
                            wchar_t dir[_MAX_DIR] = {};
                            wchar_t fname[_MAX_FNAME] = {};
                            wchar_t ext[_MAX_FNAME] = {};
                            _wsplitpath_s(path, drive, dir, fname, ext);
                            wcscat_s(dir, findData.cFileName);
                            _wmakepath_s(subdir, drive, dir, fname, ext);
                        }

                        SearchForFiles(subdir, files, recursive);
                    }
                }

                if (!FindNextFileW(hFile.get(), &findData))
                    break;
            }
        }
    }

    void ProcessFileList(std::wifstream& inFile, std::list<SConversion>& files)
    {
        std::list<SConversion> flist;
        std::set<std::wstring> excludes;
        wchar_t fname[1024] = {};
        for (;;)
        {
            inFile >> fname;
            if (!inFile)
                break;

            if (*fname == L'#')
            {
                // Comment
            }
            else if (*fname == L'-')
            {
                if (flist.empty())
                {
                    wprintf(L"WARNING: Ignoring the line '%ls' in -flist\n", fname);
                }
                else
                {
                    if (wcspbrk(fname, L"?*") != nullptr)
                    {
                        std::list<SConversion> removeFiles;
                        SearchForFiles(&fname[1], removeFiles, false);

                        for (auto it : removeFiles)
                        {
                            _wcslwr_s(it.szSrc);
                            excludes.insert(it.szSrc);
                        }
                    }
                    else
                    {
                        std::wstring name = (fname + 1);
                        std::transform(name.begin(), name.end(), name.begin(), towlower);
                        excludes.insert(name);
                    }
                }
            }
            else if (wcspbrk(fname, L"?*") != nullptr)
            {
                SearchForFiles(fname, flist, false);
            }
            else
            {
                SConversion conv = {};
                wcscpy_s(conv.szSrc, MAX_PATH, fname);
                flist.push_back(conv);
            }

            inFile.ignore(1000, '\n');
        }

        inFile.close();

        if (!excludes.empty())
        {
            // Remove any excluded files
            for (auto it = flist.begin(); it != flist.end();)
            {
                std::wstring name = it->szSrc;
                std::transform(name.begin(), name.end(), name.begin(), towlower);
                auto item = it;
                ++it;
                if (excludes.find(name) != excludes.end())
                {
                    flist.erase(item);
                }
            }
        }

        if (flist.empty())
        {
            wprintf(L"WARNING: No file names found in -flist\n");
        }
        else
        {
            files.splice(files.end(), flist);
        }
    }

    void PrintFormat(DXGI_FORMAT Format)
    {
        for (auto pFormat = g_pFormats; pFormat->name; pFormat++)
        {
            if (static_cast<DXGI_FORMAT>(pFormat->value) == Format)
            {
                wprintf(L"%ls", pFormat->name);
                return;
            }
        }

        for (auto pFormat = g_pReadOnlyFormats; pFormat->name; pFormat++)
        {
            if (static_cast<DXGI_FORMAT>(pFormat->value) == Format)
            {
                wprintf(L"%ls", pFormat->name);
                return;
            }
        }

        wprintf(L"*UNKNOWN*");
    }

    void PrintList(size_t cch, const SValue *pValue)
    {
        while (pValue->name)
        {
            size_t cchName = wcslen(pValue->name);

            if (cch + cchName + 2 >= 80)
            {
                wprintf(L"\n      ");
                cch = 6;
            }

            wprintf(L"%ls ", pValue->name);
            cch += cchName + 2;
            pValue++;
        }

        wprintf(L"\n");
    }

    void PrintLogo()
    {
        wchar_t version[32] = {};

        wchar_t appName[_MAX_PATH] = {};
        if (GetModuleFileNameW(nullptr, appName, static_cast<DWORD>(std::size(appName))))
        {
            DWORD size = GetFileVersionInfoSizeW(appName, nullptr);
            if (size > 0)
            {
                auto verInfo = std::make_unique<uint8_t[]>(size);
                if (GetFileVersionInfoW(appName, 0, size, verInfo.get()))
                {
                    LPVOID lpstr = nullptr;
                    UINT strLen = 0;
                    if (VerQueryValueW(verInfo.get(), L"\\StringFileInfo\\040904B0\\ProductVersion", &lpstr, &strLen))
                    {
                        wcsncpy_s(version, reinterpret_cast<const wchar_t*>(lpstr), strLen);
                    }
                }
            }
        }

        if (!*version || wcscmp(version, L"1.0.0.0") == 0)
        {
            swprintf_s(version, L"%03d (library)", DIRECTX_TEX_VERSION);
        }

        wprintf(L"Microsoft (R) DirectX Texture Diagnostic Tool [DirectXTex] Version %ls\n", version);
        wprintf(L"Copyright (C) Microsoft Corp.\n");
#ifdef _DEBUG
        wprintf(L"*** Debug build ***\n");
#endif
        wprintf(L"\n");
    }

    void PrintUsage()
    {
        PrintLogo();

        wprintf(L"Usage: texdiag <command> <options> <files>\n\n");
        wprintf(L"   info                Output image metadata\n");
        wprintf(L"   analyze             Analyze and summarize image information\n");
        wprintf(L"   compare             Compare two images with MSE error metric\n");
        wprintf(L"   diff                Generate difference image from two images\n");
        wprintf(L"   dumpbc              Dump out compressed blocks (DDS BC only)\n");
        wprintf(L"   dumpdds             Dump out all the images in a complex DDS\n\n");
        wprintf(L"   -r                  wildcard filename search is recursive\n");
        wprintf(L"   -if <filter>        image filtering\n");
        wprintf(L"\n                       (DDS input only)\n");
        wprintf(L"   -t{u|f}             TYPELESS format is treated as UNORM or FLOAT\n");
        wprintf(L"   -dword              Use DWORD instead of BYTE alignment\n");
        wprintf(L"   -badtails           Fix for older DXTn with bad mipchain tails\n");
        wprintf(L"   -xlum               expand legacy L8, L16, and A8P8 formats\n");
        wprintf(L"\n                       (diff only)\n");
        wprintf(L"   -f <format>         format\n");
        wprintf(L"   -o <filename>       output filename\n");
        wprintf(L"   -l                  force output filename to lower case\n");
        wprintf(L"   -y                  overwrite existing output file (if any)\n");
        wprintf(L"   -c <hex-RGB>        highlight difference color (defaults to off)\n");
        wprintf(L"   -t <threshold>      highlight threshold (defaults to 0.25)\n");
        wprintf(L"\n                       (dumpbc only)\n");
        wprintf(L"   -targetx <num>      dump pixels at location x (defaults to all)\n");
        wprintf(L"   -targety <num>      dump pixels at location y (defaults to all)\n");
        wprintf(L"\n                       (dumpdds only)\n");
        wprintf(L"   -ft <filetype>      output file type\n");
        wprintf(L"\n   -nologo             suppress copyright message\n");
        wprintf(L"   -flist <filename>   use text file with a list of input files (one per line)\n");

        wprintf(L"\n   <format>: ");
        PrintList(13, g_pFormats);
        wprintf(L"      ");
        PrintList(13, g_pFormatAliases);

        wprintf(L"\n   <filter>: ");
        PrintList(13, g_pFilters);

        wprintf(L"\n   <filetype>: ");
        PrintList(15, g_pDumpFileTypes);
    }

    const wchar_t* GetErrorDesc(HRESULT hr)
    {
        static wchar_t desc[1024] = {};

        LPWSTR errorText = nullptr;

        DWORD result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
            nullptr, static_cast<DWORD>(hr),
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&errorText), 0, nullptr);

        *desc = 0;

        if (result > 0 && errorText)
        {
            swprintf_s(desc, L": %ls", errorText);

            size_t len = wcslen(desc);
            if (len >= 2)
            {
                desc[len - 2] = 0;
                desc[len - 1] = 0;
            }

            if (errorText)
                LocalFree(errorText);
        }

        return desc;
    }

    HRESULT LoadImage(
        const wchar_t *fileName,
        uint32_t dwOptions,
        TEX_FILTER_FLAGS dwFilter,
        TexMetadata& info,
        std::unique_ptr<ScratchImage>& image)
    {
        if (!fileName)
            return E_INVALIDARG;

        image.reset(new (std::nothrow) ScratchImage);
        if (!image)
            return E_OUTOFMEMORY;

        wchar_t ext[_MAX_EXT] = {};
        _wsplitpath_s(fileName, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT);

        if (_wcsicmp(ext, L".dds") == 0)
        {
            DDS_FLAGS ddsFlags = DDS_FLAGS_ALLOW_LARGE_FILES;
            if (dwOptions & (1 << OPT_DDS_DWORD_ALIGN))
                ddsFlags |= DDS_FLAGS_LEGACY_DWORD;
            if (dwOptions & (1 << OPT_EXPAND_LUMINANCE))
                ddsFlags |= DDS_FLAGS_EXPAND_LUMINANCE;
            if (dwOptions & (1 << OPT_DDS_BAD_DXTN_TAILS))
                ddsFlags |= DDS_FLAGS_BAD_DXTN_TAILS;

            HRESULT hr = LoadFromDDSFile(fileName, ddsFlags, &info, *image);
            if (FAILED(hr))
                return hr;

            if (IsTypeless(info.format))
            {
                if (dwOptions & (1 << OPT_TYPELESS_UNORM))
                {
                    info.format = MakeTypelessUNORM(info.format);
                }
                else if (dwOptions & (1 << OPT_TYPELESS_FLOAT))
                {
                    info.format = MakeTypelessFLOAT(info.format);
                }

                if (IsTypeless(info.format))
                    return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);

                image->OverrideFormat(info.format);
            }

            return S_OK;
        }
        else if (_wcsicmp(ext, L".tga") == 0)
        {
            return LoadFromTGAFile(fileName, TGA_FLAGS_NONE, &info, *image);
        }
        else if (_wcsicmp(ext, L".hdr") == 0)
        {
            return LoadFromHDRFile(fileName, &info, *image);
        }
#ifdef USE_OPENEXR
        else if (_wcsicmp(ext, L".exr") == 0)
        {
            return LoadFromEXRFile(fileName, &info, *image);
        }
#endif
        else
        {
            // WIC shares the same filter values for mode and dither
            static_assert(static_cast<int>(WIC_FLAGS_DITHER) == static_cast<int>(TEX_FILTER_DITHER), "WIC_FLAGS_* & TEX_FILTER_* should match");
            static_assert(static_cast<int>(WIC_FLAGS_DITHER_DIFFUSION) == static_cast<int>(TEX_FILTER_DITHER_DIFFUSION), "WIC_FLAGS_* & TEX_FILTER_* should match");
            static_assert(static_cast<int>(WIC_FLAGS_FILTER_POINT) == static_cast<int>(TEX_FILTER_POINT), "WIC_FLAGS_* & TEX_FILTER_* should match");
            static_assert(static_cast<int>(WIC_FLAGS_FILTER_LINEAR) == static_cast<int>(TEX_FILTER_LINEAR), "WIC_FLAGS_* & TEX_FILTER_* should match");
            static_assert(static_cast<int>(WIC_FLAGS_FILTER_CUBIC) == static_cast<int>(TEX_FILTER_CUBIC), "WIC_FLAGS_* & TEX_FILTER_* should match");
            static_assert(static_cast<int>(WIC_FLAGS_FILTER_FANT) == static_cast<int>(TEX_FILTER_FANT), "WIC_FLAGS_* & TEX_FILTER_* should match");

            return LoadFromWICFile(fileName, dwFilter | WIC_FLAGS_ALL_FRAMES, &info, *image);
        }
    }

    HRESULT SaveImage(const Image* image, const wchar_t *fileName, uint32_t codec)
    {
        switch (codec)
        {
        case CODEC_DDS:
            return SaveToDDSFile(*image, DDS_FLAGS_NONE, fileName);

        case CODEC_TGA:
            return SaveToTGAFile(*image, TGA_FLAGS_NONE, fileName);

        case CODEC_HDR:
            return SaveToHDRFile(*image, fileName);

#ifdef USE_OPENEXR
        case CODEC_EXR:
            return SaveToEXRFile(*image, fileName);
#endif

        default:
            return SaveToWICFile(*image, WIC_FLAGS_NONE, GetWICCodec(static_cast<WICCodecs>(codec)), fileName);
        }
    }

    //--------------------------------------------------------------------------------------
    struct AnalyzeData
    {
        XMFLOAT4 imageMin;
        XMFLOAT4 imageMax;
        XMFLOAT4 imageAvg;
        XMFLOAT4 imageVariance;
        XMFLOAT4 imageStdDev;
        float luminance;
        size_t   specials_x;
        size_t   specials_y;
        size_t   specials_z;
        size_t   specials_w;

        void Print()
        {
            wprintf(L"\t  Minimum - (%f %f %f %f)\n", imageMin.x, imageMin.y, imageMin.z, imageMin.w);
            wprintf(L"\t  Average - (%f %f %f %f)\n", imageAvg.x, imageAvg.y, imageAvg.z, imageAvg.w);
            wprintf(L"\t  Maximum - (%f %f %f %f)\n", imageMax.x, imageMax.y, imageMax.z, imageMax.w);
            wprintf(L"\t Variance - (%f %f %f %f)\n", imageVariance.x, imageVariance.y, imageVariance.z, imageVariance.w);
            wprintf(L"\t  Std Dev - (%f %f %f %f)\n", imageStdDev.x, imageStdDev.y, imageStdDev.z, imageStdDev.w);

            wprintf(L"\tLuminance - %f (maximum)\n", luminance);

            if ((specials_x > 0) || (specials_y > 0) || (specials_z > 0) || (specials_w > 0))
            {
                wprintf(L"     FP specials - (%zu %zu %zu %zu)\n", specials_x, specials_y, specials_z, specials_w);
            }
        }
    };

    HRESULT Analyze(const Image& image, _Out_ AnalyzeData& result)
    {
        memset(&result, 0, sizeof(AnalyzeData));

        // First pass
        XMVECTOR minv = g_XMFltMax;
        XMVECTOR maxv = XMVectorNegate(g_XMFltMax);
        XMVECTOR acc = g_XMZero;
        XMVECTOR luminance = g_XMZero;

        size_t totalPixels = 0;

        HRESULT hr = EvaluateImage(image, [&](const XMVECTOR * pixels, size_t width, size_t y)
            {
                static const XMVECTORF32 s_luminance = { { {  0.3f, 0.59f, 0.11f, 0.f } } };

                UNREFERENCED_PARAMETER(y);

                for (size_t x = 0; x < width; ++x)
                {
                    XMVECTOR v = *pixels++;
                    luminance = XMVectorMax(luminance, XMVector3Dot(v, s_luminance));
                    minv = XMVectorMin(minv, v);
                    maxv = XMVectorMax(maxv, v);
                    acc = XMVectorAdd(v, acc);
                    ++totalPixels;

                    XMFLOAT4 f;
                    XMStoreFloat4(&f, v);
                    if (!isfinite(f.x))
                    {
                        ++result.specials_x;
                    }

                    if (!isfinite(f.y))
                    {
                        ++result.specials_y;
                    }

                    if (!isfinite(f.z))
                    {
                        ++result.specials_z;
                    }

                    if (!isfinite(f.w))
                    {
                        ++result.specials_w;
                    }
                }
            });
        if (FAILED(hr))
            return hr;

        if (!totalPixels)
            return S_FALSE;

        result.luminance = XMVectorGetX(luminance);
        XMStoreFloat4(&result.imageMin, minv);
        XMStoreFloat4(&result.imageMax, maxv);

        XMVECTOR pixelv = XMVectorReplicate(float(totalPixels));
        XMVECTOR avgv = XMVectorDivide(acc, pixelv);
        XMStoreFloat4(&result.imageAvg, avgv);

        // Second pass
        acc = g_XMZero;

        hr = EvaluateImage(image, [&](const XMVECTOR * pixels, size_t width, size_t y)
            {
                UNREFERENCED_PARAMETER(y);

                for (size_t x = 0; x < width; ++x)
                {
                    XMVECTOR v = *pixels++;

                    XMVECTOR diff = XMVectorSubtract(v, avgv);
                    acc = XMVectorMultiplyAdd(diff, diff, acc);
                }
            });
        if (FAILED(hr))
            return hr;

        XMStoreFloat4(&result.imageVariance, acc);

        XMVECTOR stddev = XMVectorSqrt(acc);

        XMStoreFloat4(&result.imageStdDev, stddev);

        return S_OK;
    }

    //--------------------------------------------------------------------------------------
    struct AnalyzeBCData
    {
        size_t blocks;
        size_t blockHist[15];

        void Print(DXGI_FORMAT fmt)
        {
            wprintf(L"\t        Compression - ");
            PrintFormat(fmt);
            wprintf(L"\n\t       Total blocks - %zu\n", blocks);

            switch (fmt)
            {
            case DXGI_FORMAT_BC1_UNORM:
            case DXGI_FORMAT_BC1_UNORM_SRGB:
                wprintf(L"\t     4 color blocks - %zu\n", blockHist[0]);
                wprintf(L"\t     3 color blocks - %zu\n", blockHist[1]);
                break;

                // BC2 only has a single 'type' of block

            case DXGI_FORMAT_BC3_UNORM:
            case DXGI_FORMAT_BC3_UNORM_SRGB:
                wprintf(L"\t     8 alpha blocks - %zu\n", blockHist[0]);
                wprintf(L"\t     6 alpha blocks - %zu\n", blockHist[1]);
                break;

            case DXGI_FORMAT_BC4_UNORM:
            case DXGI_FORMAT_BC4_SNORM:
                wprintf(L"\t     8 red blocks - %zu\n", blockHist[0]);
                wprintf(L"\t     6 red blocks - %zu\n", blockHist[1]);
                break;

            case DXGI_FORMAT_BC5_UNORM:
            case DXGI_FORMAT_BC5_SNORM:
                wprintf(L"\t     8 red blocks - %zu\n", blockHist[0]);
                wprintf(L"\t     6 red blocks - %zu\n", blockHist[1]);
                wprintf(L"\t   8 green blocks - %zu\n", blockHist[2]);
                wprintf(L"\t   6 green blocks - %zu\n", blockHist[3]);
                break;

            case DXGI_FORMAT_BC6H_UF16:
            case DXGI_FORMAT_BC6H_SF16:
                for (size_t j = 1; j <= 14; ++j)
                {
                    if (blockHist[j] > 0)
                        wprintf(L"\t     Mode %02zu blocks - %zu\n", j, blockHist[j]);
                }
                if (blockHist[0] > 0)
                    wprintf(L"\tReserved mode blcks - %zu\n", blockHist[0]);
                break;

            case DXGI_FORMAT_BC7_UNORM:
            case DXGI_FORMAT_BC7_UNORM_SRGB:
                for (size_t j = 0; j <= 7; ++j)
                {
                    if (blockHist[j] > 0)
                        wprintf(L"\t     Mode %02zu blocks - %zu\n", j, blockHist[j]);
                }
                if (blockHist[8] > 0)
                    wprintf(L"\tReserved mode blcks - %zu\n", blockHist[8]);
                break;

            default:
                break;
            }
        }
    };

#pragma pack(push,1)
    struct BC1Block
    {
        uint16_t    rgb[2]; // 565 colors
        uint32_t    bitmap; // 2bpp rgb bitmap
    };

    struct BC2Block
    {
        uint32_t    bitmap[2];  // 4bpp alpha bitmap
        BC1Block    bc1;        // BC1 rgb data
    };

    struct BC3Block
    {
        uint8_t     alpha[2];   // alpha values
        uint8_t     bitmap[6];  // 3bpp alpha bitmap
        BC1Block    bc1;        // BC1 rgb data
    };

    struct BC4UBlock
    {
        uint8_t red_0;
        uint8_t red_1;
        uint8_t indices[6];
    };

    struct BC4SBlock
    {
        int8_t red_0;
        int8_t red_1;
        uint8_t indices[6];
    };

    struct BC5UBlock
    {
        BC4UBlock u;
        BC4UBlock v;
    };

    struct BC5SBlock
    {
        BC4SBlock u;
        BC4SBlock v;
    };
#pragma pack(pop)

    HRESULT AnalyzeBC(const Image& image, _Out_ AnalyzeBCData& result)
    {
        memset(&result, 0, sizeof(AnalyzeBCData));

        size_t sbpp;
        switch (image.format)
        {
        case DXGI_FORMAT_BC1_UNORM:
        case DXGI_FORMAT_BC1_UNORM_SRGB:
        case DXGI_FORMAT_BC4_UNORM:
        case DXGI_FORMAT_BC4_SNORM:
            sbpp = 8;
            break;

        case DXGI_FORMAT_BC2_UNORM:
        case DXGI_FORMAT_BC2_UNORM_SRGB:
        case DXGI_FORMAT_BC3_UNORM:
        case DXGI_FORMAT_BC3_UNORM_SRGB:
        case DXGI_FORMAT_BC5_UNORM:
        case DXGI_FORMAT_BC5_SNORM:
        case DXGI_FORMAT_BC6H_UF16:
        case DXGI_FORMAT_BC6H_SF16:
        case DXGI_FORMAT_BC7_UNORM:
        case DXGI_FORMAT_BC7_UNORM_SRGB:
            sbpp = 16;
            break;

        default:
            return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
        }

        const uint8_t *pSrc = image.pixels;
        const size_t rowPitch = image.rowPitch;

        for (size_t h = 0; h < image.height; h += 4)
        {
            const uint8_t *sptr = pSrc;

            for (size_t count = 0; count < rowPitch; count += sbpp)
            {
                switch (image.format)
                {
                case DXGI_FORMAT_BC1_UNORM:
                case DXGI_FORMAT_BC1_UNORM_SRGB:
                {
                    auto block = reinterpret_cast<const BC1Block*>(sptr);

                    if (block->rgb[0] <= block->rgb[1])
                    {
                        // Transparent block
                        ++result.blockHist[1];
                    }
                    else
                    {
                        // Opaque block
                        ++result.blockHist[0];
                    }
                }
                break;

                // BC2 only has a single 'type' of block

                case DXGI_FORMAT_BC3_UNORM:
                case DXGI_FORMAT_BC3_UNORM_SRGB:
                {
                    auto block = reinterpret_cast<const BC3Block*>(sptr);

                    if (block->alpha[0] > block->alpha[1])
                    {
                        // 8 alpha block
                        ++result.blockHist[0];
                    }
                    else
                    {
                        // 6 alpha block
                        ++result.blockHist[1];
                    }
                }
                break;

                case DXGI_FORMAT_BC4_UNORM:
                {
                    auto block = reinterpret_cast<const BC4UBlock*>(sptr);

                    if (block->red_0 > block->red_1)
                    {
                        // 8 red block
                        ++result.blockHist[0];
                    }
                    else
                    {
                        // 6 red block
                        ++result.blockHist[1];
                    }
                }
                break;

                case DXGI_FORMAT_BC4_SNORM:
                {
                    auto block = reinterpret_cast<const BC4SBlock*>(sptr);

                    if (block->red_0 > block->red_1)
                    {
                        // 8 red block
                        ++result.blockHist[0];
                    }
                    else
                    {
                        // 6 red block
                        ++result.blockHist[1];
                    }
                }
                break;

                case DXGI_FORMAT_BC5_UNORM:
                {
                    auto block = reinterpret_cast<const BC5UBlock*>(sptr);

                    if (block->u.red_0 > block->u.red_1)
                    {
                        // 8 red block
                        ++result.blockHist[0];
                    }
                    else
                    {
                        // 6 red block
                        ++result.blockHist[1];
                    }

                    if (block->v.red_0 > block->v.red_1)
                    {
                        // 8 green block
                        ++result.blockHist[2];
                    }
                    else
                    {
                        // 6 green block
                        ++result.blockHist[3];
                    }
                }
                break;

                case DXGI_FORMAT_BC5_SNORM:
                {
                    auto block = reinterpret_cast<const BC5SBlock*>(sptr);

                    if (block->u.red_0 > block->u.red_1)
                    {
                        // 8 red block
                        ++result.blockHist[0];
                    }
                    else
                    {
                        // 6 red block
                        ++result.blockHist[1];
                    }

                    if (block->v.red_0 > block->v.red_1)
                    {
                        // 8 green block
                        ++result.blockHist[2];
                    }
                    else
                    {
                        // 6 green block
                        ++result.blockHist[3];
                    }
                }
                break;

                case DXGI_FORMAT_BC6H_UF16:
                case DXGI_FORMAT_BC6H_SF16:
                    switch (*sptr & 0x03)
                    {
                    case 0x00:
                        // Mode 1 (2 bits, 00)
                        ++result.blockHist[1];
                        break;

                    case 0x01:
                        // Mode 2 (2 bits, 01)
                        ++result.blockHist[2];
                        break;

                    default:
                        switch (*sptr & 0x1F)
                        {
                        case 0x02:
                            // Mode 3 (5 bits, 00010)
                            ++result.blockHist[3];
                            break;

                        case 0x06:
                            // Mode 4 (5 bits, 00110)
                            ++result.blockHist[4];
                            break;

                        case 0x0A:
                            // Mode 5 (5 bits, 01010)
                            ++result.blockHist[5];
                            break;

                        case 0x0E:
                            // Mode 6 (5 bits, 01110)
                            ++result.blockHist[6];
                            break;

                        case 0x12:
                            // Mode 7 (5 bits, 10010)
                            ++result.blockHist[7];
                            break;

                        case 0x16:
                            // Mode 8 (5 bits, 10110)
                            ++result.blockHist[8];
                            break;

                        case 0x1A:
                            // Mode 9 (5 bits, 11010)
                            ++result.blockHist[9];
                            break;

                        case 0x1E:
                            // Mode 10 (5 bits, 11110)
                            ++result.blockHist[10];
                            break;

                        case 0x03:
                            // Mode 11 (5 bits, 00011)
                            ++result.blockHist[11];
                            break;

                        case 0x07:
                            // Mode 12 (5 bits, 00111)
                            ++result.blockHist[12];
                            break;

                        case 0x0B:
                            // Mode 13 (5 bits, 01011)
                            ++result.blockHist[13];
                            break;

                        case 0x0F:
                            // Mode 14 (5 bits, 01111)
                            ++result.blockHist[14];
                            break;

                        case 0x13: // Reserved mode (5 bits, 10011)
                        case 0x17: // Reserved mode (5 bits, 10111)
                        case 0x1B: // Reserved mode (5 bits, 11011)
                        case 0x1F: // Reserved mode (5 bits, 11111)
                        default:
                            ++result.blockHist[0];
                            break;
                        }
                        break;
                    }
                    break;

                case DXGI_FORMAT_BC7_UNORM:
                case DXGI_FORMAT_BC7_UNORM_SRGB:
                    if (*sptr & 0x01)
                    {
                        // Mode 0 (1)
                        ++result.blockHist[0];
                    }
                    else if (*sptr & 0x02)
                    {
                        // Mode 1 (01)
                        ++result.blockHist[1];
                    }
                    else if (*sptr & 0x04)
                    {
                        // Mode 2 (001)
                        ++result.blockHist[2];
                    }
                    else if (*sptr & 0x08)
                    {
                        // Mode 3 (0001)
                        ++result.blockHist[3];
                    }
                    else if (*sptr & 0x10)
                    {
                        // Mode 4 (00001)
                        ++result.blockHist[4];
                    }
                    else if (*sptr & 0x20)
                    {
                        // Mode 5 (000001)
                        ++result.blockHist[5];
                    }
                    else if (*sptr & 0x40)
                    {
                        // Mode 6 (0000001)
                        ++result.blockHist[6];
                    }
                    else if (*sptr & 0x80)
                    {
                        // Mode 7 (00000001)
                        ++result.blockHist[7];
                    }
                    else
                    {
                        // Reserved mode 8 (00000000)
                        ++result.blockHist[8];
                    }
                    break;

                default:
                    break;
                }

                sptr += sbpp;
                ++result.blocks;
            }

            pSrc += rowPitch;
        }

        return S_OK;
    }

    //--------------------------------------------------------------------------------------
    HRESULT Difference(
        const Image& image1,
        const Image& image2,
        TEX_FILTER_FLAGS dwFilter,
        DXGI_FORMAT format,
        uint32_t diffColor,
        float threshold,
        ScratchImage& result)
    {
        if (!image1.pixels || !image2.pixels)
            return E_POINTER;

        if (image1.width != image2.width
            || image1.height != image2.height)
            return E_FAIL;

        ScratchImage tempA;
        const Image* imageA = &image1;
        if (IsCompressed(image1.format))
        {
            HRESULT hr = Decompress(image1, DXGI_FORMAT_R32G32B32A32_FLOAT, tempA);
            if (FAILED(hr))
                return hr;

            imageA = tempA.GetImage(0, 0, 0);
        }

        ScratchImage tempB;
        const Image* imageB = &image2;
        if (image2.format != DXGI_FORMAT_R32G32B32A32_FLOAT)
        {
            if (IsCompressed(image2.format))
            {
                HRESULT hr = Decompress(image2, DXGI_FORMAT_R32G32B32A32_FLOAT, tempB);
                if (FAILED(hr))
                    return hr;

                imageB = tempB.GetImage(0, 0, 0);
            }
            else
            {
                HRESULT hr = Convert(image2, DXGI_FORMAT_R32G32B32A32_FLOAT, dwFilter, TEX_THRESHOLD_DEFAULT, tempB);
                if (FAILED(hr))
                    return hr;

                imageB = tempB.GetImage(0, 0, 0);
            }
        }

        if (!imageA || !imageB)
            return E_POINTER;

        XMVECTOR colorValue = PackedVector::XMLoadColor(reinterpret_cast<const PackedVector::XMCOLOR*>(&diffColor));
        colorValue = XMVectorSelect(g_XMIdentityR3, colorValue, g_XMSelect1110);

        ScratchImage diffImage;
        HRESULT hr = TransformImage(*imageA, [&](XMVECTOR* outPixels, const XMVECTOR * inPixels, size_t width, size_t y)
            {
                XMVECTOR tolerance = XMVectorReplicate(threshold);

                auto *inPixelsB = reinterpret_cast<XMVECTOR*>(imageB->pixels + (y*imageB->rowPitch));

                for (size_t x = 0; x < width; ++x)
                {
                    XMVECTOR v1 = *inPixels++;
                    XMVECTOR v2 = *inPixelsB++;

                    v1 = XMVectorSubtract(v1, v2);
                    v1 = XMVectorAbs(v1);
                    v1 = XMVectorSelect(g_XMIdentityR3, v1, g_XMSelect1110);

                    if (diffColor && XMVector3GreaterOrEqual(v1, tolerance))
                    {
                        *outPixels++ = colorValue;
                    }
                    else
                    {
                        *outPixels++ = v1;
                    }
                }
            }, (format == DXGI_FORMAT_R32G32B32A32_FLOAT) ? result : diffImage);
        if (FAILED(hr))
            return hr;

        if (format == diffImage.GetMetadata().format)
        {
            std::swap(diffImage, result);
            return S_OK;
        }

        return Convert(diffImage.GetImages(), diffImage.GetImageCount(), diffImage.GetMetadata(), format, dwFilter, TEX_THRESHOLD_DEFAULT, result);
    }

    //--------------------------------------------------------------------------------------
    // Partition, Shape, Fixup
    const uint8_t g_aFixUp[3][64][3] =
    {
        {   // No fix-ups for 1st subset for BC6H or BC7
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },
            { 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 0, 0 }
        },

        {   // BC6H/BC7 Partition Set Fixups for 2 Subsets
            { 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
            { 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
            { 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
            { 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
            { 0,15, 0 },{ 0, 2, 0 },{ 0, 8, 0 },{ 0, 2, 0 },
            { 0, 2, 0 },{ 0, 8, 0 },{ 0, 8, 0 },{ 0,15, 0 },
            { 0, 2, 0 },{ 0, 8, 0 },{ 0, 2, 0 },{ 0, 2, 0 },
            { 0, 8, 0 },{ 0, 8, 0 },{ 0, 2, 0 },{ 0, 2, 0 },

            // BC7 Partition Set Fixups for 2 Subsets (second-half)
            { 0,15, 0 },{ 0,15, 0 },{ 0, 6, 0 },{ 0, 8, 0 },
            { 0, 2, 0 },{ 0, 8, 0 },{ 0,15, 0 },{ 0,15, 0 },
            { 0, 2, 0 },{ 0, 8, 0 },{ 0, 2, 0 },{ 0, 2, 0 },
            { 0, 2, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0, 6, 0 },
            { 0, 6, 0 },{ 0, 2, 0 },{ 0, 6, 0 },{ 0, 8, 0 },
            { 0,15, 0 },{ 0,15, 0 },{ 0, 2, 0 },{ 0, 2, 0 },
            { 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },{ 0,15, 0 },
            { 0,15, 0 },{ 0, 2, 0 },{ 0, 2, 0 },{ 0,15, 0 }
        },

        {   // BC7 Partition Set Fixups for 3 Subsets
            { 0, 3,15 },{ 0, 3, 8 },{ 0,15, 8 },{ 0,15, 3 },
            { 0, 8,15 },{ 0, 3,15 },{ 0,15, 3 },{ 0,15, 8 },
            { 0, 8,15 },{ 0, 8,15 },{ 0, 6,15 },{ 0, 6,15 },
            { 0, 6,15 },{ 0, 5,15 },{ 0, 3,15 },{ 0, 3, 8 },
            { 0, 3,15 },{ 0, 3, 8 },{ 0, 8,15 },{ 0,15, 3 },
            { 0, 3,15 },{ 0, 3, 8 },{ 0, 6,15 },{ 0,10, 8 },
            { 0, 5, 3 },{ 0, 8,15 },{ 0, 8, 6 },{ 0, 6,10 },
            { 0, 8,15 },{ 0, 5,15 },{ 0,15,10 },{ 0,15, 8 },
            { 0, 8,15 },{ 0,15, 3 },{ 0, 3,15 },{ 0, 5,10 },
            { 0, 6,10 },{ 0,10, 8 },{ 0, 8, 9 },{ 0,15,10 },
            { 0,15, 6 },{ 0, 3,15 },{ 0,15, 8 },{ 0, 5,15 },
            { 0,15, 3 },{ 0,15, 6 },{ 0,15, 6 },{ 0,15, 8 },
            { 0, 3,15 },{ 0,15, 3 },{ 0, 5,15 },{ 0, 5,15 },
            { 0, 5,15 },{ 0, 8,15 },{ 0, 5,15 },{ 0,10,15 },
            { 0, 5,15 },{ 0,10,15 },{ 0, 8,15 },{ 0,13,15 },
            { 0,15, 3 },{ 0,12,15 },{ 0, 3,15 },{ 0, 3, 8 }
        }
    };

    inline static bool IsFixUpOffset(
        _In_range_(0, 2) size_t uPartitions,
        _In_range_(0, 63) uint64_t uShape,
        _In_range_(0, 15) size_t uOffset)
    {
        for (size_t p = 0; p <= uPartitions; p++)
        {
            if (uOffset == g_aFixUp[uPartitions][uShape][p])
            {
                return true;
            }
        }
        return false;
    }

    //--------------------------------------------------------------------------------------
#define SIGN_EXTEND(x,nb) ((((x)&(1<<((nb)-1)))?((~0)^((1<<(nb))-1)):0)|(x))

#define NUM_PIXELS_PER_BLOCK 16

    void Print565(uint16_t rgb)
    {
        auto r = float(((rgb >> 11) & 31) * (1.0f / 31.0f));
        auto g = float(((rgb >> 5) & 63) * (1.0f / 63.0f));
        auto b = float(((rgb >> 0) & 31) * (1.0f / 31.0f));

        wprintf(L"(R: %.3f, G: %.3f, B: %.3f)", r, g, b);
    }

    void PrintIndex2bpp(uint32_t bitmap)
    {
        for (size_t j = 0; j < NUM_PIXELS_PER_BLOCK; ++j, bitmap >>= 2)
        {
            wprintf(L"%u%ls", bitmap & 0x3, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
        }
    }

    void PrintIndex2bpp(uint64_t bitmap, size_t parts, uint64_t shape)
    {
        for (size_t j = 0; j < NUM_PIXELS_PER_BLOCK; ++j)
        {
            if (IsFixUpOffset(parts, shape, j))
            {
                wprintf(L"%llu%ls", bitmap & 0x1, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
                bitmap >>= 1;
            }
            else
            {
                wprintf(L"%llu%ls", bitmap & 0x3, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
                bitmap >>= 2;
            }
        }
    }

    void PrintIndex3bpp(uint64_t bitmap, size_t parts, uint64_t shape)
    {
        for (size_t j = 0; j < NUM_PIXELS_PER_BLOCK; ++j)
        {
            if (IsFixUpOffset(parts, shape, j))
            {
                wprintf(L"%llu%ls", bitmap & 0x3, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
                bitmap >>= 2;
            }
            else
            {
                wprintf(L"%llu%ls", bitmap & 0x7, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
                bitmap >>= 3;
            }
        }
    }

    void PrintIndex4bpp(uint64_t bitmap, size_t parts, uint64_t shape)
    {
        for (size_t j = 0; j < NUM_PIXELS_PER_BLOCK; ++j)
        {
            if (IsFixUpOffset(parts, shape, j))
            {
                wprintf(L"%llX%ls", bitmap & 0x7, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
                bitmap >>= 3;
            }
            else
            {
                wprintf(L"%llX%ls", bitmap & 0xF, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
                bitmap >>= 4;
            }
        }
    }

    void PrintIndex3bpp(const uint8_t data[6])
    {
        uint32_t bitmap = uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16);

        size_t j = 0;
        for (; j < (NUM_PIXELS_PER_BLOCK / 2); ++j, bitmap >>= 3)
        {
            wprintf(L"%u%ls", bitmap & 0x7, ((j % 4) == 3) ? L" | " : L" ");
        }

        bitmap = uint32_t(data[3]) | (uint32_t(data[4]) << 8) | (uint32_t(data[5]) << 16);

        for (; j < NUM_PIXELS_PER_BLOCK; ++j, bitmap >>= 3)
        {
            wprintf(L"%u%ls", bitmap & 0x7, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
        }
    }

    const wchar_t* GetRotBits(uint64_t rot)
    {
        switch (rot)
        {
        case 1: return L" (R<->A)";
        case 2: return L" (G<->A)";
        case 3: return L" (B<->A)";
        default: return L"";
        }
    }

    HRESULT DumpBCImage(const Image& image, int pixelx, int pixely)
    {
        size_t sbpp;
        switch (image.format)
        {
        case DXGI_FORMAT_BC1_UNORM:
        case DXGI_FORMAT_BC1_UNORM_SRGB:
        case DXGI_FORMAT_BC4_UNORM:
        case DXGI_FORMAT_BC4_SNORM:
            sbpp = 8;
            break;

        case DXGI_FORMAT_BC2_UNORM:
        case DXGI_FORMAT_BC2_UNORM_SRGB:
        case DXGI_FORMAT_BC3_UNORM:
        case DXGI_FORMAT_BC3_UNORM_SRGB:
        case DXGI_FORMAT_BC5_UNORM:
        case DXGI_FORMAT_BC5_SNORM:
        case DXGI_FORMAT_BC6H_UF16:
        case DXGI_FORMAT_BC6H_SF16:
        case DXGI_FORMAT_BC7_UNORM:
        case DXGI_FORMAT_BC7_UNORM_SRGB:
            sbpp = 16;
            break;

        default:
            return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
        }

        const uint8_t *pSrc = image.pixels;
        const size_t rowPitch = image.rowPitch;

        size_t nblock = 0;
        for (size_t h = 0; h < image.height; h += 4, pSrc += rowPitch)
        {
            if (pixely >= 0)
            {
                if ((pixely < int(h)) || (pixely >= int(h + 4)))
                    continue;
            }

            const uint8_t *sptr = pSrc;

            size_t w = 0;
            for (size_t count = 0; count < rowPitch; count += sbpp, w += 4, ++nblock, sptr += sbpp)
            {
                if (pixelx >= 0)
                {
                    if ((pixelx < int(w)) || (pixelx >= int(w + 4)))
                        continue;
                }

                wprintf(L"   Block %zu (pixel: %zu x %zu)\n", nblock, w, h);
                switch (image.format)
                {
                case DXGI_FORMAT_BC1_UNORM:
                case DXGI_FORMAT_BC1_UNORM_SRGB:
                {
                    auto block = reinterpret_cast<const BC1Block*>(sptr);

                    if (block->rgb[0] <= block->rgb[1])
                    {
                        // Transparent block
                        wprintf(L"\tTransparent - E0: ");
                    }
                    else
                    {
                        // Opaque block
                        wprintf(L"\t     Opaque - E0: ");
                    }

                    Print565(block->rgb[0]);
                    wprintf(L"\n\t              E1: ");
                    Print565(block->rgb[1]);
                    wprintf(L"\n\t           Index: ");
                    PrintIndex2bpp(block->bitmap);
                    wprintf(L"\n");
                }
                break;

                case DXGI_FORMAT_BC2_UNORM:
                case DXGI_FORMAT_BC2_UNORM_SRGB:
                {
                    auto block = reinterpret_cast<const BC2Block*>(sptr);

                    wprintf(L"\tColor - E0: ");
                    Print565(block->bc1.rgb[0]);
                    wprintf(L"\n\t        E1: ");
                    Print565(block->bc1.rgb[1]);
                    wprintf(L"\n\t     Index: ");
                    PrintIndex2bpp(block->bc1.bitmap);
                    wprintf(L"\n");

                    wprintf(L"\tAlpha - ");

                    size_t j = 0;
                    uint32_t bitmap = block->bitmap[0];
                    for (; j < (NUM_PIXELS_PER_BLOCK / 2); ++j, bitmap >>= 4)
                    {
                        wprintf(L"%X%ls", bitmap & 0xF, ((j % 4) == 3) ? L" | " : L" ");
                    }

                    bitmap = block->bitmap[1];
                    for (; j < NUM_PIXELS_PER_BLOCK; ++j, bitmap >>= 4)
                    {
                        wprintf(L"%X%ls", bitmap & 0xF, ((j < (NUM_PIXELS_PER_BLOCK - 1)) && ((j % 4) == 3)) ? L" | " : L" ");
                    }

                    wprintf(L"\n");
                }
                break;

                case DXGI_FORMAT_BC3_UNORM:
                case DXGI_FORMAT_BC3_UNORM_SRGB:
                {
                    auto block = reinterpret_cast<const BC3Block*>(sptr);

                    wprintf(L"\tColor - E0: ");
                    Print565(block->bc1.rgb[0]);
                    wprintf(L"\n\t        E1: ");
                    Print565(block->bc1.rgb[1]);
                    wprintf(L"\n\t     Index: ");
                    PrintIndex2bpp(block->bc1.bitmap);
                    wprintf(L"\n");

                    wprintf(L"\tAlpha - E0: %0.3f  E1: %0.3f (%u)\n\t     Index: ",
                        (float(block->alpha[0]) / 255.f),
                        (float(block->alpha[1]) / 255.f), (block->alpha[0] > block->alpha[1]) ? 8u : 6u);

                    PrintIndex3bpp(block->bitmap);

                    wprintf(L"\n");
                }
                break;

                case DXGI_FORMAT_BC4_UNORM:
                {
                    auto block = reinterpret_cast<const BC4UBlock*>(sptr);

                    wprintf(L"\t   E0: %0.3f  E1: %0.3f (%u)\n\tIndex: ",
                        (float(block->red_0) / 255.f),
                        (float(block->red_1) / 255.f), (block->red_0 > block->red_1) ? 8u : 6u);

                    PrintIndex3bpp(block->indices);

                    wprintf(L"\n");
                }
                break;

                case DXGI_FORMAT_BC4_SNORM:
                {
                    auto block = reinterpret_cast<const BC4SBlock*>(sptr);

                    wprintf(L"\t   E0: %0.3f  E1: %0.3f (%u)\n\tIndex: ",
                        (float(block->red_0) / 127.f),
                        (float(block->red_1) / 127.f), (block->red_0 > block->red_1) ? 8u : 6u);

                    PrintIndex3bpp(block->indices);

                    wprintf(L"\n");
                }
                break;

                case DXGI_FORMAT_BC5_UNORM:
                {
                    auto block = reinterpret_cast<const BC5UBlock*>(sptr);

                    wprintf(L"\tU -   E0: %0.3f  E1: %0.3f (%u)\n\t   Index: ",
                        (float(block->u.red_0) / 255.f),
                        (float(block->u.red_1) / 255.f), (block->u.red_0 > block->u.red_1) ? 8u : 6u);

                    PrintIndex3bpp(block->u.indices);

                    wprintf(L"\n");

                    wprintf(L"\tV -   E0: %0.3f  E1: %0.3f (%u)\n\t   Index: ",
                        (float(block->v.red_0) / 255.f),
                        (float(block->v.red_1) / 255.f), (block->v.red_0 > block->v.red_1) ? 8u : 6u);

                    PrintIndex3bpp(block->v.indices);

                    wprintf(L"\n");
                }
                break;

                case DXGI_FORMAT_BC5_SNORM:
                {
                    auto block = reinterpret_cast<const BC5SBlock*>(sptr);

                    wprintf(L"\tU -   E0: %0.3f  E1: %0.3f (%u)\n\t   Index: ",
                        (float(block->u.red_0) / 127.f),
                        (float(block->u.red_1) / 127.f), (block->u.red_0 > block->u.red_1) ? 8u : 6u);

                    PrintIndex3bpp(block->u.indices);

                    wprintf(L"\n");

                    wprintf(L"\tV -   E0: %0.3f  E1: %0.3f (%u)\n\t   Index: ",
                        (float(block->v.red_0) / 127.f),
                        (float(block->v.red_1) / 127.f), (block->v.red_0 > block->v.red_1) ? 8u : 6u);

                    PrintIndex3bpp(block->v.indices);

                    wprintf(L"\n");
                }
                break;

                case DXGI_FORMAT_BC6H_UF16:
                case DXGI_FORMAT_BC6H_SF16:
                    // http://msdn.microsoft.com/en-us/library/windows/desktop/hh308952.aspx#decoding_the_bc6h_format

                    switch (*sptr & 0x03)
                    {
                    case 0x00:
                        // Mode 1 (2 bits, 00)
                    {
                        struct bc6h_mode1
                        {
                            uint64_t mode : 2; // { M, 0}, { M, 1}
                            uint64_t gy4 : 1;  // {GY, 4}
                            uint64_t by4 : 1;  // {BY, 4}
                            uint64_t bz4 : 1;  // {BZ, 4}
                            uint64_t rw : 10;  // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
                            uint64_t gw : 10;  // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
                            uint64_t bw : 10;  // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
                            uint64_t rx : 5;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
                            uint64_t gz4 : 1;  // {GZ, 4}
                            uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                            uint64_t gx : 5;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
                            uint64_t bz0 : 1;  // {BZ, 0},
                            uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                            uint64_t bx : 5;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
                            uint64_t bz1 : 1;  // {BZ, 1}
                            uint64_t by : 3;   // {BY, 0}, {BY, 1}, {BY, 2}
                            uint64_t by3 : 1;  // {BY, 3}
                            uint64_t ry : 5;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}
                            uint64_t bz2 : 1;  // {BZ, 2}
                            uint64_t rz : 5;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}
                            uint64_t bz3 : 1;  // {BZ, 3}
                            uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                            uint64_t indices : 46;
                        };
                        static_assert(sizeof(bc6h_mode1) == 16, "Block size must be 16 bytes");

                        bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                        auto m = reinterpret_cast<const bc6h_mode1*>(sptr);

                        XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
                        XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                        XMINT3 e1_A(int(m->ry),
                            int(m->gy | (m->gy4 << 4)),
                            int(m->by | (m->by3 << 3) | (m->by4 << 4)));
                        XMINT3 e1_B(int(m->rz),
                            int(m->gz | (m->gz4 << 4)),
                            int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4)));

                        if (bSigned)
                        {
                            e0_A.x = SIGN_EXTEND(e0_A.x, 10);
                            e0_A.y = SIGN_EXTEND(e0_A.y, 10);
                            e0_A.z = SIGN_EXTEND(e0_A.z, 10);

                            e0_B.x = SIGN_EXTEND(e0_B.x, 5);
                            e0_B.y = SIGN_EXTEND(e0_B.y, 5);
                            e0_B.z = SIGN_EXTEND(e0_B.z, 5);

                            e1_A.x = SIGN_EXTEND(e1_A.x, 5);
                            e1_A.y = SIGN_EXTEND(e1_A.y, 5);
                            e1_A.z = SIGN_EXTEND(e1_A.z, 5);

                            e1_B.x = SIGN_EXTEND(e1_B.x, 5);
                            e1_B.y = SIGN_EXTEND(e1_B.y, 5);
                            e1_B.z = SIGN_EXTEND(e1_B.z, 5);
                        }

                        wprintf(L"\tMode 1 - [10 5 5 5] shape %llu\n", m->d);
                        wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                        wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                        wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                        wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                        wprintf(L"\t         Index: ");
                        PrintIndex3bpp(m->indices, 1, m->d);
                        wprintf(L"\n");
                    }
                    break;

                    case 0x01:
                        // Mode 2 (2 bits, 01)
                    {
                        struct bc6h_mode2
                        {
                            uint64_t mode : 2; // { M, 0}, { M, 1}
                            uint64_t gy5 : 1;  // {GY, 5}
                            uint64_t gz45 : 2; // {GZ, 4}, {GZ, 5}
                            uint64_t rw : 7;   // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}
                            uint64_t bz : 2;   // {BZ, 0}, {BZ, 1}
                            uint64_t by4 : 1;  // {BY, 4},
                            uint64_t gw : 7;   // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}
                            uint64_t by5 : 1;  // {BY, 5}
                            uint64_t bz2 : 1;  // {BZ, 2}
                            uint64_t gy4 : 1;  // {GY, 4}
                            uint64_t bw : 7;   // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}
                            uint64_t bz3 : 1;  // {BZ, 3}
                            uint64_t bz5 : 1;  // {BZ, 5}
                            uint64_t bz4 : 1;  // {BZ, 4}
                            uint64_t rx : 6;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}
                            uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                            uint64_t gx : 6;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}
                            uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                            uint64_t bx : 5;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}
                            uint64_t by : 4;   // {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}
                            uint64_t ry : 6;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, {RY, 5}
                            uint64_t rz : 6;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {RZ, 5},
                            uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                            uint64_t indices : 46;

                        };
                        static_assert(sizeof(bc6h_mode2) == 16, "Block size must be 16 bytes");

                        bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                        auto m = reinterpret_cast<const bc6h_mode2*>(sptr);

                        XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
                        XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                        XMINT3 e1_A(int(m->ry),
                            int(m->gy | (m->gy4 << 4) | (m->gy5 << 5)),
                            int(m->by | (m->by4 << 4) | (m->by5 << 5)));
                        XMINT3 e1_B(int(m->rz),
                            int(m->gz | (m->gz45 << 4)),
                            int(m->bz | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4) | (m->bz5 << 5)));

                        if (bSigned)
                        {
                            e0_A.x = SIGN_EXTEND(e0_A.x, 7);
                            e0_A.y = SIGN_EXTEND(e0_A.y, 7);
                            e0_A.z = SIGN_EXTEND(e0_A.z, 7);

                            e0_B.x = SIGN_EXTEND(e0_B.x, 6);
                            e0_B.y = SIGN_EXTEND(e0_B.y, 6);
                            e0_B.z = SIGN_EXTEND(e0_B.z, 6);

                            e1_A.x = SIGN_EXTEND(e1_A.x, 6);
                            e1_A.y = SIGN_EXTEND(e1_A.y, 6);
                            e1_A.z = SIGN_EXTEND(e1_A.z, 6);

                            e1_B.x = SIGN_EXTEND(e1_B.x, 6);
                            e1_B.y = SIGN_EXTEND(e1_B.y, 6);
                            e1_B.z = SIGN_EXTEND(e1_B.z, 6);
                        }

                        wprintf(L"\tMode 2 - [7 6 6 6] shape %llu\n", m->d);
                        wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                        wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                        wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                        wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                        wprintf(L"\t         Index: ");
                        PrintIndex3bpp(m->indices, 1, m->d);
                        wprintf(L"\n");
                    }
                    break;

                    default:
                        switch (*sptr & 0x1F)
                        {
                        case 0x02:
                            // Mode 3 (5 bits, 00010)
                        {
                            struct bc6h_mode3
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 10;  // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
                                uint64_t gw : 10;  // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
                                uint64_t bw : 10;  // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
                                uint64_t rx : 5;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
                                uint64_t rw10 : 1; // {RW,10}
                                uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                                uint64_t gx : 4;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}
                                uint64_t gw10 : 1; // {GW,10}
                                uint64_t bz0 : 1;  // {BZ, 0}
                                uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                                uint64_t bx : 4;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}
                                uint64_t bw10 : 1; // {BW,10}
                                uint64_t bz1 : 1;  // {BZ, 1}
                                uint64_t by : 3;   // {BY, 0}, {BY, 1}, {BY, 2}
                                uint64_t by3 : 1;  // {BY, 3}
                                uint64_t ry : 5;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}
                                uint64_t bz2 : 1;  // {BZ, 2}
                                uint64_t rz : 5;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}
                                uint64_t bz3 : 1;  // {BZ, 3}
                                uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                                uint64_t indices : 46;

                            };
                            static_assert(sizeof(bc6h_mode3) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode3*>(sptr);

                            XMINT3 e0_A(int(m->rw | (m->rw10 << 10)),
                                int(m->gw | (m->gw10 << 10)),
                                int(m->bw | (m->bw10 << 10)));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                            XMINT3 e1_A(int(m->ry), int(m->gy),
                                int(m->by | (m->by3 << 3)));
                            XMINT3 e1_B(int(m->rz),
                                int(m->gz),
                                int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3)));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 11);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 11);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 11);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 5);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 4);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 4);

                                e1_A.x = SIGN_EXTEND(e1_A.x, 5);
                                e1_A.y = SIGN_EXTEND(e1_A.y, 4);
                                e1_A.z = SIGN_EXTEND(e1_A.z, 4);

                                e1_B.x = SIGN_EXTEND(e1_B.x, 5);
                                e1_B.y = SIGN_EXTEND(e1_B.y, 4);
                                e1_B.z = SIGN_EXTEND(e1_B.z, 4);
                            }

                            wprintf(L"\tMode 3 - [11 5 4 4] shape %llu\n", m->d);
                            wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                            wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex3bpp(m->indices, 1, m->d);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x06:
                            // Mode 4 (5 bits, 00110)
                        {
                            struct bc6h_mode4
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 10;  // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
                                uint64_t gw : 10;  // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
                                uint64_t bw : 10;  // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
                                uint64_t rx : 4;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}
                                uint64_t rw10 : 1; // {RW,10}
                                uint64_t gz4 : 1;  // {GZ, 4}
                                uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                                uint64_t gx : 5;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
                                uint64_t gw10 : 1; // {GW,10}
                                uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                                uint64_t bx : 4;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}
                                uint64_t bw10 : 1; // {BW,10}
                                uint64_t bz1 : 1;  // {BZ, 1}
                                uint64_t by : 3;   // {BY, 0}, {BY, 1}, {BY, 2}
                                uint64_t by3 : 1;  // {BY, 3}
                                uint64_t ry : 4;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}
                                uint64_t bz0 : 1;  // {BZ, 0}
                                uint64_t bz2 : 1;  // {BZ, 2}
                                uint64_t rz : 4;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}
                                uint64_t gy4 : 1;  // {GY, 4}
                                uint64_t bz3 : 1;  // {BZ, 3}
                                uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                                uint64_t indices : 46;

                            };
                            static_assert(sizeof(bc6h_mode4) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode4*>(sptr);

                            XMINT3 e0_A(int(m->rw | (m->rw10 << 10)),
                                int(m->gw | (m->gw10 << 10)),
                                int(m->bw | (m->bw10 << 10)));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                            XMINT3 e1_A(int(m->ry),
                                int(m->gy | (m->gy4 << 4)),
                                int(m->by | (m->by3 << 3)));
                            XMINT3 e1_B(int(m->rz),
                                int(m->gz | (m->gz4 << 4)),
                                int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3)));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 11);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 11);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 11);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 4);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 5);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 4);

                                e1_A.x = SIGN_EXTEND(e1_A.x, 4);
                                e1_A.y = SIGN_EXTEND(e1_A.y, 5);
                                e1_A.z = SIGN_EXTEND(e1_A.z, 4);

                                e1_B.x = SIGN_EXTEND(e1_B.x, 4);
                                e1_B.y = SIGN_EXTEND(e1_B.y, 5);
                                e1_B.z = SIGN_EXTEND(e1_B.z, 4);
                            }

                            wprintf(L"\tMode 4 - [11 4 5 4] shape %llu\n", m->d);
                            wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                            wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex3bpp(m->indices, 1, m->d);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x0A:
                            // Mode 5 (5 bits, 01010)
                        {
                            struct bc6h_mode5
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 10;  // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
                                uint64_t gw : 10;  // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
                                uint64_t bw : 10;  // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
                                uint64_t rx : 4;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}
                                uint64_t rw10 : 1; // {RW,10}
                                uint64_t by4 : 1;  // {BY, 4}
                                uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                                uint64_t gx : 4;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}
                                uint64_t gw10 : 1; // {GW,10}
                                uint64_t bz0 : 1;  // {BZ, 0}
                                uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                                uint64_t bx : 5;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
                                uint64_t bw10 : 1; // {BW,10}
                                uint64_t by : 3;   // {BY, 0}, {BY, 1}, {BY, 2}
                                uint64_t by3 : 1;  // {BY, 3}
                                uint64_t ry : 4;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}
                                uint64_t bz12 : 2; // {BZ, 1}, {BZ, 2}
                                uint64_t rz : 5;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {BZ, 4}
                                uint64_t bz3 : 1;  // {BZ, 3}
                                uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                                uint64_t indices : 46;
                            };
                            static_assert(sizeof(bc6h_mode5) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode5*>(sptr);

                            XMINT3 e0_A(int(m->rw | (m->rw10 << 10)),
                                int(m->gw | (m->gw10 << 10)),
                                int(m->bw | (m->bw10 << 10)));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                            XMINT3 e1_A(int(m->ry), int(m->gy),
                                int(m->by | (m->by3 << 3) | (m->by4 << 4)));
                            XMINT3 e1_B(int(m->rz), int(m->gz),
                                int(m->bz0 | (m->bz12 << 1) | (m->bz3 << 3)));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 11);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 11);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 11);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 4);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 4);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 5);

                                e1_A.x = SIGN_EXTEND(e1_A.x, 4);
                                e1_A.y = SIGN_EXTEND(e1_A.y, 4);
                                e1_A.z = SIGN_EXTEND(e1_A.z, 5);

                                e1_B.x = SIGN_EXTEND(e1_B.x, 4);
                                e1_B.y = SIGN_EXTEND(e1_B.y, 4);
                                e1_B.z = SIGN_EXTEND(e1_B.z, 5);
                            }

                            wprintf(L"\tMode 5 - [11 4 4 5] shape %llu\n", m->d);
                            wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                            wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex3bpp(m->indices, 1, m->d);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x0E:
                            // Mode 6 (5 bits, 01110)
                        {
                            struct bc6h_mode6
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 9;   // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}
                                uint64_t by4 : 1;  // {BY, 4}
                                uint64_t gw : 9;   // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}
                                uint64_t gy4 : 1;  // {GY, 4}
                                uint64_t bw : 9;   // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}
                                uint64_t bz4 : 1;  // {BZ, 4}
                                uint64_t rx : 5;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
                                uint64_t gz4 : 1;  // {GZ, 4}
                                uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                                uint64_t gx : 5;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
                                uint64_t bz0 : 1;  // {BZ, 0}
                                uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                                uint64_t bx : 5;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
                                uint64_t bz1 : 1;  // {BZ, 1}
                                uint64_t by : 3;   // {BY, 0}, {BY, 1}, {BY, 2}
                                uint64_t by3 : 1;  // {BY, 3}
                                uint64_t ry : 5;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4},
                                uint64_t bz2 : 1;  // {BZ, 2}
                                uint64_t rz : 5;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {BZ, 4}
                                uint64_t bz3 : 1;  // {BZ, 3}
                                uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                                uint64_t indices : 46;
                            };
                            static_assert(sizeof(bc6h_mode6) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode6*>(sptr);

                            XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                            XMINT3 e1_A(int(m->ry),
                                int(m->gy | (m->gy4 << 4)),
                                int(m->by | (m->by3 << 3) | (m->by4 << 4)));
                            XMINT3 e1_B(int(m->rz),
                                int(m->gz | (m->gz4 << 4)),
                                int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4)));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 9);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 9);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 9);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 5);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 5);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 5);

                                e1_A.x = SIGN_EXTEND(e1_A.x, 5);
                                e1_A.y = SIGN_EXTEND(e1_A.y, 5);
                                e1_A.z = SIGN_EXTEND(e1_A.z, 5);

                                e1_B.x = SIGN_EXTEND(e1_B.x, 5);
                                e1_B.y = SIGN_EXTEND(e1_B.y, 5);
                                e1_B.z = SIGN_EXTEND(e1_B.z, 5);
                            }

                            wprintf(L"\tMode 6 - [9 5 5 5] shape %llu\n", m->d);
                            wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                            wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex3bpp(m->indices, 1, m->d);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x12:
                            // Mode 7 (5 bits, 10010)
                        {
                            struct bc6h_mode7
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 8;   // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}
                                uint64_t gz4 : 1;  // {GZ, 4}
                                uint64_t by4 : 1;  // {BY, 4}
                                uint64_t gw : 8;   // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}
                                uint64_t bz2 : 1;  // {BZ, 2}
                                uint64_t gy4 : 1;  // {GY, 4}
                                uint64_t bw : 8;   // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}
                                uint64_t bz3 : 1;  // {BZ, 3}
                                uint64_t bz4 : 1;  // {BZ, 4}
                                uint64_t rx : 6;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}
                                uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                                uint64_t gx : 5;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
                                uint64_t bz0 : 1;  // {BZ, 0}
                                uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                                uint64_t bx : 5;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
                                uint64_t bz1 : 1;  // {BZ, 1}
                                uint64_t by : 3;   // {BY, 0}, {BY, 1}, {BY, 2}
                                uint64_t by3 : 1;  // {BY, 3}
                                uint64_t ry : 6;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, {RY, 5}
                                uint64_t rz : 6;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {RZ, 5}
                                uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                                uint64_t indices : 46;
                            };
                            static_assert(sizeof(bc6h_mode7) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode7*>(sptr);

                            XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                            XMINT3 e1_A(int(m->ry),
                                int(m->gy | (m->gy4 << 4)),
                                int(m->by | (m->by3 << 3) | (m->by4 << 4)));
                            XMINT3 e1_B(int(m->rz),
                                int(m->gz | (m->gz4 << 4)),
                                int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4)));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 8);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 8);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 8);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 6);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 5);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 5);

                                e1_A.x = SIGN_EXTEND(e1_A.x, 6);
                                e1_A.y = SIGN_EXTEND(e1_A.y, 5);
                                e1_A.z = SIGN_EXTEND(e1_A.z, 5);

                                e1_B.x = SIGN_EXTEND(e1_B.x, 6);
                                e1_B.y = SIGN_EXTEND(e1_B.y, 5);
                                e1_B.z = SIGN_EXTEND(e1_B.z, 5);
                            }

                            wprintf(L"\tMode 7 - [8 6 5 5] shape %llu\n", m->d);
                            wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                            wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex3bpp(m->indices, 1, m->d);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x16:
                            // Mode 8 (5 bits, 10110)
                        {
                            struct bc6h_mode8
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 8;   // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}
                                uint64_t bz0 : 1;  // {BZ, 0}
                                uint64_t by4 : 1;  // {BY, 4}
                                uint64_t gw : 8;   // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}
                                uint64_t gy5 : 1;  // {GY, 5}
                                uint64_t gy4 : 1;  // {GY, 4}
                                uint64_t bw : 8;   // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}
                                uint64_t gz5 : 1;  // {GZ, 5}
                                uint64_t bz4 : 1;  // {BZ, 4}
                                uint64_t rx : 5;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
                                uint64_t gz4 : 1;  // {GZ, 4}
                                uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                                uint64_t gx : 6;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}
                                uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                                uint64_t bx : 5;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}
                                uint64_t bz1 : 1;  // {BZ, 1}
                                uint64_t by : 3;   // {BY, 0}, {BY, 1}, {BY, 2}
                                uint64_t by3 : 1;  // {BY, 3}
                                uint64_t ry : 5;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}
                                uint64_t bz2 : 1;  // {BZ, 2}
                                uint64_t rz : 5;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}
                                uint64_t bz3 : 1;  // {BZ, 3}
                                uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                                uint64_t indices : 46;
                            };
                            static_assert(sizeof(bc6h_mode8) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode8*>(sptr);

                            XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                            XMINT3 e1_A(int(m->ry),
                                int(m->gy | (m->gy4 << 4) | (m->gy5 << 5)),
                                int(m->by | (m->by3 << 3) | (m->by4 << 4)));
                            XMINT3 e1_B(int(m->rz),
                                int(m->gz | (m->gz4 << 4) | (m->gz5 << 5)),
                                int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4)));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 8);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 8);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 8);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 5);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 6);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 5);

                                e1_A.x = SIGN_EXTEND(e1_A.x, 5);
                                e1_A.y = SIGN_EXTEND(e1_A.y, 6);
                                e1_A.z = SIGN_EXTEND(e1_A.z, 5);

                                e1_B.x = SIGN_EXTEND(e1_B.x, 5);
                                e1_B.y = SIGN_EXTEND(e1_B.y, 6);
                                e1_B.z = SIGN_EXTEND(e1_B.z, 5);
                            }

                            wprintf(L"\tMode 8 - [8 5 6 5] shape %llu\n", m->d);
                            wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                            wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex3bpp(m->indices, 1, m->d);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x1A:
                            // Mode 9 (5 bits, 11010)
                        {
                            struct bc6h_mode9
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 8;   // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}
                                uint64_t bz1 : 1;  // {BZ, 1}
                                uint64_t by4 : 1;  // {BY, 4}
                                uint64_t gw : 8;   // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}
                                uint64_t by5 : 1;  // {BY, 5}
                                uint64_t gy4 : 1;  // {GY, 4}
                                uint64_t bw : 8;   // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}
                                uint64_t bz5 : 1;  // {BZ, 5}
                                uint64_t bz4 : 1;  // {BZ, 4}
                                uint64_t rx : 5;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}
                                uint64_t gz4 : 1;  // {GZ, 4}
                                uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                                uint64_t gx : 5;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}
                                uint64_t bz0 : 1;  // {BZ, 0}
                                uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                                uint64_t bx : 6;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}
                                uint64_t by : 3;   // {BY, 0}, {BY, 1}, {BY, 2}
                                uint64_t by3 : 1;  // {BY, 3}
                                uint64_t ry : 5;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}
                                uint64_t bz2 : 1;  // {BZ, 2}
                                uint64_t rz : 5;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}
                                uint64_t bz3 : 1;  // {BZ, 3}
                                uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                                uint64_t indices : 46;
                            };
                            static_assert(sizeof(bc6h_mode9) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode9*>(sptr);

                            XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                            XMINT3 e1_A(int(m->ry),
                                int(m->gy | (m->gy4 << 4)),
                                int(m->by | (m->by3 << 3) | (m->by4 << 4) | (m->by5 << 5)));
                            XMINT3 e1_B(int(m->rz),
                                int(m->gz | (m->gz4 << 4)),
                                int(m->bz0 | (m->bz1 << 1) | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4) | (m->bz5 << 5)));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 8);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 8);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 8);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 5);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 5);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 6);

                                e1_A.x = SIGN_EXTEND(e1_A.x, 5);
                                e1_A.y = SIGN_EXTEND(e1_A.y, 5);
                                e1_A.z = SIGN_EXTEND(e1_A.z, 6);

                                e1_B.x = SIGN_EXTEND(e1_B.x, 5);
                                e1_B.y = SIGN_EXTEND(e1_B.y, 5);
                                e1_B.z = SIGN_EXTEND(e1_B.z, 6);
                            }

                            wprintf(L"\tMode 9 - [8 5 5 6] shape %llu\n", m->d);
                            wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                            wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex3bpp(m->indices, 1, m->d);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x1E:
                            // Mode 10 (5 bits, 11110)
                        {
                            struct bc6h_mode10
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 6;   // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}
                                uint64_t gz4 : 1;  // {GZ, 4}
                                uint64_t bz : 2;  // {BZ, 0}, {BZ, 1}
                                uint64_t by4 : 1;  // {BY, 4}
                                uint64_t gw : 6;   // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}
                                uint64_t gy5 : 1;  // {GY, 5}
                                uint64_t by5 : 1;  // {BY, 5}
                                uint64_t bz2 : 1;  // {BZ, 2}
                                uint64_t gy4 : 1;  // {GY, 4}
                                uint64_t bw : 6;   // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {GZ, 5}
                                uint64_t bz3 : 1;  // {BZ, 3}
                                uint64_t bz5 : 1;  // {BZ, 5}
                                uint64_t bz4 : 1;  // {BZ, 4}
                                uint64_t rx : 6;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}
                                uint64_t gy : 4;   // {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}
                                uint64_t gx : 6;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}
                                uint64_t gz : 4;   // {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}
                                uint64_t bx : 6;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}
                                uint64_t by : 3;   // {BY, 0}, {BY, 1}, {BY, 2}
                                uint64_t by3 : 1;  // {BY, 3}
                                uint64_t ry : 6;   // {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, {RY, 5}
                                uint64_t rz : 6;   // {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {RZ, 5}
                                uint64_t d : 5;    // { D, 0}, { D, 1}, { D, 2}, { D, 3}, { D, 4}
                                uint64_t indices : 46;
                            };
                            static_assert(sizeof(bc6h_mode10) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode10*>(sptr);

                            XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));
                            XMINT3 e1_A(int(m->ry),
                                int(m->gy | (m->gy4 << 4) | (m->gy5 << 5)),
                                int(m->by | (m->by3 << 3) | (m->by4 << 4) | (m->by5 << 5)));
                            XMINT3 e1_B(int(m->rz),
                                int(m->gz | (m->gz4 << 4)),
                                int(m->bz | (m->bz2 << 2) | (m->bz3 << 3) | (m->bz4 << 4) | (m->bz5 << 5)));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 6);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 6);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 6);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 6);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 6);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 6);

                                e1_A.x = SIGN_EXTEND(e1_A.x, 6);
                                e1_A.y = SIGN_EXTEND(e1_A.y, 6);
                                e1_A.z = SIGN_EXTEND(e1_A.z, 6);

                                e1_B.x = SIGN_EXTEND(e1_B.x, 6);
                                e1_B.y = SIGN_EXTEND(e1_B.y, 6);
                                e1_B.z = SIGN_EXTEND(e1_B.z, 6);
                            }

                            wprintf(L"\tMode 10 - [6 6 6 6] shape %llu\n", m->d);
                            wprintf(L"\t         E0(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E0(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         E1(A): (%04X, %04X, %04X)\n", e1_A.x & 0xFFFF, e1_A.y & 0xFFFF, e1_A.z & 0xFFFF);
                            wprintf(L"\t         E1(B): (%04X, %04X, %04X)\n", e1_B.x & 0xFFFF, e1_B.y & 0xFFFF, e1_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex3bpp(m->indices, 1, m->d);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x03:
                            // Mode 11 (5 bits, 00011)
                        {
                            struct bc6h_mode11
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 10;  // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
                                uint64_t gw : 10;  // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
                                uint64_t bw : 10;  // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
                                uint64_t rx : 10;  // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}, {RX, 6}, {RX, 7}, {RX, 8}, {RX, 9}
                                uint64_t gx : 10;  // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}, {GX, 6}, {GX, 7}, {GX, 8}, {GX, 9}
                                uint64_t bx : 9;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}, {BX, 6}, {BX, 7}, {BX, 8}
                                uint64_t bx9 : 1;  // {BX, 9}
                                uint64_t indices : 63;
                            };
                            static_assert(sizeof(bc6h_mode11) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode11*>(sptr);

                            XMINT3 e0_A(int(m->rw), int(m->gw), int(m->bw));
                            XMINT3 e0_B(int(m->rx), int(m->gx),
                                int(m->bx | (m->bx9 << 9)));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 10);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 10);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 10);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 10);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 10);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 10);
                            }

                            wprintf(L"\tMode 11 - [10 10]\n");
                            wprintf(L"\t         E(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex4bpp(m->indices, 0, 0);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x07:
                            // Mode 12 (5 bits, 00111)
                        {
                            struct bc6h_mode12
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 10;  // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
                                uint64_t gw : 10;  // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
                                uint64_t bw : 10;  // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
                                uint64_t rx : 9;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}, {RX, 6}, {RX, 7}, {RX, 8}
                                uint64_t rw10 : 1; // {RW,10}
                                uint64_t gx : 9;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}, {GX, 6}, {GX, 7}, {GX, 8}
                                uint64_t gw10 : 1; // {GW,10}
                                uint64_t bx : 9;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}, {BX, 6}, {BX, 7}, {BX, 8}
                                uint64_t bw10 : 1; // {BW,10}
                                uint64_t indices : 63;
                            };
                            static_assert(sizeof(bc6h_mode12) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode12*>(sptr);

                            XMINT3 e0_A(int(m->rw | (m->rw10 << 10)),
                                int(m->gw | (m->gw10 << 10)),
                                int(m->bw | (m->bw10 << 10)));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 11);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 11);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 11);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 9);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 9);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 9);
                            }

                            wprintf(L"\tMode 12 - [11 9]\n");
                            wprintf(L"\t         E(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex4bpp(m->indices, 0, 0);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x0B:
                            // Mode 13 (5 bits, 01011)
                        {
                            struct bc6h_mode13
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 10;  // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
                                uint64_t gw : 10;  // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
                                uint64_t bw : 10;  // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
                                uint64_t rx : 8;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, {RX, 5}, {RX, 6}, {RX, 7}
                                uint64_t rw11 : 1; // {RW,11}
                                uint64_t rw10 : 1; // {RW,10}
                                uint64_t gx : 8;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, {GX, 5}, {GX, 6}, {GX, 7}
                                uint64_t gw11 : 1; // {GW,11}
                                uint64_t gw10 : 1; // {GW,10}
                                uint64_t bx : 8;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, {BX, 5}, {BX, 6}, {BX, 7}
                                uint64_t bw11 : 1; // {BW,11}
                                uint64_t bw10 : 1; // {BW,10}
                                uint64_t indices : 63;
                            };
                            static_assert(sizeof(bc6h_mode13) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode13*>(sptr);

                            XMINT3 e0_A(int(m->rw | (m->rw10 << 10) | (m->rw11 << 11)),
                                int(m->gw | (m->gw10 << 10) | (m->gw11 << 11)),
                                int(m->bw | (m->bw10 << 10) | (m->bw11 << 11)));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 12);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 12);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 12);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 8);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 8);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 8);
                            }

                            wprintf(L"\tMode 13 - [12 8]\n");
                            wprintf(L"\t         E(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex4bpp(m->indices, 0, 0);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x0F:
                            // Mode 14 (5 bits, 01111)
                        {
                            struct bc6h_mode14
                            {
                                uint64_t mode : 5; // { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}
                                uint64_t rw : 10;  // {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}
                                uint64_t gw : 10;  // {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}
                                uint64_t bw : 10;  // {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}
                                uint64_t rx : 4;   // {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}
                                uint64_t rw15 : 1; // {RW,15}
                                uint64_t rw14 : 1; // {RW,14}
                                uint64_t rw13 : 1; // {RW,13}
                                uint64_t rw12 : 1; // {RW,12}
                                uint64_t rw11 : 1; // {RW,11}
                                uint64_t rw10 : 1; // {RW,10}
                                uint64_t gx : 4;   // {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}
                                uint64_t gw15 : 1; // {GW,15}
                                uint64_t gw14 : 1; // {GW,14}
                                uint64_t gw13 : 1; // {GW,13}
                                uint64_t gw12 : 1; // {GW,12}
                                uint64_t gw11 : 1; // {GW,11}
                                uint64_t gw10 : 1; // {GW,10}
                                uint64_t bx : 4;   // {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}
                                uint64_t bw15 : 1; // {BW,15}
                                uint64_t bw14 : 1; // {BW,14}
                                uint64_t bw13 : 1; // {BW,13}
                                uint64_t bw12 : 1; // {BW,12}
                                uint64_t bw11 : 1; // {BW,11}
                                uint64_t bw10 : 1; // {BW,10}
                                uint64_t indices : 63;
                            };
                            static_assert(sizeof(bc6h_mode14) == 16, "Block size must be 16 bytes");

                            bool bSigned = (image.format == DXGI_FORMAT_BC6H_SF16) ? true : false;

                            auto m = reinterpret_cast<const bc6h_mode14*>(sptr);

                            XMINT3 e0_A(int(m->rw | (m->rw10 << 10) | (m->rw11 << 11) | (m->rw12 << 12) | (m->rw13 << 13) | (m->rw14 << 14) | (m->rw15 << 15)),
                                int(m->gw | (m->gw10 << 10) | (m->gw11 << 11) | (m->gw12 << 12) | (m->gw13 << 13) | (m->gw14 << 14) | (m->gw15 << 15)),
                                int(m->bw | (m->bw10 << 10) | (m->bw11 << 11) | (m->bw12 << 12) | (m->bw13 << 13) | (m->bw14 << 14) | (m->bw15 << 15)));
                            XMINT3 e0_B(int(m->rx), int(m->gx), int(m->bx));

                            if (bSigned)
                            {
                                e0_A.x = SIGN_EXTEND(e0_A.x, 16);
                                e0_A.y = SIGN_EXTEND(e0_A.y, 16);
                                e0_A.z = SIGN_EXTEND(e0_A.z, 16);

                                e0_B.x = SIGN_EXTEND(e0_B.x, 4);
                                e0_B.y = SIGN_EXTEND(e0_B.y, 4);
                                e0_B.z = SIGN_EXTEND(e0_B.z, 4);
                            }

                            wprintf(L"\tMode 14 - [16 4]\n");
                            wprintf(L"\t         E(A): (%04X, %04X, %04X)\n", e0_A.x & 0xFFFF, e0_A.y & 0xFFFF, e0_A.z & 0xFFFF);
                            wprintf(L"\t         E(B): (%04X, %04X, %04X)\n", e0_B.x & 0xFFFF, e0_B.y & 0xFFFF, e0_B.z & 0xFFFF);
                            wprintf(L"\t         Index: ");
                            PrintIndex4bpp(m->indices, 0, 0);
                            wprintf(L"\n");
                        }
                        break;

                        case 0x13: // Reserved mode (5 bits, 10011)
                            wprintf(L"\tERROR - Reserved mode 10011\n");
                            break;

                        case 0x17: // Reserved mode (5 bits, 10111)
                            wprintf(L"\tERROR - Reserved mode 10011\n");
                            break;

                        case 0x1B: // Reserved mode (5 bits, 11011)
                            wprintf(L"\tERROR - Reserved mode 11011\n");
                            break;

                        case 0x1F: // Reserved mode (5 bits, 11111)
                            wprintf(L"\tERROR - Reserved mode 11111\n");
                            break;

                        default:
                            break;
                        }
                        break;
                    }
                    break;

                case DXGI_FORMAT_BC7_UNORM:
                case DXGI_FORMAT_BC7_UNORM_SRGB:
                    // http://msdn.microsoft.com/en-us/library/windows/desktop/hh308954.aspx

                    if (*sptr & 0x01)
                    {
                        // Mode 0 (1)
                        struct bc7_mode0
                        {
                            uint64_t mode : 1;
                            uint64_t part : 4;
                            uint64_t r0 : 4;
                            uint64_t r1 : 4;
                            uint64_t r2 : 4;
                            uint64_t r3 : 4;
                            uint64_t r4 : 4;
                            uint64_t r5 : 4;
                            uint64_t g0 : 4;
                            uint64_t g1 : 4;
                            uint64_t g2 : 4;
                            uint64_t g3 : 4;
                            uint64_t g4 : 4;
                            uint64_t g5 : 4;
                            uint64_t b0 : 4;
                            uint64_t b1 : 4;
                            uint64_t b2 : 3;
                            uint64_t b2n : 1;
                            uint64_t b3 : 4;
                            uint64_t b4 : 4;
                            uint64_t b5 : 4;
                            uint64_t P0 : 1;
                            uint64_t P1 : 1;
                            uint64_t P2 : 1;
                            uint64_t P3 : 1;
                            uint64_t P4 : 1;
                            uint64_t P5 : 1;
                            uint64_t index : 45;
                        };
                        static_assert(sizeof(bc7_mode0) == 16, "Block size must be 16 bytes");

                        auto m = reinterpret_cast<const bc7_mode0*>(sptr);

                        wprintf(L"\tMode 0 - [4 4 4] partition %llu\n", m->part);
                        wprintf(L"\t         E0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 31.f, float((m->g0 << 1) | m->P0) / 31.f, float((m->b0 << 1) | m->P0) / 31.f);
                        wprintf(L"\t         E1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P1) / 31.f, float((m->g1 << 1) | m->P1) / 31.f, float((m->b1 << 1) | m->P1) / 31.f);
                        wprintf(L"\t         E2:(%0.3f, %0.3f, %0.3f)\n", float((m->r2 << 1) | m->P2) / 31.f, float((m->g2 << 1) | m->P2) / 31.f, float(((m->b2 | (m->b2n << 3)) << 1) | m->P2) / 31.f);
                        wprintf(L"\t         E3:(%0.3f, %0.3f, %0.3f)\n", float((m->r3 << 1) | m->P3) / 31.f, float((m->g3 << 1) | m->P3) / 31.f, float((m->b3 << 1) | m->P3) / 31.f);
                        wprintf(L"\t         E4:(%0.3f, %0.3f, %0.3f)\n", float((m->r4 << 1) | m->P4) / 31.f, float((m->g4 << 1) | m->P4) / 31.f, float((m->b4 << 1) | m->P4) / 31.f);
                        wprintf(L"\t         E5:(%0.3f, %0.3f, %0.3f)\n", float((m->r5 << 1) | m->P5) / 31.f, float((m->g5 << 1) | m->P5) / 31.f, float((m->b5 << 1) | m->P5) / 31.f);
                        wprintf(L"\t      Index: ");
                        PrintIndex2bpp(m->index, 2, m->part);
                        wprintf(L"\n");
                    }
                    else if (*sptr & 0x02)
                    {
                        // Mode 1 (01)
                        struct bc7_mode1
                        {
                            uint64_t mode : 2;
                            uint64_t part : 6;
                            uint64_t r0 : 6;
                            uint64_t r1 : 6;
                            uint64_t r2 : 6;
                            uint64_t r3 : 6;
                            uint64_t g0 : 6;
                            uint64_t g1 : 6;
                            uint64_t g2 : 6;
                            uint64_t g3 : 6;
                            uint64_t b0 : 6;
                            uint64_t b1 : 2;
                            uint64_t b1n : 4;
                            uint64_t b2 : 6;
                            uint64_t b3 : 6;
                            uint64_t P0 : 1;
                            uint64_t P1 : 1;
                            uint64_t index : 46;
                        };
                        static_assert(sizeof(bc7_mode1) == 16, "Block size must be 16 bytes");

                        auto m = reinterpret_cast<const bc7_mode1*>(sptr);

                        wprintf(L"\tMode 1 - [6 6 6] partition %llu\n", m->part);
                        wprintf(L"\t         E0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 127.f, float((m->g0 << 1) | m->P0) / 127.f, float((m->b0 << 1) | m->P0) / 127.f);
                        wprintf(L"\t         E1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P0) / 127.f, float((m->g1 << 1) | m->P0) / 127.f, float(((m->b1 | (m->b1n << 2)) << 1) | m->P0) / 127.f);
                        wprintf(L"\t         E2:(%0.3f, %0.3f, %0.3f)\n", float((m->r2 << 1) | m->P1) / 127.f, float((m->g2 << 1) | m->P1) / 127.f, float((m->b2 << 1) | m->P1) / 127.f);
                        wprintf(L"\t         E3:(%0.3f, %0.3f, %0.3f)\n", float((m->r3 << 1) | m->P1) / 127.f, float((m->g3 << 1) | m->P1) / 127.f, float((m->b3 << 1) | m->P1) / 127.f);
                        wprintf(L"\t      Index: ");
                        PrintIndex3bpp(m->index, 1, m->part);
                        wprintf(L"\n");
                    }
                    else if (*sptr & 0x04)
                    {
                        // Mode 2 (001)
                        struct bc7_mode2
                        {
                            uint64_t mode : 3;
                            uint64_t part : 6;
                            uint64_t r0 : 5;
                            uint64_t r1 : 5;
                            uint64_t r2 : 5;
                            uint64_t r3 : 5;
                            uint64_t r4 : 5;
                            uint64_t r5 : 5;
                            uint64_t g0 : 5;
                            uint64_t g1 : 5;
                            uint64_t g2 : 5;
                            uint64_t g3 : 5;
                            uint64_t g4 : 5;
                            uint64_t g5 : 5;
                            uint64_t b0 : 5;
                            uint64_t b1 : 5;
                            uint64_t b2 : 5;
                            uint64_t b3 : 5;
                            uint64_t b4 : 5;
                            uint64_t b5 : 5;
                            uint64_t index : 29;
                        };
                        static_assert(sizeof(bc7_mode2) == 16, "Block size must be 16 bytes");

                        auto m = reinterpret_cast<const bc7_mode2*>(sptr);

                        wprintf(L"\tMode 2 - [5 5 5] partition %llu\n", m->part);
                        wprintf(L"\t         E0:(%0.3f, %0.3f, %0.3f)\n", float(m->r0) / 31.f, float(m->g0) / 31.f, float(m->b0) / 31.f);
                        wprintf(L"\t         E1:(%0.3f, %0.3f, %0.3f)\n", float(m->r1) / 31.f, float(m->g1) / 31.f, float(m->b1) / 31.f);
                        wprintf(L"\t         E2:(%0.3f, %0.3f, %0.3f)\n", float(m->r2) / 31.f, float(m->g2) / 31.f, float(m->b2) / 31.f);
                        wprintf(L"\t         E3:(%0.3f, %0.3f, %0.3f)\n", float(m->r3) / 31.f, float(m->g3) / 31.f, float(m->b3) / 31.f);
                        wprintf(L"\t         E4:(%0.3f, %0.3f, %0.3f)\n", float(m->r4) / 31.f, float(m->g4) / 31.f, float(m->b4) / 31.f);
                        wprintf(L"\t         E5:(%0.3f, %0.3f, %0.3f)\n", float(m->r5) / 31.f, float(m->g5) / 31.f, float(m->b5) / 31.f);
                        wprintf(L"\t      Index: ");
                        PrintIndex2bpp(m->index, 2, m->part);
                        wprintf(L"\n");
                    }
                    else if (*sptr & 0x08)
                    {
                        // Mode 3 (0001)
                        struct bc7_mode3
                        {
                            uint64_t mode : 4;
                            uint64_t part : 6;
                            uint64_t r0 : 7;
                            uint64_t r1 : 7;
                            uint64_t r2 : 7;
                            uint64_t r3 : 7;
                            uint64_t g0 : 7;
                            uint64_t g1 : 7;
                            uint64_t g2 : 7;
                            uint64_t g3 : 5;
                            uint64_t g3n : 2;
                            uint64_t b0 : 7;
                            uint64_t b1 : 7;
                            uint64_t b2 : 7;
                            uint64_t b3 : 7;
                            uint64_t P0 : 1;
                            uint64_t P1 : 1;
                            uint64_t P2 : 1;
                            uint64_t P3 : 1;
                            uint64_t index : 30;
                        };
                        static_assert(sizeof(bc7_mode3) == 16, "Block size must be 16 bytes");

                        auto m = reinterpret_cast<const bc7_mode3*>(sptr);

                        wprintf(L"\tMode 3 - [7 7 7] partition %llu\n", m->part);
                        wprintf(L"\t         E0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 255.f, float((m->g0 << 1) | m->P0) / 255.f, float((m->b0 << 1) | m->P0) / 255.f);
                        wprintf(L"\t         E1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P1) / 255.f, float((m->g1 << 1) | m->P1) / 255.f, float((m->b1 << 1) | m->P1) / 255.f);
                        wprintf(L"\t         E2:(%0.3f, %0.3f, %0.3f)\n", float((m->r2 << 1) | m->P2) / 255.f, float((m->g2 << 1) | m->P2) / 255.f, float((m->b2 << 1) | m->P2) / 255.f);
                        wprintf(L"\t         E3:(%0.3f, %0.3f, %0.3f)\n", float((m->r3 << 1) | m->P3) / 255.f, float(((m->g3 | (m->g3n << 5)) << 1) | m->P3) / 255.f, float((m->b3 << 1) | m->P3) / 255.f);
                        wprintf(L"\t      Index: ");
                        PrintIndex2bpp(m->index, 1, m->part);
                        wprintf(L"\n");
                    }
                    else if (*sptr & 0x10)
                    {
                        // Mode 4 (00001)
                        struct bc7_mode4
                        {
                            uint64_t mode : 5;
                            uint64_t rot : 2;
                            uint64_t idx : 1;
                            uint64_t r0 : 5;
                            uint64_t r1 : 5;
                            uint64_t g0 : 5;
                            uint64_t g1 : 5;
                            uint64_t b0 : 5;
                            uint64_t b1 : 5;
                            uint64_t a0 : 6;
                            uint64_t a1 : 6;
                            uint64_t color_index : 14;
                            uint64_t color_indexn : 17;
                            uint64_t alpha_index : 47;
                        };
                        static_assert(sizeof(bc7_mode4) == 16, "Block size must be 16 bytes");

                        auto m = reinterpret_cast<const bc7_mode4*>(sptr);

                        wprintf(L"\tMode 4 - [5 5 5 A6] indx mode %ls, rot-bits %llu%ls\n", m->idx ? L"3-bit" : L"2-bit", m->rot, GetRotBits(m->rot));
                        wprintf(L"\t         C0:(%0.3f, %0.3f, %0.3f)\n", float(m->r0) / 31.f, float(m->g0) / 31.f, float(m->b0) / 31.f);
                        wprintf(L"\t         C1:(%0.3f, %0.3f, %0.3f)\n", float(m->r1) / 31.f, float(m->g1) / 31.f, float(m->b1) / 31.f);
                        wprintf(L"\t         A0:(%0.3f)\n", float(m->a0) / 63.f);
                        wprintf(L"\t         A1:(%0.3f)\n", float(m->a1) / 63.f);
                        wprintf(L"\t    Colors: ");

                        uint64_t color_index = uint64_t(m->color_index) | uint64_t(m->color_indexn << 14);
                        if (m->idx)
                            PrintIndex3bpp(color_index, 0, 0);
                        else
                            PrintIndex2bpp(color_index, 0, 0);
                        wprintf(L"\n");
                        wprintf(L"\t     Alpha: ");
                        PrintIndex3bpp(m->alpha_index, 0, 0);
                        wprintf(L"\n");
                    }
                    else if (*sptr & 0x20)
                    {
                        // Mode 5 (000001)
                        struct bc7_mode5
                        {
                            uint64_t mode : 6;
                            uint64_t rot : 2;
                            uint64_t r0 : 7;
                            uint64_t r1 : 7;
                            uint64_t g0 : 7;
                            uint64_t g1 : 7;
                            uint64_t b0 : 7;
                            uint64_t b1 : 7;
                            uint64_t a0 : 8;
                            uint64_t a1 : 6;
                            uint64_t a1n : 2;
                            uint64_t color_index : 31;
                            uint64_t alpha_index : 31;
                        };
                        static_assert(sizeof(bc7_mode5) == 16, "Block size must be 16 bytes");

                        auto m = reinterpret_cast<const bc7_mode5*>(sptr);

                        wprintf(L"\tMode 5 - [7 7 7 A8] rot-bits %llu%ls\n", m->rot, GetRotBits(m->rot));
                        wprintf(L"\t         C0:(%0.3f, %0.3f, %0.3f)\n", float(m->r0) / 127.f, float(m->g0) / 127.f, float(m->b0) / 127.f);
                        wprintf(L"\t         C1:(%0.3f, %0.3f, %0.3f)\n", float(m->r1) / 127.f, float(m->g1) / 127.f, float(m->b1) / 127.f);
                        wprintf(L"\t         A0:(%0.3f)\n", float(m->a0) / 255.f);
                        wprintf(L"\t         A1:(%0.3f)\n", float(m->a1 | (m->a1n << 6)) / 255.f);
                        wprintf(L"\t    Colors: ");
                        PrintIndex2bpp(m->color_index, 0, 0);
                        wprintf(L"\n");
                        wprintf(L"\t     Alpha: ");
                        PrintIndex2bpp(m->alpha_index, 0, 0);
                        wprintf(L"\n");
                    }
                    else if (*sptr & 0x40)
                    {
                        // Mode 6 (0000001)
                        struct bc7_mode6
                        {
                            uint64_t mode : 7;
                            uint64_t r0 : 7;
                            uint64_t r1 : 7;
                            uint64_t g0 : 7;
                            uint64_t g1 : 7;
                            uint64_t b0 : 7;
                            uint64_t b1 : 7;
                            uint64_t a0 : 7;
                            uint64_t a1 : 7;
                            uint64_t P0 : 1;
                            uint64_t P1 : 1;
                            uint64_t index : 63;

                        };
                        static_assert(sizeof(bc7_mode6) == 16, "Block size must be 16 bytes");

                        auto m = reinterpret_cast<const bc7_mode6*>(sptr);

                        wprintf(L"\tMode 6 - [7 7 7 A7]\n");
                        wprintf(L"\t         C0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 255.f, float((m->g0 << 1) | m->P0) / 255.f, float((m->b0 << 1) | m->P0) / 255.f);
                        wprintf(L"\t         C1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P1) / 255.f, float((m->g1 << 1) | m->P1) / 255.f, float((m->b1 << 1) | m->P1) / 255.f);
                        wprintf(L"\t         A0:(%0.3f)\n", float((m->a0 << 1) | m->P0) / 255.f);
                        wprintf(L"\t         A1:(%0.3f)\n", float((m->a1 << 1) | m->P1) / 255.f);
                        wprintf(L"\t      Index: ");
                        PrintIndex4bpp(m->index, 0, 0);
                        wprintf(L"\n");
                    }
                    else if (*sptr & 0x80)
                    {
                        // Mode 7 (00000001)
                        struct bc7_mode7
                        {
                            uint64_t mode : 8;
                            uint64_t part : 6;
                            uint64_t r0 : 5;
                            uint64_t r1 : 5;
                            uint64_t r2 : 5;
                            uint64_t r3 : 5;
                            uint64_t g0 : 5;
                            uint64_t g1 : 5;
                            uint64_t g2 : 5;
                            uint64_t g3 : 5;
                            uint64_t b0 : 5;
                            uint64_t b1 : 5;
                            uint64_t b2 : 5;
                            uint64_t b3 : 5;
                            uint64_t a0 : 5;
                            uint64_t a1 : 5;
                            uint64_t a2 : 5;
                            uint64_t a3 : 5;
                            uint64_t P0 : 1;
                            uint64_t P1 : 1;
                            uint64_t P2 : 1;
                            uint64_t P3 : 1;
                            uint64_t index : 30;

                        };
                        static_assert(sizeof(bc7_mode7) == 16, "Block size must be 16 bytes");

                        auto m = reinterpret_cast<const bc7_mode7*>(sptr);

                        wprintf(L"\tMode 7 - [5 5 5 A5] partition %llu\n", m->part);
                        wprintf(L"\t         C0:(%0.3f, %0.3f, %0.3f)\n", float((m->r0 << 1) | m->P0) / 63.f, float((m->g0 << 1) | m->P0) / 63.f, float((m->b0 << 1) | m->P0) / 63.f);
                        wprintf(L"\t         C1:(%0.3f, %0.3f, %0.3f)\n", float((m->r1 << 1) | m->P1) / 63.f, float((m->g1 << 1) | m->P1) / 63.f, float((m->b1 << 1) | m->P1) / 63.f);
                        wprintf(L"\t         C2:(%0.3f, %0.3f, %0.3f)\n", float((m->r2 << 1) | m->P2) / 63.f, float((m->g2 << 1) | m->P2) / 63.f, float((m->b2 << 1) | m->P2) / 63.f);
                        wprintf(L"\t         C3:(%0.3f, %0.3f, %0.3f)\n", float((m->r3 << 1) | m->P3) / 63.f, float((m->g3 << 1) | m->P3) / 63.f, float((m->b3 << 1) | m->P3) / 63.f);
                        wprintf(L"\t         A0:(%0.3f)\n", float((m->a0 << 1) | m->P0) / 63.f);
                        wprintf(L"\t         A1:(%0.3f)\n", float((m->a1 << 1) | m->P1) / 63.f);
                        wprintf(L"\t         A2:(%0.3f)\n", float((m->a2 << 1) | m->P2) / 63.f);
                        wprintf(L"\t         A3:(%0.3f)\n", float((m->a3 << 1) | m->P3) / 63.f);
                        wprintf(L"\t      Index: ");
                        PrintIndex4bpp(m->index, 1, m->part);
                        wprintf(L"\n");
                    }
                    else
                    {
                        // Reserved mode 8 (00000000)
                        wprintf(L"\tERROR - Reserved mode 8\n");
                    }
                    break;
                }
            }
        }

        return S_OK;
    }
}


//--------------------------------------------------------------------------------------
// Entry-point
//--------------------------------------------------------------------------------------
#ifdef _PREFAST_
#pragma prefast(disable : 28198, "Command-line tool, frees all memory on exit")
#endif

int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
{
    // Parameters and defaults
    TEX_FILTER_FLAGS dwFilter = TEX_FILTER_DEFAULT;
    int pixelx = -1;
    int pixely = -1;
    uint32_t diffColor = 0;
    float threshold = 0.25f;
    DXGI_FORMAT diffFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
    uint32_t fileType = WIC_CODEC_BMP;
    wchar_t szOutputFile[MAX_PATH] = {};

    // Set locale for output since GetErrorDesc can get localized strings.
    std::locale::global(std::locale(""));

    // Initialize COM (needed for WIC)
    HRESULT hr = hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr))
    {
        wprintf(L"Failed to initialize COM (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
        return 1;
    }

    // Process command line
    if (argc < 2)
    {
        PrintUsage();
        return 0;
    }

    uint32_t dwCommand = LookupByName(argv[1], g_pCommands);
    switch (dwCommand)
    {
    case CMD_INFO:
    case CMD_ANALYZE:
    case CMD_COMPARE:
    case CMD_DIFF:
    case CMD_DUMPBC:
    case CMD_DUMPDDS:
        break;

    default:
        wprintf(L"Must use one of: info, analyze, compare, diff, dumpbc, or dumpdds\n\n");
        return 1;
    }

    uint32_t dwOptions = 0;
    std::list<SConversion> conversion;

    for (int iArg = 2; iArg < argc; iArg++)
    {
        PWSTR pArg = argv[iArg];

        if (('-' == pArg[0]) || ('/' == pArg[0]))
        {
            pArg++;
            PWSTR pValue;

            for (pValue = pArg; *pValue && (':' != *pValue); pValue++);

            if (*pValue)
                *pValue++ = 0;

            uint32_t dwOption = LookupByName(pArg, g_pOptions);

            if (!dwOption || (dwOptions & (1 << dwOption)))
            {
                PrintUsage();
                return 1;
            }

            dwOptions |= 1 << dwOption;

            // Handle options with additional value parameter
            switch (dwOption)
            {
            case OPT_FILTER:
            case OPT_FORMAT:
            case OPT_FILETYPE:
            case OPT_OUTPUTFILE:
            case OPT_TARGET_PIXELX:
            case OPT_TARGET_PIXELY:
            case OPT_DIFF_COLOR:
            case OPT_THRESHOLD:
            case OPT_FILELIST:
                if (!*pValue)
                {
                    if ((iArg + 1 >= argc))
                    {
                        PrintUsage();
                        return 1;
                    }

                    iArg++;
                    pValue = argv[iArg];
                }
                break;

            default:
                break;
            }

            switch (dwOption)
            {
            case OPT_FORMAT:
                if (dwCommand != CMD_DIFF)
                {
                    wprintf(L"-f only valid for use with diff command\n");
                    return 1;
                }
                else
                {
                    diffFormat = static_cast<DXGI_FORMAT>(LookupByName(pValue, g_pFormats));
                    if (!diffFormat)
                    {
                        diffFormat = static_cast<DXGI_FORMAT>(LookupByName(pValue, g_pFormatAliases));
                        if (!diffFormat)
                        {
                            wprintf(L"Invalid value specified with -f (%ls)\n", pValue);
                            return 1;
                        }
                    }
                }
                break;

            case OPT_FILTER:
                dwFilter = static_cast<TEX_FILTER_FLAGS>(LookupByName(pValue, g_pFilters));
                if (!dwFilter)
                {
                    wprintf(L"Invalid value specified with -if (%ls)\n", pValue);
                    return 1;
                }
                break;

            case OPT_OUTPUTFILE:
                if (dwCommand != CMD_DIFF)
                {
                    wprintf(L"-o only valid for use with diff command\n");
                    return 1;
                }
                else
                {
                    wcscpy_s(szOutputFile, MAX_PATH, pValue);

                    wchar_t ext[_MAX_EXT] = {};
                    _wsplitpath_s(szOutputFile, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT);

                    fileType = LookupByName(ext, g_pExtFileTypes);
                }
                break;

            case OPT_FILETYPE:
                if (dwCommand != CMD_DUMPDDS)
                {
                    wprintf(L"-ft only valid for use with dumpdds command\n");
                    return 1;
                }
                else
                {
                    fileType = LookupByName(pValue, g_pDumpFileTypes);
                    if (!fileType)
                    {
                        wprintf(L"Invalid value specified with -ft (%ls)\n", pValue);
                        wprintf(L"\n");
                        PrintUsage();
                        return 1;
                    }
                }
                break;

            case OPT_TARGET_PIXELX:
                if (dwCommand != CMD_DUMPBC)
                {
                    wprintf(L"-targetx only valid with dumpbc command\n");
                    return 1;
                }
                else if (swscanf_s(pValue, L"%d", &pixelx) != 1)
                {
                    wprintf(L"Invalid value for pixel x location (%ls)\n", pValue);
                    return 1;
                }
                break;

            case OPT_TARGET_PIXELY:
                if (dwCommand != CMD_DUMPBC)
                {
                    wprintf(L"-targety only valid with dumpbc command\n");
                    return 1;
                }
                else if (swscanf_s(pValue, L"%d", &pixely) != 1)
                {
                    wprintf(L"Invalid value for pixel y location (%ls)\n", pValue);
                    return 1;
                }
                break;

            case OPT_DIFF_COLOR:
                if (swscanf_s(pValue, L"%x", &diffColor) != 1)
                {
                    printf("Invalid value specified with -c (%ls)\n", pValue);
                    printf("\n");
                    PrintUsage();
                    return 1;
                }
                diffColor &= 0xFFFFFF;
                break;

            case OPT_THRESHOLD:
                if (swscanf_s(pValue, L"%f", &threshold) != 1)
                {
                    printf("Invalid value specified with -t (%ls)\n", pValue);
                    printf("\n");
                    PrintUsage();
                    return 1;
                }
                break;

            case OPT_FILELIST:
            {
                std::wifstream inFile(pValue);
                if (!inFile)
                {
                    wprintf(L"Error opening -flist file %ls\n", pValue);
                    return 1;
                }

                ProcessFileList(inFile, conversion);
            }
            break;

            default:
                break;
            }
        }
        else if (wcspbrk(pArg, L"?*") != nullptr)
        {
            size_t count = conversion.size();
            SearchForFiles(pArg, conversion, (dwOptions & (1 << OPT_RECURSIVE)) != 0);
            if (conversion.size() <= count)
            {
                wprintf(L"No matching files found for %ls\n", pArg);
                return 1;
            }
        }
        else
        {
            SConversion conv = {};
            wcscpy_s(conv.szSrc, MAX_PATH, pArg);

            conversion.push_back(conv);
        }
    }

    if (conversion.empty())
    {
        PrintUsage();
        return 0;
    }

    if (~dwOptions & (1 << OPT_NOLOGO))
        PrintLogo();

    switch (dwCommand)
    {
    case CMD_COMPARE:
    case CMD_DIFF:
        // --- Compare/Diff ------------------------------------------------------------
        if (conversion.size() != 2)
        {
            wprintf(L"ERROR: compare/diff needs exactly two images\n");
            return 1;
        }
        else
        {
            auto pImage1 = conversion.cbegin();

            wprintf(L"1: %ls", pImage1->szSrc);
            fflush(stdout);

            TexMetadata info1;
            std::unique_ptr<ScratchImage> image1;
            hr = LoadImage(pImage1->szSrc, dwOptions, dwFilter, info1, image1);
            if (FAILED(hr))
            {
                wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
                return 1;
            }

            auto pImage2 = conversion.cbegin();
            std::advance(pImage2, 1);

            wprintf(L"\n2: %ls", pImage2->szSrc);
            fflush(stdout);

            TexMetadata info2;
            std::unique_ptr<ScratchImage> image2;
            hr = LoadImage(pImage2->szSrc, dwOptions, dwFilter, info2, image2);
            if (FAILED(hr))
            {
                wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
                return 1;
            }

            wprintf(L"\n");
            fflush(stdout);

            if (info1.height != info2.height
                || info1.width != info2.width)
            {
                wprintf(L"ERROR: Can only compare/diff images of the same width & height\n");
                return 1;
            }

            if (dwCommand == CMD_DIFF)
            {
                if (!*szOutputFile)
                {
                    wchar_t ext[_MAX_EXT] = {};
                    wchar_t fname[_MAX_FNAME] = {};
                    _wsplitpath_s(pImage1->szSrc, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, ext, _MAX_EXT);
                    if (_wcsicmp(ext, L".bmp") == 0)
                    {
                        wprintf(L"ERROR: Need to specify output file via -o\n");
                        return 1;
                    }

                    _wmakepath_s(szOutputFile, nullptr, nullptr, fname, L".bmp");
                }

                if (image1->GetImageCount() > 1 || image2->GetImageCount() > 1)
                    wprintf(L"WARNING: ignoring all images but first one in each file\n");

                ScratchImage diffImage;
                hr = Difference(*image1->GetImage(0, 0, 0), *image2->GetImage(0, 0, 0), dwFilter, diffFormat, diffColor, threshold, diffImage);
                if (FAILED(hr))
                {
                    wprintf(L"Failed diffing images (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
                    return 1;
                }

                if (dwOptions & (1 << OPT_TOLOWER))
                {
                    (void)_wcslwr_s(szOutputFile);
                }

                if (~dwOptions & (1 << OPT_OVERWRITE))
                {
                    if (GetFileAttributesW(szOutputFile) != INVALID_FILE_ATTRIBUTES)
                    {
                        wprintf(L"\nERROR: Output file already exists, use -y to overwrite\n");
                        return 1;
                    }
                }

                hr = SaveImage(diffImage.GetImage(0, 0, 0), szOutputFile, fileType);
                if (FAILED(hr))
                {
                    wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
                    return 1;
                }

                wprintf(L"Difference %ls\n", szOutputFile);
            }
            else if ((info1.depth == 1
                && info1.arraySize == 1
                && info1.mipLevels == 1)
                || info1.depth != info2.depth
                || info1.arraySize != info2.arraySize
                || info1.mipLevels != info2.mipLevels
                || image1->GetImageCount() != image2->GetImageCount())
            {
                // Compare single image
                if (image1->GetImageCount() > 1 || image2->GetImageCount() > 1)
                    wprintf(L"WARNING: ignoring all images but first one in each file\n");

                float mse, mseV[4];
                hr = ComputeMSE(*image1->GetImage(0, 0, 0), *image2->GetImage(0, 0, 0), mse, mseV);
                if (FAILED(hr))
                {
                    wprintf(L"Failed comparing images (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
                    return 1;
                }

                wprintf(L"Result: %f (%f %f %f %f) PSNR %f dB\n", mse, mseV[0], mseV[1], mseV[2], mseV[3],
                    10.0 * log10(3.0 / (double(mseV[0]) + double(mseV[1]) + double(mseV[2]))));
            }
            else
            {
                // Compare all images
                float min_mse = FLT_MAX;
                float min_mseV[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };

                float max_mse = -FLT_MAX;
                float max_mseV[4] = { -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX };

                double sum_mse = 0;
                double sum_mseV[4] = { 0, 0, 0, 0 };

                size_t total_images = 0;

                if (info1.depth > 1)
                {
                    wprintf(L"Results by mip (%3zu) and slice (%3zu)\n\n", info1.mipLevels, info1.depth);

                    size_t depth = info1.depth;
                    for (size_t mip = 0; mip < info1.mipLevels; ++mip)
                    {
                        for (size_t slice = 0; slice < depth; ++slice)
                        {
                            const Image* img1 = image1->GetImage(mip, 0, slice);
                            const Image* img2 = image2->GetImage(mip, 0, slice);

                            if (!img1
                                || !img2
                                || img1->height != img2->height
                                || img1->width != img2->width)
                            {
                                wprintf(L"ERROR: Unexpected mismatch at slice %3zu, mip %3zu\n", slice, mip);
                                return 1;
                            }
                            else
                            {
                                float mse, mseV[4];
                                hr = ComputeMSE(*img1, *img2, mse, mseV);
                                if (FAILED(hr))
                                {
                                    wprintf(L"Failed comparing images at slice %3zu, mip %3zu (%08X%ls)\n", slice, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }

                                min_mse = std::min(min_mse, mse);
                                max_mse = std::max(max_mse, mse);
                                sum_mse += double(mse);

                                for (size_t j = 0; j < 4; ++j)
                                {
                                    min_mseV[j] = std::min(min_mseV[j], mseV[j]);
                                    max_mseV[j] = std::max(max_mseV[j], mseV[j]);
                                    sum_mseV[j] += double(mseV[j]);
                                }

                                ++total_images;

                                wprintf(L"[%3zu,%3zu]: %f (%f %f %f %f) PSNR %f dB\n", mip, slice, mse, mseV[0], mseV[1], mseV[2], mseV[3],
                                    10.0 * log10(3.0 / (double(mseV[0]) + double(mseV[1]) + double(mseV[2]))));
                            }
                        }

                        if (depth > 1)
                            depth >>= 1;
                    }
                }
                else
                {
                    wprintf(L"Results by item (%3zu) and mip (%3zu)\n\n", info1.arraySize, info1.mipLevels);

                    for (size_t item = 0; item < info1.arraySize; ++item)
                    {
                        for (size_t mip = 0; mip < info1.mipLevels; ++mip)
                        {
                            const Image* img1 = image1->GetImage(mip, item, 0);
                            const Image* img2 = image2->GetImage(mip, item, 0);

                            if (!img1
                                || !img2
                                || img1->height != img2->height
                                || img1->width != img2->width)
                            {
                                wprintf(L"ERROR: Unexpected mismatch at item %3zu, mip %3zu\n", item, mip);
                                return 1;
                            }
                            else
                            {
                                float mse, mseV[4];
                                hr = ComputeMSE(*img1, *img2, mse, mseV);
                                if (FAILED(hr))
                                {
                                    wprintf(L"Failed comparing images at item %3zu, mip %3zu (%08X%ls)\n", item, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }

                                min_mse = std::min(min_mse, mse);
                                max_mse = std::max(max_mse, mse);
                                sum_mse += double(mse);

                                for (size_t j = 0; j < 4; ++j)
                                {
                                    min_mseV[j] = std::min(min_mseV[j], mseV[j]);
                                    max_mseV[j] = std::max(max_mseV[j], mseV[j]);
                                    sum_mseV[j] += double(mseV[j]);
                                }

                                ++total_images;

                                wprintf(L"[%3zu,%3zu]: %f (%f %f %f %f) PSNR %f dB\n", item, mip, mse, mseV[0], mseV[1], mseV[2], mseV[3],
                                    10.0 * log10(3.0 / (double(mseV[0]) + double(mseV[1]) + double(mseV[2]))));
                            }
                        }
                    }
                }

                // Output multi-image stats
                if (total_images > 1)
                {
                    wprintf(L"\n    Minimum MSE: %f (%f %f %f %f) PSNR %f dB\n", min_mse, min_mseV[0], min_mseV[1], min_mseV[2], min_mseV[3],
                        10.0 * log10(3.0 / (double(min_mseV[0]) + double(min_mseV[1]) + double(min_mseV[2]))));
                    double total_mseV0 = sum_mseV[0] / double(total_images);
                    double total_mseV1 = sum_mseV[1] / double(total_images);
                    double total_mseV2 = sum_mseV[2] / double(total_images);
                    wprintf(L"    Average MSE: %f (%f %f %f %f) PSNR %f dB\n", sum_mse / double(total_images),
                        total_mseV0,
                        total_mseV1,
                        total_mseV2,
                        sum_mseV[3] / double(total_images),
                        10.0 * log10(3.0 / (total_mseV0 + total_mseV1 + total_mseV2)));
                    wprintf(L"    Maximum MSE: %f (%f %f %f %f) PSNR %f dB\n", max_mse, max_mseV[0], max_mseV[1], max_mseV[2], max_mseV[3],
                        10.0 * log10(3.0 / (double(max_mseV[0]) + double(max_mseV[1]) + double(max_mseV[2]))));
                }
            }
        }
        break;

    default:
        for (auto pConv = conversion.cbegin(); pConv != conversion.cend(); ++pConv)
        {
            // Load source image
            if (pConv != conversion.begin())
                wprintf(L"\n");

            wprintf(L"%ls", pConv->szSrc);
            fflush(stdout);

            TexMetadata info;
            std::unique_ptr<ScratchImage> image;
            hr = LoadImage(pConv->szSrc, dwOptions, dwFilter, info, image);
            if (FAILED(hr))
            {
                wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
                return 1;
            }

            wprintf(L"\n");
            fflush(stdout);

            if (dwCommand == CMD_INFO)
            {
                // --- Info ----------------------------------------------------------------
                wprintf(L"        width = %zu\n", info.width);
                wprintf(L"       height = %zu\n", info.height);
                wprintf(L"        depth = %zu\n", info.depth);
                wprintf(L"    mipLevels = %zu\n", info.mipLevels);
                wprintf(L"    arraySize = %zu\n", info.arraySize);
                wprintf(L"       format = ");
                PrintFormat(info.format);
                wprintf(L"\n    dimension = ");
                switch (info.dimension)
                {
                case TEX_DIMENSION_TEXTURE1D:
                    wprintf(L"%ls", (info.arraySize > 1) ? L"1DArray\n" : L"1D\n");
                    break;

                case TEX_DIMENSION_TEXTURE2D:
                    if (info.IsCubemap())
                    {
                        wprintf(L"%ls", (info.arraySize > 6) ? L"CubeArray\n" : L"Cube\n");
                    }
                    else
                    {
                        wprintf(L"%ls", (info.arraySize > 1) ? L"2DArray\n" : L"2D\n");
                    }
                    break;

                case TEX_DIMENSION_TEXTURE3D:
                    wprintf(L" 3D");
                    break;
                }

                wprintf(L"   alpha mode = ");
                switch (info.GetAlphaMode())
                {
                case TEX_ALPHA_MODE_OPAQUE:
                    wprintf(L"Opaque");
                    break;
                case TEX_ALPHA_MODE_PREMULTIPLIED:
                    wprintf(L"Premultiplied");
                    break;
                case TEX_ALPHA_MODE_STRAIGHT:
                    wprintf(L"Straight");
                    break;
                case TEX_ALPHA_MODE_CUSTOM:
                    wprintf(L"Custom");
                    break;
                case TEX_ALPHA_MODE_UNKNOWN:
                    wprintf(L"Unknown");
                    break;
                }

                wprintf(L"\n       images = %zu\n", image->GetImageCount());

                auto sizeInKb = static_cast<uint32_t>(image->GetPixelsSize() / 1024);

                wprintf(L"   pixel size = %u (KB)\n\n", sizeInKb);
            }
            else if (dwCommand == CMD_DUMPDDS)
            {
                // --- Dump DDS ------------------------------------------------------------
                if (IsCompressed(info.format))
                {
                    wprintf(L"ERROR: dumpdds only operates on non-compressed format DDS files\n");
                    return 1;
                }

                wchar_t ext[_MAX_EXT] = {};
                wchar_t fname[_MAX_FNAME] = {};
                _wsplitpath_s(pConv->szSrc, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, nullptr, 0);

                wcscpy_s(ext, LookupByValue(fileType, g_pDumpFileTypes));

                if (info.depth > 1)
                {
                    wprintf(L"Writing by mip (%3zu) and slice (%3zu)...", info.mipLevels, info.depth);

                    size_t depth = info.depth;
                    for (size_t mip = 0; mip < info.mipLevels; ++mip)
                    {
                        for (size_t slice = 0; slice < depth; ++slice)
                        {
                            const Image* img = image->GetImage(mip, 0, slice);

                            if (!img)
                            {
                                wprintf(L"ERROR: Unexpected error at slice %3zu, mip %3zu\n", slice, mip);
                                return 1;
                            }
                            else
                            {
                                wchar_t subFname[_MAX_FNAME] = {};
                                if (info.mipLevels > 1)
                                {
                                    swprintf_s(subFname, L"%ls_slice%03zu_mip%03zu", fname, slice, mip);
                                }
                                else
                                {
                                    swprintf_s(subFname, L"%ls_slice%03zu", fname, slice);
                                }

                                _wmakepath_s(szOutputFile, nullptr, nullptr, subFname, ext);

                                hr = SaveImage(img, szOutputFile, fileType);
                                if (FAILED(hr))
                                {
                                    wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }
                            }
                        }

                        if (depth > 1)
                            depth >>= 1;
                    }
                    wprintf(L"\n");
                }
                else
                {
                    wprintf(L"Writing by item (%3zu) and mip (%3zu)...", info.arraySize, info.mipLevels);

                    for (size_t item = 0; item < info.arraySize; ++item)
                    {
                        for (size_t mip = 0; mip < info.mipLevels; ++mip)
                        {
                            const Image* img = image->GetImage(mip, item, 0);

                            if (!img)
                            {
                                wprintf(L"ERROR: Unexpected error at item %3zu, mip %3zu\n", item, mip);
                                return 1;
                            }
                            else
                            {
                                wchar_t subFname[_MAX_FNAME];
                                if (info.mipLevels > 1)
                                {
                                    swprintf_s(subFname, L"%ls_item%03zu_mip%03zu", fname, item, mip);
                                }
                                else
                                {
                                    swprintf_s(subFname, L"%ls_item%03zu", fname, item);
                                }

                                _wmakepath_s(szOutputFile, nullptr, nullptr, subFname, ext);

                                hr = SaveImage(img, szOutputFile, fileType);
                                if (FAILED(hr))
                                {
                                    wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }
                            }
                        }
                    }

                    wprintf(L"\n");
                }
            }
            else if (dwCommand == CMD_DUMPBC)
            {
                // --- Dump BC -------------------------------------------------------------
                if (!IsCompressed(info.format))
                {
                    wprintf(L"ERROR: dumpbc only operates on BC format DDS files\n");
                    return 1;
                }

                if (pixelx >= int(info.width)
                    || pixely >= int(info.height))
                {
                    wprintf(L"WARNING: Specified pixel location (%d x %d) is out of range for image (%zu x %zu)\n", pixelx, pixely, info.width, info.height);
                    continue;
                }

                wprintf(L"Compression: ");
                PrintFormat(info.format);
                wprintf(L"\n");

                if (info.depth > 1)
                {
                    wprintf(L"Results by mip (%3zu) and slice (%3zu)\n", info.mipLevels, info.depth);

                    size_t depth = info.depth;
                    for (size_t mip = 0; mip < info.mipLevels; ++mip)
                    {
                        for (size_t slice = 0; slice < depth; ++slice)
                        {
                            const Image* img = image->GetImage(mip, 0, slice);

                            if (!img)
                            {
                                wprintf(L"ERROR: Unexpected error at slice %3zu, mip %3zu\n", slice, mip);
                                return 1;
                            }
                            else
                            {
                                wprintf(L"\n[%3zu, %3zu]:\n", mip, slice);

                                hr = DumpBCImage(*img, pixelx, pixely);
                                if (FAILED(hr))
                                {
                                    wprintf(L"ERROR: Failed dumping image at slice %3zu, mip %3zu (%08X%ls)\n", slice, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }
                            }
                        }

                        if (depth > 1)
                            depth >>= 1;

                        if (pixelx > 0)
                            pixelx >>= 1;

                        if (pixely > 0)
                            pixely >>= 1;
                    }
                }
                else
                {
                    wprintf(L"Results by item (%3zu) and mip (%3zu)\n", info.arraySize, info.mipLevels);

                    for (size_t item = 0; item < info.arraySize; ++item)
                    {
                        int tpixelx = pixelx;
                        int tpixely = pixely;

                        for (size_t mip = 0; mip < info.mipLevels; ++mip)
                        {
                            const Image* img = image->GetImage(mip, item, 0);

                            if (!img)
                            {
                                wprintf(L"ERROR: Unexpected error at item %3zu, mip %3zu\n", item, mip);
                                return 1;
                            }
                            else
                            {
                                if (image->GetImageCount() > 1)
                                {
                                    wprintf(L"\n[%3zu, %3zu]:\n", item, mip);
                                }
                                hr = DumpBCImage(*img, tpixelx, tpixely);
                                if (FAILED(hr))
                                {
                                    wprintf(L"ERROR: Failed dumping image at item %3zu, mip %3zu (%08X%ls)\n", item, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }
                            }

                            if (tpixelx > 0)
                                tpixelx >>= 1;

                            if (tpixely > 0)
                                tpixely >>= 1;
                        }
                    }
                }
            }
            else
            {
                // --- Analyze -------------------------------------------------------------
                if (IsPlanar(info.format))
                {
                    auto img = image->GetImage(0, 0, 0);
                    assert(img);
                    size_t nimg = image->GetImageCount();

                    std::unique_ptr<ScratchImage> timage(new (std::nothrow) ScratchImage);
                    if (!timage)
                    {
                        wprintf(L"\nERROR: Memory allocation failed\n");
                        return 1;
                    }

                    hr = ConvertToSinglePlane(img, nimg, info, *timage);
                    if (FAILED(hr))
                    {
                        wprintf(L" FAILED [converttosingleplane] (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
                        return 1;
                    }

                    auto& tinfo = timage->GetMetadata();

                    info.format = tinfo.format;

                    assert(info.width == tinfo.width);
                    assert(info.height == tinfo.height);
                    assert(info.depth == tinfo.depth);
                    assert(info.arraySize == tinfo.arraySize);
                    assert(info.mipLevels == tinfo.mipLevels);
                    assert(info.miscFlags == tinfo.miscFlags);
                    assert(info.dimension == tinfo.dimension);

                    image.swap(timage);
                }

                if (info.depth > 1)
                {
                    wprintf(L"Results by mip (%3zu) and slice (%3zu)\n\n", info.mipLevels, info.depth);

                    size_t depth = info.depth;
                    for (size_t mip = 0; mip < info.mipLevels; ++mip)
                    {
                        for (size_t slice = 0; slice < depth; ++slice)
                        {
                            const Image* img = image->GetImage(mip, 0, slice);

                            if (!img)
                            {
                                wprintf(L"ERROR: Unexpected error at slice %3zu, mip %3zu\n", slice, mip);
                                return 1;
                            }
                            else
                            {
                                AnalyzeData data;
                                hr = Analyze(*img, data);
                                if (FAILED(hr))
                                {
                                    wprintf(L"ERROR: Failed analyzing image at slice %3zu, mip %3zu (%08X%ls)\n", slice, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }

                                wprintf(L"Result slice %3zu, mip %3zu:\n", slice, mip);
                                data.Print();
                            }

                            if (IsCompressed(info.format))
                            {
                                AnalyzeBCData data;
                                hr = AnalyzeBC(*img, data);
                                if (FAILED(hr))
                                {
                                    wprintf(L"ERROR: Failed analyzing BC image at slice %3zu, mip %3zu (%08X%ls)\n", slice, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }

                                data.Print(img->format);
                            }
                            wprintf(L"\n");
                        }

                        if (depth > 1)
                            depth >>= 1;
                    }
                }
                else
                {
                    wprintf(L"Results by item (%3zu) and mip (%3zu)\n\n", info.arraySize, info.mipLevels);

                    for (size_t item = 0; item < info.arraySize; ++item)
                    {
                        for (size_t mip = 0; mip < info.mipLevels; ++mip)
                        {
                            const Image* img = image->GetImage(mip, item, 0);

                            if (!img)
                            {
                                wprintf(L"ERROR: Unexpected error at item %3zu, mip %3zu\n", item, mip);
                                return 1;
                            }
                            else
                            {
                                AnalyzeData data;
                                hr = Analyze(*img, data);
                                if (FAILED(hr))
                                {
                                    wprintf(L"ERROR: Failed analyzing image at item %3zu, mip %3zu (%08X%ls)\n", item, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }

                                if (image->GetImageCount() > 1)
                                {
                                    wprintf(L"Result item %3zu, mip %3zu:\n", item, mip);
                                }
                                data.Print();
                            }

                            if (IsCompressed(info.format))
                            {
                                AnalyzeBCData data;
                                hr = AnalyzeBC(*img, data);
                                if (FAILED(hr))
                                {
                                    wprintf(L"ERROR: Failed analyzing BC image at item %3zu, mip %3zu (%08X%ls)\n", item, mip, static_cast<unsigned int>(hr), GetErrorDesc(hr));
                                    return 1;
                                }

                                data.Print(img->format);
                            }
                            wprintf(L"\n");
                        }
                    }
                }
            }
        }
        break;
    }

    return 0;
}
