text: //--------------------------------------------------------------------------------------
  // File: DDSTextureLoader.cpp
  //
  // Functions for loading a DDS texture and creating a Direct3D 11 runtime resource for it
  //
  // Note these functions are useful as a light-weight runtime loader for DDS files. For
  // a full-featured DDS file reader, writer, and texture processing pipeline see
  // the 'Texconv' sample and the 'DirectXTex' library.
  //
  // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  // PARTICULAR PURPOSE.
  //
  // Copyright (c) Microsoft Corporation. All rights reserved.
  //
  // http://go.microsoft.com/fwlink/?LinkId=248926
  // http://go.microsoft.com/fwlink/?LinkId=248929
  //--------------------------------------------------------------------------------------
  
  #include <assert.h>
  #include <algorithm>
  #include <memory>
  
  #include "DDSTextureLoader.h"
  
  #if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) )
  #pragma comment(lib,"dxguid.lib")
  #endif
  
  using namespace DirectX;
  
  //--------------------------------------------------------------------------------------
  // Macros
  //--------------------------------------------------------------------------------------
  #ifndef MAKEFOURCC
  #define MAKEFOURCC(ch0, ch1, ch2, ch3)                              \
                  ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) |       \
                  ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 ))
  #endif /* defined(MAKEFOURCC) */
  
  //--------------------------------------------------------------------------------------
  // DDS file structure definitions
  //
  // See DDS.h in the 'Texconv' sample and the 'DirectXTex' library
  //--------------------------------------------------------------------------------------
  #pragma pack(push,1)
  
  const uint32_t DDS_MAGIC = 0x20534444; // "DDS "
  
  struct DDS_PIXELFORMAT
  {
  	uint32_t    size;
  	uint32_t    flags;
  	uint32_t    fourCC;
  	uint32_t    RGBBitCount;
  	uint32_t    RBitMask;
  	uint32_t    GBitMask;
  	uint32_t    BBitMask;
  	uint32_t    ABitMask;
  };
  
  #define DDS_FOURCC      0x00000004  // DDPF_FOURCC
  #define DDS_RGB         0x00000040  // DDPF_RGB
  #define DDS_LUMINANCE   0x00020000  // DDPF_LUMINANCE
  #define DDS_ALPHA       0x00000002  // DDPF_ALPHA
  
  #define DDS_HEADER_FLAGS_VOLUME         0x00800000  // DDSD_DEPTH
  
  #define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT
  #define DDS_WIDTH  0x00000004 // DDSD_WIDTH
  
  #define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
  #define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
  #define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
  #define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
  #define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
  #define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
  
  #define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\
                                 DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\
                                 DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ )
  
  #define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP
  
  enum DDS_MISC_FLAGS2
  {
  	DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L,
  };
  
  struct DDS_HEADER
  {
  	uint32_t        size;
  	uint32_t        flags;
  	uint32_t        height;
  	uint32_t        width;
  	uint32_t        pitchOrLinearSize;
  	uint32_t        depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags
  	uint32_t        mipMapCount;
  	uint32_t        reserved1[11];
  	DDS_PIXELFORMAT ddspf;
  	uint32_t        caps;
  	uint32_t        caps2;
  	uint32_t        caps3;
  	uint32_t        caps4;
  	uint32_t        reserved2;
  };
  
  struct DDS_HEADER_DXT10
  {
  	DXGI_FORMAT     dxgiFormat;
  	uint32_t        resourceDimension;
  	uint32_t        miscFlag; // see D3D11_RESOURCE_MISC_FLAG
  	uint32_t        arraySize;
  	uint32_t        miscFlags2;
  };
  
  #pragma pack(pop)
  
  //--------------------------------------------------------------------------------------
  namespace
  {
  
  	struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } };
  
  	typedef public std::unique_ptr<void, handle_closer> ScopedHandle;
  
  	inline HANDLE safe_handle(HANDLE h) { return (h == INVALID_HANDLE_VALUE) ? 0 : h; }
  
  	template<UINT TNameLength>
  	inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_ const char(&name)[TNameLength])
  	{
  #if defined(_DEBUG) || defined(PROFILE)
  		resource->SetPrivateData(WKPDID_D3DDebugObjectName, TNameLength - 1, name);
  #else
  		UNREFERENCED_PARAMETER(resource);
  		UNREFERENCED_PARAMETER(name);
  #endif
  	}
  
  };
  
  //--------------------------------------------------------------------------------------
  static HRESULT LoadTextureDataFromFile(_In_z_ const wchar_t* fileName,
  	std::unique_ptr<uint8_t[]>& ddsData,
  	DDS_HEADER** header,
  	uint8_t** bitData,
  	size_t* bitSize
  )
  {
  	if (!header || !bitData || !bitSize)
  	{
  		return E_POINTER;
  	}
  
  	// open the file
  #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
  	ScopedHandle hFile(safe_handle(CreateFile2(fileName,
  		GENERIC_READ,
  		FILE_SHARE_READ,
  		OPEN_EXISTING,
  		nullptr)));
  #else
  	ScopedHandle hFile(safe_handle(CreateFileW(fileName,
  		GENERIC_READ,
  		FILE_SHARE_READ,
  		nullptr,
  		OPEN_EXISTING,
  		FILE_ATTRIBUTE_NORMAL,
  		nullptr)));
  #endif
  
  	if (!hFile)
  	{
  		return HRESULT_FROM_WIN32(GetLastError());
  	}
  
  	// Get the file size
  	LARGE_INTEGER FileSize = { 0 };
  
  #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
  	FILE_STANDARD_INFO fileInfo;
  	if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
  	{
  		return HRESULT_FROM_WIN32(GetLastError());
  	}
  	FileSize = fileInfo.EndOfFile;
  #else
  	GetFileSizeEx(hFile.get(), &FileSize);
  #endif
  
  	// File is too big for 32-bit allocation, so reject read
  	if (FileSize.HighPart > 0)
  	{
  		return E_FAIL;
  	}
  
  	// Need at least enough data to fill the header and magic number to be a valid DDS
  	if (FileSize.LowPart < (sizeof(DDS_HEADER) + sizeof(uint32_t)))
  	{
  		return E_FAIL;
  	}
  
  	// create enough space for the file data
  	ddsData.reset(new (std::nothrow) uint8_t[FileSize.LowPart]);
  	if (!ddsData)
  	{
  		return E_OUTOFMEMORY;
  	}
  
  	// read the data in
  	DWORD BytesRead = 0;
  	if (!ReadFile(hFile.get(),
  		ddsData.get(),
  		FileSize.LowPart,
  		&BytesRead,
  		nullptr
  	))
  	{
  		return HRESULT_FROM_WIN32(GetLastError());
  	}
  
  	if (BytesRead < FileSize.LowPart)
  	{
  		return E_FAIL;
  	}
  
  	// DDS files always start with the same magic number ("DDS ")
  	uint32_t dwMagicNumber = *(const uint32_t*)(ddsData.get());
  	if (dwMagicNumber != DDS_MAGIC)
  	{
  		return E_FAIL;
  	}
  
  	auto hdr = reinterpret_cast<DDS_HEADER*>(ddsData.get() + sizeof(uint32_t));
  
  	// Verify header to validate DDS file
  	if (hdr->size != sizeof(DDS_HEADER) ||
  		hdr->ddspf.size != sizeof(DDS_PIXELFORMAT))
  	{
  		return E_FAIL;
  	}
  
  	// Check for DX10 extension
  	bool bDXT10Header = false;
  	if ((hdr->ddspf.flags & DDS_FOURCC) &&
  		(MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC))
  	{
  		// Must be long enough for both headers and magic value
  		if (FileSize.LowPart < (sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10)))
  		{
  			return E_FAIL;
  		}
  
  		bDXT10Header = true;
  	}
  
  	// setup the pointers in the process request
  	*header = hdr;
  	ptrdiff_t offset = sizeof(uint32_t) + sizeof(DDS_HEADER)
  		+ (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0);
  	*bitData = ddsData.get() + offset;
  	*bitSize = FileSize.LowPart - offset;
  
  	return S_OK;
  }
  
  
  //--------------------------------------------------------------------------------------
  // Return the BPP for a particular format
  //--------------------------------------------------------------------------------------
  static size_t BitsPerPixel(_In_ DXGI_FORMAT fmt)
  {
  	switch (fmt)
  	{
  	case DXGI_FORMAT_R32G32B32A32_TYPELESS:
  	case DXGI_FORMAT_R32G32B32A32_FLOAT:
  	case DXGI_FORMAT_R32G32B32A32_UINT:
  	case DXGI_FORMAT_R32G32B32A32_SINT:
  		return 128;
  
  	case DXGI_FORMAT_R32G32B32_TYPELESS:
  	case DXGI_FORMAT_R32G32B32_FLOAT:
  	case DXGI_FORMAT_R32G32B32_UINT:
  	case DXGI_FORMAT_R32G32B32_SINT:
  		return 96;
  
  	case DXGI_FORMAT_R16G16B16A16_TYPELESS:
  	case DXGI_FORMAT_R16G16B16A16_FLOAT:
  	case DXGI_FORMAT_R16G16B16A16_UNORM:
  	case DXGI_FORMAT_R16G16B16A16_UINT:
  	case DXGI_FORMAT_R16G16B16A16_SNORM:
  	case DXGI_FORMAT_R16G16B16A16_SINT:
  	case DXGI_FORMAT_R32G32_TYPELESS:
  	case DXGI_FORMAT_R32G32_FLOAT:
  	case DXGI_FORMAT_R32G32_UINT:
  	case DXGI_FORMAT_R32G32_SINT:
  	case DXGI_FORMAT_R32G8X24_TYPELESS:
  	case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
  	case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
  	case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
  	case DXGI_FORMAT_Y416:
  	case DXGI_FORMAT_Y210:
  	case DXGI_FORMAT_Y216:
  		return 64;
  
  	case DXGI_FORMAT_R10G10B10A2_TYPELESS:
  	case DXGI_FORMAT_R10G10B10A2_UNORM:
  	case DXGI_FORMAT_R10G10B10A2_UINT:
  	case DXGI_FORMAT_R11G11B10_FLOAT:
  	case DXGI_FORMAT_R8G8B8A8_TYPELESS:
  	case DXGI_FORMAT_R8G8B8A8_UNORM:
  	case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
  	case DXGI_FORMAT_R8G8B8A8_UINT:
  	case DXGI_FORMAT_R8G8B8A8_SNORM:
  	case DXGI_FORMAT_R8G8B8A8_SINT:
  	case DXGI_FORMAT_R16G16_TYPELESS:
  	case DXGI_FORMAT_R16G16_FLOAT:
  	case DXGI_FORMAT_R16G16_UNORM:
  	case DXGI_FORMAT_R16G16_UINT:
  	case DXGI_FORMAT_R16G16_SNORM:
  	case DXGI_FORMAT_R16G16_SINT:
  	case DXGI_FORMAT_R32_TYPELESS:
  	case DXGI_FORMAT_D32_FLOAT:
  	case DXGI_FORMAT_R32_FLOAT:
  	case DXGI_FORMAT_R32_UINT:
  	case DXGI_FORMAT_R32_SINT:
  	case DXGI_FORMAT_R24G8_TYPELESS:
  	case DXGI_FORMAT_D24_UNORM_S8_UINT:
  	case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
  	case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
  	case DXGI_FORMAT_R9G9B9E5_SHAREDEXP:
  	case DXGI_FORMAT_R8G8_B8G8_UNORM:
  	case DXGI_FORMAT_G8R8_G8B8_UNORM:
  	case DXGI_FORMAT_B8G8R8A8_UNORM:
  	case DXGI_FORMAT_B8G8R8X8_UNORM:
  	case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
  	case DXGI_FORMAT_B8G8R8A8_TYPELESS:
  	case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
  	case DXGI_FORMAT_B8G8R8X8_TYPELESS:
  	case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
  	case DXGI_FORMAT_AYUV:
  	case DXGI_FORMAT_Y410:
  	case DXGI_FORMAT_YUY2:
  		return 32;
  
  	case DXGI_FORMAT_P010:
  	case DXGI_FORMAT_P016:
  		return 24;
  
  	case DXGI_FORMAT_R8G8_TYPELESS:
  	case DXGI_FORMAT_R8G8_UNORM:
  	case DXGI_FORMAT_R8G8_UINT:
  	case DXGI_FORMAT_R8G8_SNORM:
  	case DXGI_FORMAT_R8G8_SINT:
  	case DXGI_FORMAT_R16_TYPELESS:
  	case DXGI_FORMAT_R16_FLOAT:
  	case DXGI_FORMAT_D16_UNORM:
  	case DXGI_FORMAT_R16_UNORM:
  	case DXGI_FORMAT_R16_UINT:
  	case DXGI_FORMAT_R16_SNORM:
  	case DXGI_FORMAT_R16_SINT:
  	case DXGI_FORMAT_B5G6R5_UNORM:
  	case DXGI_FORMAT_B5G5R5A1_UNORM:
  	case DXGI_FORMAT_A8P8:
  	case DXGI_FORMAT_B4G4R4A4_UNORM:
  		return 16;
  
  	case DXGI_FORMAT_NV12:
  	case DXGI_FORMAT_420_OPAQUE:
  	case DXGI_FORMAT_NV11:
  		return 12;
  
  	case DXGI_FORMAT_R8_TYPELESS:
  	case DXGI_FORMAT_R8_UNORM:
  	case DXGI_FORMAT_R8_UINT:
  	case DXGI_FORMAT_R8_SNORM:
  	case DXGI_FORMAT_R8_SINT:
  	case DXGI_FORMAT_A8_UNORM:
  	case DXGI_FORMAT_AI44:
  	case DXGI_FORMAT_IA44:
  	case DXGI_FORMAT_P8:
  		return 8;
  
  	case DXGI_FORMAT_R1_UNORM:
  		return 1;
  
  	case DXGI_FORMAT_BC1_TYPELESS:
  	case DXGI_FORMAT_BC1_UNORM:
  	case DXGI_FORMAT_BC1_UNORM_SRGB:
  	case DXGI_FORMAT_BC4_TYPELESS:
  	case DXGI_FORMAT_BC4_UNORM:
  	case DXGI_FORMAT_BC4_SNORM:
  		return 4;
  
  	case DXGI_FORMAT_BC2_TYPELESS:
  	case DXGI_FORMAT_BC2_UNORM:
  	case DXGI_FORMAT_BC2_UNORM_SRGB:
  	case DXGI_FORMAT_BC3_TYPELESS:
  	case DXGI_FORMAT_BC3_UNORM:
  	case DXGI_FORMAT_BC3_UNORM_SRGB:
  	case DXGI_FORMAT_BC5_TYPELESS:
  	case DXGI_FORMAT_BC5_UNORM:
  	case DXGI_FORMAT_BC5_SNORM:
  	case DXGI_FORMAT_BC6H_TYPELESS:
  	case DXGI_FORMAT_BC6H_UF16:
  	case DXGI_FORMAT_BC6H_SF16:
  	case DXGI_FORMAT_BC7_TYPELESS:
  	case DXGI_FORMAT_BC7_UNORM:
  	case DXGI_FORMAT_BC7_UNORM_SRGB:
  		return 8;
  
  #if defined(_XBOX_ONE) && defined(_TITLE)
  
  	case DXGI_FORMAT_R10G10B10_7E3_A2_FLOAT:
  	case DXGI_FORMAT_R10G10B10_6E4_A2_FLOAT:
  		return 32;
  
  	case DXGI_FORMAT_D16_UNORM_S8_UINT:
  	case DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
  	case DXGI_FORMAT_X16_TYPELESS_G8_UINT:
  		return 24;
  
  #endif // _XBOX_ONE && _TITLE
  
  	default:
  		return 0;
  	}
  }
  
  
  //--------------------------------------------------------------------------------------
  // Get surface information for a particular format
  //--------------------------------------------------------------------------------------
  static void GetSurfaceInfo(_In_ size_t width,
  	_In_ size_t height,
  	_In_ DXGI_FORMAT fmt,
  	_Out_opt_ size_t* outNumBytes,
  	_Out_opt_ size_t* outRowBytes,
  	_Out_opt_ size_t* outNumRows)
  {
  	size_t numBytes = 0;
  	size_t rowBytes = 0;
  	size_t numRows = 0;
  
  	bool bc = false;
  	bool packed = false;
  	bool planar = false;
  	size_t bpe = 0;
  	switch (fmt)
  	{
  	case DXGI_FORMAT_BC1_TYPELESS:
  	case DXGI_FORMAT_BC1_UNORM:
  	case DXGI_FORMAT_BC1_UNORM_SRGB:
  	case DXGI_FORMAT_BC4_TYPELESS:
  	case DXGI_FORMAT_BC4_UNORM:
  	case DXGI_FORMAT_BC4_SNORM:
  		bc = true;
  		bpe = 8;
  		break;
  
  	case DXGI_FORMAT_BC2_TYPELESS:
  	case DXGI_FORMAT_BC2_UNORM:
  	case DXGI_FORMAT_BC2_UNORM_SRGB:
  	case DXGI_FORMAT_BC3_TYPELESS:
  	case DXGI_FORMAT_BC3_UNORM:
  	case DXGI_FORMAT_BC3_UNORM_SRGB:
  	case DXGI_FORMAT_BC5_TYPELESS:
  	case DXGI_FORMAT_BC5_UNORM:
  	case DXGI_FORMAT_BC5_SNORM:
  	case DXGI_FORMAT_BC6H_TYPELESS:
  	case DXGI_FORMAT_BC6H_UF16:
  	case DXGI_FORMAT_BC6H_SF16:
  	case DXGI_FORMAT_BC7_TYPELESS:
  	case DXGI_FORMAT_BC7_UNORM:
  	case DXGI_FORMAT_BC7_UNORM_SRGB:
  		bc = true;
  		bpe = 16;
  		break;
  
  	case DXGI_FORMAT_R8G8_B8G8_UNORM:
  	case DXGI_FORMAT_G8R8_G8B8_UNORM:
  	case DXGI_FORMAT_YUY2:
  		packed = true;
  		bpe = 4;
  		break;
  
  	case DXGI_FORMAT_Y210:
  	case DXGI_FORMAT_Y216:
  		packed = true;
  		bpe = 8;
  		break;
  
  	case DXGI_FORMAT_NV12:
  	case DXGI_FORMAT_420_OPAQUE:
  		planar = true;
  		bpe = 2;
  		break;
  
  	case DXGI_FORMAT_P010:
  	case DXGI_FORMAT_P016:
  		planar = true;
  		bpe = 4;
  		break;
  
  #if defined(_XBOX_ONE) && defined(_TITLE)
  
  	case DXGI_FORMAT_D16_UNORM_S8_UINT:
  	case DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
  	case DXGI_FORMAT_X16_TYPELESS_G8_UINT:
  		planar = true;
  		bpe = 4;
  		break;
  
  #endif
  	}
  
  	if (bc)
  	{
  		size_t numBlocksWide = 0;
  		if (width > 0)
  		{
  			numBlocksWide = std::max<size_t>(1, (width + 3) / 4);
  		}
  		size_t numBlocksHigh = 0;
  		if (height > 0)
  		{
  			numBlocksHigh = std::max<size_t>(1, (height + 3) / 4);
  		}
  		rowBytes = numBlocksWide * bpe;
  		numRows = numBlocksHigh;
  		numBytes = rowBytes * numBlocksHigh;
  	}
  	else if (packed)
  	{
  		rowBytes = ((width + 1) >> 1) * bpe;
  		numRows = height;
  		numBytes = rowBytes * height;
  	}
  	else if (fmt == DXGI_FORMAT_NV11)
  	{
  		rowBytes = ((width + 3) >> 2) * 4;
  		numRows = height * 2; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data
  		numBytes = rowBytes * numRows;
  	}
  	else if (planar)
  	{
  		rowBytes = ((width + 1) >> 1) * bpe;
  		numBytes = (rowBytes * height) + ((rowBytes * height + 1) >> 1);
  		numRows = height + ((height + 1) >> 1);
  	}
  	else
  	{
  		size_t bpp = BitsPerPixel(fmt);
  		rowBytes = (width * bpp + 7) / 8; // round up to nearest byte
  		numRows = height;
  		numBytes = rowBytes * height;
  	}
  
  	if (outNumBytes)
  	{
  		*outNumBytes = numBytes;
  	}
  	if (outRowBytes)
  	{
  		*outRowBytes = rowBytes;
  	}
  	if (outNumRows)
  	{
  		*outNumRows = numRows;
  	}
  }
  
  
  //--------------------------------------------------------------------------------------
  #define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a )
  
  static DXGI_FORMAT GetDXGIFormat(const DDS_PIXELFORMAT& ddpf)
  {
  	if (ddpf.flags & DDS_RGB)
  	{
  		// Note that sRGB formats are written using the "DX10" extended header
  
  		switch (ddpf.RGBBitCount)
  		{
  		case 32:
  			if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000))
  			{
  				return DXGI_FORMAT_R8G8B8A8_UNORM;
  			}
  
  			if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000))
  			{
  				return DXGI_FORMAT_B8G8R8A8_UNORM;
  			}
  
  			if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000))
  			{
  				return DXGI_FORMAT_B8G8R8X8_UNORM;
  			}
  
  			// No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0x00000000) aka D3DFMT_X8B8G8R8
  
  			// Note that many common DDS reader/writers (including D3DX) swap the
  			// the RED/BLUE masks for 10:10:10:2 formats. We assumme
  			// below that the 'backwards' header mask is being used since it is most
  			// likely written by D3DX. The more robust solution is to use the 'DX10'
  			// header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly
  
  			// For 'correct' writers, this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data
  			if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000))
  			{
  				return DXGI_FORMAT_R10G10B10A2_UNORM;
  			}
  
  			// No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10
  
  			if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000))
  			{
  				return DXGI_FORMAT_R16G16_UNORM;
  			}
  
  			if (ISBITMASK(0xffffffff, 0x00000000, 0x00000000, 0x00000000))
  			{
  				// Only 32-bit color channel format in D3D9 was R32F
  				return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114
  			}
  			break;
  
  		case 24:
  			// No 24bpp DXGI formats aka D3DFMT_R8G8B8
  			break;
  
  		case 16:
  			if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x8000))
  			{
  				return DXGI_FORMAT_B5G5R5A1_UNORM;
  			}
  			if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0x0000))
  			{
  				return DXGI_FORMAT_B5G6R5_UNORM;
  			}
  
  			// No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0x0000) aka D3DFMT_X1R5G5B5
  
  			if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000))
  			{
  				return DXGI_FORMAT_B4G4R4A4_UNORM;
  			}
  
  			// No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0x0000) aka D3DFMT_X4R4G4B4
  
  			// No 3:3:2, 3:3:2:8, or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8, etc.
  			break;
  		}
  	}
  	else if (ddpf.flags & DDS_LUMINANCE)
  	{
  		if (8 == ddpf.RGBBitCount)
  		{
  			if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x00000000))
  			{
  				return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension
  			}
  
  			// No DXGI format maps to ISBITMASK(0x0f,0x00,0x00,0xf0) aka D3DFMT_A4L4
  		}
  
  		if (16 == ddpf.RGBBitCount)
  		{
  			if (ISBITMASK(0x0000ffff, 0x00000000, 0x00000000, 0x00000000))
  			{
  				return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension
  			}
  			if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x0000ff00))
  			{
  				return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension
  			}
  		}
  	}
  	else if (ddpf.flags & DDS_ALPHA)
  	{
  		if (8 == ddpf.RGBBitCount)
  		{
  			return DXGI_FORMAT_A8_UNORM;
  		}
  	}
  	else if (ddpf.flags & DDS_FOURCC)
  	{
  		if (MAKEFOURCC('D', 'X', 'T', '1') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC1_UNORM;
  		}
  		if (MAKEFOURCC('D', 'X', 'T', '3') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC2_UNORM;
  		}
  		if (MAKEFOURCC('D', 'X', 'T', '5') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC3_UNORM;
  		}
  
  		// While pre-mulitplied alpha isn't directly supported by the DXGI formats,
  		// they are basically the same as these BC formats so they can be mapped
  		if (MAKEFOURCC('D', 'X', 'T', '2') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC2_UNORM;
  		}
  		if (MAKEFOURCC('D', 'X', 'T', '4') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC3_UNORM;
  		}
  
  		if (MAKEFOURCC('A', 'T', 'I', '1') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC4_UNORM;
  		}
  		if (MAKEFOURCC('B', 'C', '4', 'U') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC4_UNORM;
  		}
  		if (MAKEFOURCC('B', 'C', '4', 'S') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC4_SNORM;
  		}
  
  		if (MAKEFOURCC('A', 'T', 'I', '2') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC5_UNORM;
  		}
  		if (MAKEFOURCC('B', 'C', '5', 'U') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC5_UNORM;
  		}
  		if (MAKEFOURCC('B', 'C', '5', 'S') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_BC5_SNORM;
  		}
  
  		// BC6H and BC7 are written using the "DX10" extended header
  
  		if (MAKEFOURCC('R', 'G', 'B', 'G') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_R8G8_B8G8_UNORM;
  		}
  		if (MAKEFOURCC('G', 'R', 'G', 'B') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_G8R8_G8B8_UNORM;
  		}
  
  		if (MAKEFOURCC('Y', 'U', 'Y', '2') == ddpf.fourCC)
  		{
  			return DXGI_FORMAT_YUY2;
  		}
  
  		// Check for D3DFORMAT enums being set here
  		switch (ddpf.fourCC)
  		{
  		case 36: // D3DFMT_A16B16G16R16
  			return DXGI_FORMAT_R16G16B16A16_UNORM;
  
  		case 110: // D3DFMT_Q16W16V16U16
  			return DXGI_FORMAT_R16G16B16A16_SNORM;
  
  		case 111: // D3DFMT_R16F
  			return DXGI_FORMAT_R16_FLOAT;
  
  		case 112: // D3DFMT_G16R16F
  			return DXGI_FORMAT_R16G16_FLOAT;
  
  		case 113: // D3DFMT_A16B16G16R16F
  			return DXGI_FORMAT_R16G16B16A16_FLOAT;
  
  		case 114: // D3DFMT_R32F
  			return DXGI_FORMAT_R32_FLOAT;
  
  		case 115: // D3DFMT_G32R32F
  			return DXGI_FORMAT_R32G32_FLOAT;
  
  		case 116: // D3DFMT_A32B32G32R32F
  			return DXGI_FORMAT_R32G32B32A32_FLOAT;
  		}
  	}
  
  	return DXGI_FORMAT_UNKNOWN;
  }
  
  
  //--------------------------------------------------------------------------------------
  static DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format)
  {
  	switch (format)
  	{
  	case DXGI_FORMAT_R8G8B8A8_UNORM:
  		return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
  
  	case DXGI_FORMAT_BC1_UNORM:
  		return DXGI_FORMAT_BC1_UNORM_SRGB;
  
  	case DXGI_FORMAT_BC2_UNORM:
  		return DXGI_FORMAT_BC2_UNORM_SRGB;
  
  	case DXGI_FORMAT_BC3_UNORM:
  		return DXGI_FORMAT_BC3_UNORM_SRGB;
  
  	case DXGI_FORMAT_B8G8R8A8_UNORM:
  		return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
  
  	case DXGI_FORMAT_B8G8R8X8_UNORM:
  		return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
  
  	case DXGI_FORMAT_BC7_UNORM:
  		return DXGI_FORMAT_BC7_UNORM_SRGB;
  
  	default:
  		return format;
  	}
  }
  
  
  //--------------------------------------------------------------------------------------
  static HRESULT FillInitData(_In_ size_t width,
  	_In_ size_t height,
  	_In_ size_t depth,
  	_In_ size_t mipCount,
  	_In_ size_t arraySize,
  	_In_ DXGI_FORMAT format,
  	_In_ size_t maxsize,
  	_In_ size_t bitSize,
  	_In_reads_bytes_(bitSize) const uint8_t* bitData,
  	_Out_ size_t& twidth,
  	_Out_ size_t& theight,
  	_Out_ size_t& tdepth,
  	_Out_ size_t& skipMip,
  	_Out_writes_(mipCount*arraySize) D3D11_SUBRESOURCE_DATA* initData)
  {
  	if (!bitData || !initData)
  	{
  		return E_POINTER;
  	}
  
  	skipMip = 0;
  	twidth = 0;
  	theight = 0;
  	tdepth = 0;
  
  	size_t NumBytes = 0;
  	size_t RowBytes = 0;
  	const uint8_t* pSrcBits = bitData;
  	const uint8_t* pEndBits = bitData + bitSize;
  
  	size_t index = 0;
  	for (size_t j = 0; j < arraySize; j++)
  	{
  		size_t w = width;
  		size_t h = height;
  		size_t d = depth;
  		for (size_t i = 0; i < mipCount; i++)
  		{
  			GetSurfaceInfo(w,
  				h,
  				format,
  				&NumBytes,
  				&RowBytes,
  				nullptr
  			);
  
  			if ((mipCount <= 1) || !maxsize || (w <= maxsize && h <= maxsize && d <= maxsize))
  			{
  				if (!twidth)
  				{
  					twidth = w;
  					theight = h;
  					tdepth = d;
  				}
  
  				assert(index < mipCount * arraySize);
  				_Analysis_assume_(index < mipCount * arraySize);
  				initData[index].pSysMem = (const void*)pSrcBits;
  				initData[index].SysMemPitch = static_cast<UINT>(RowBytes);
  				initData[index].SysMemSlicePitch = static_cast<UINT>(NumBytes);
  				++index;
  			}
  			else if (!j)
  			{
  				// Count number of skipped mipmaps (first item only)
  				++skipMip;
  			}
  
  			if (pSrcBits + (NumBytes*d) > pEndBits)
  			{
  				return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
  			}
  
  			pSrcBits += NumBytes * d;
  
  			w = w >> 1;
  			h = h >> 1;
  			d = d >> 1;
  			if (w == 0)
  			{
  				w = 1;
  			}
  			if (h == 0)
  			{
  				h = 1;
  			}
  			if (d == 0)
  			{
  				d = 1;
  			}
  		}
  	}
  
  	return (index > 0) ? S_OK : E_FAIL;
  }
  
  
  //--------------------------------------------------------------------------------------
  static HRESULT CreateD3DResources(_In_ ID3D11Device* d3dDevice,
  	_In_ uint32_t resDim,
  	_In_ size_t width,
  	_In_ size_t height,
  	_In_ size_t depth,
  	_In_ size_t mipCount,
  	_In_ size_t arraySize,
  	_In_ DXGI_FORMAT format,
  	_In_ D3D11_USAGE usage,
  	_In_ unsigned int bindFlags,
  	_In_ unsigned int cpuAccessFlags,
  	_In_ unsigned int miscFlags,
  	_In_ bool forceSRGB,
  	_In_ bool isCubeMap,
  	_In_reads_opt_(mipCount*arraySize) D3D11_SUBRESOURCE_DATA* initData,
  	_Outptr_opt_ ID3D11Resource** texture,
  	_Outptr_opt_ ID3D11ShaderResourceView** textureView)
  {
  	if (!d3dDevice)
  		return E_POINTER;
  
  	HRESULT hr = E_FAIL;
  
  	if (forceSRGB)
  	{
  		format = MakeSRGB(format);
  	}
  
  	switch (resDim)
  	{
  	case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
  	{
  		D3D11_TEXTURE1D_DESC desc;
  		desc.Width = static_cast<UINT>(width);
  		desc.MipLevels = static_cast<UINT>(mipCount);
  		desc.ArraySize = static_cast<UINT>(arraySize);
  		desc.Format = format;
  		desc.Usage = usage;
  		desc.BindFlags = bindFlags;
  		desc.CPUAccessFlags = cpuAccessFlags;
  		desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE;
  
  		ID3D11Texture1D* tex = nullptr;
  		hr = d3dDevice->CreateTexture1D(&desc,
  			initData,
  			&tex
  		);
  		if (SUCCEEDED(hr) && tex != 0)
  		{
  			if (textureView != 0)
  			{
  				D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
  				memset(&SRVDesc, 0, sizeof(SRVDesc));
  				SRVDesc.Format = format;
  
  				if (arraySize > 1)
  				{
  					SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY;
  					SRVDesc.Texture1DArray.MipLevels = (!mipCount) ? -1 : desc.MipLevels;
  					SRVDesc.Texture1DArray.ArraySize = static_cast<UINT>(arraySize);
  				}
  				else
  				{
  					SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
  					SRVDesc.Texture1D.MipLevels = (!mipCount) ? -1 : desc.MipLevels;
  				}
  
  				hr = d3dDevice->CreateShaderResourceView(tex,
  					&SRVDesc,
  					textureView
  				);
  				if (FAILED(hr))
  				{
  					tex->Release();
  					return hr;
  				}
  			}
  
  			if (texture != 0)
  			{
  				*texture = tex;
  			}
  			else
  			{
  				SetDebugObjectName(tex, "DDSTextureLoader");
  				tex->Release();
  			}
  		}
  	}
  	break;
  
  	case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
  	{
  		D3D11_TEXTURE2D_DESC desc;
  		desc.Width = static_cast<UINT>(width);
  		desc.Height = static_cast<UINT>(height);
  		desc.MipLevels = static_cast<UINT>(mipCount);
  		desc.ArraySize = static_cast<UINT>(arraySize);
  		desc.Format = format;
  		desc.SampleDesc.Count = 1;
  		desc.SampleDesc.Quality = 0;
  		desc.Usage = usage;
  		desc.BindFlags = bindFlags;
  		desc.CPUAccessFlags = cpuAccessFlags;
  		if (isCubeMap)
  		{
  			desc.MiscFlags = miscFlags | D3D11_RESOURCE_MISC_TEXTURECUBE;
  		}
  		else
  		{
  			desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE;
  		}
  
  		ID3D11Texture2D* tex = nullptr;
  		hr = d3dDevice->CreateTexture2D(&desc,
  			initData,
  			&tex
  		);
  		if (SUCCEEDED(hr) && tex != 0)
  		{
  			if (textureView != 0)
  			{
  				D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
  				memset(&SRVDesc, 0, sizeof(SRVDesc));
  				SRVDesc.Format = format;
  
  				if (isCubeMap)
  				{
  					if (arraySize > 6)
  					{
  						SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY;
  						SRVDesc.TextureCubeArray.MipLevels = (!mipCount) ? -1 : desc.MipLevels;
  
  						// Earlier we set arraySize to (NumCubes * 6)
  						SRVDesc.TextureCubeArray.NumCubes = static_cast<UINT>(arraySize / 6);
  					}
  					else
  					{
  						SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
  						SRVDesc.TextureCube.MipLevels = (!mipCount) ? -1 : desc.MipLevels;
  					}
  				}
  				else if (arraySize > 1)
  				{
  					SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
  					SRVDesc.Texture2DArray.MipLevels = (!mipCount) ? -1 : desc.MipLevels;
  					SRVDesc.Texture2DArray.ArraySize = static_cast<UINT>(arraySize);
  				}
  				else
  				{
  					SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
  					SRVDesc.Texture2D.MipLevels = (!mipCount) ? -1 : desc.MipLevels;
  				}
  
  				hr = d3dDevice->CreateShaderResourceView(tex,
  					&SRVDesc,
  					textureView
  				);
  				if (FAILED(hr))
  				{
  					tex->Release();
  					return hr;
  				}
  			}
  
  			if (texture != 0)
  			{
  				*texture = tex;
  			}
  			else
  			{
  				SetDebugObjectName(tex, "DDSTextureLoader");
  				tex->Release();
  			}
  		}
  	}
  	break;
  
  	case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
  	{
  		D3D11_TEXTURE3D_DESC desc;
  		desc.Width = static_cast<UINT>(width);
  		desc.Height = static_cast<UINT>(height);
  		desc.Depth = static_cast<UINT>(depth);
  		desc.MipLevels = static_cast<UINT>(mipCount);
  		desc.Format = format;
  		desc.Usage = usage;
  		desc.BindFlags = bindFlags;
  		desc.CPUAccessFlags = cpuAccessFlags;
  		desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE;
  
  		ID3D11Texture3D* tex = nullptr;
  		hr = d3dDevice->CreateTexture3D(&desc,
  			initData,
  			&tex
  		);
  		if (SUCCEEDED(hr) && tex != 0)
  		{
  			if (textureView != 0)
  			{
  				D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
  				memset(&SRVDesc, 0, sizeof(SRVDesc));
  				SRVDesc.Format = format;
  
  				SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
  				SRVDesc.Texture3D.MipLevels = (!mipCount) ? -1 : desc.MipLevels;
  
  				hr = d3dDevice->CreateShaderResourceView(tex,
  					&SRVDesc,
  					textureView
  				);
  				if (FAILED(hr))
  				{
  					tex->Release();
  					return hr;
  				}
  			}
  
  			if (texture != 0)
  			{
  				*texture = tex;
  			}
  			else
  			{
  				SetDebugObjectName(tex, "DDSTextureLoader");
  				tex->Release();
  			}
  		}
  	}
  	break;
  	}
  
  	return hr;
  }
  
  
  //--------------------------------------------------------------------------------------
  static HRESULT CreateTextureFromDDS(_In_ ID3D11Device* d3dDevice,
  	_In_opt_ ID3D11DeviceContext* d3dContext,
  	_In_ const DDS_HEADER* header,
  	_In_reads_bytes_(bitSize) const uint8_t* bitData,
  	_In_ size_t bitSize,
  	_In_ size_t maxsize,
  	_In_ D3D11_USAGE usage,
  	_In_ unsigned int bindFlags,
  	_In_ unsigned int cpuAccessFlags,
  	_In_ unsigned int miscFlags,
  	_In_ bool forceSRGB,
  	_Outptr_opt_ ID3D11Resource** texture,
  	_Outptr_opt_ ID3D11ShaderResourceView** textureView)
  {
  	HRESULT hr = S_OK;
  
  	size_t width = header->width;
  	size_t height = header->height;
  	size_t depth = header->depth;
  
  	uint32_t resDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
  	size_t arraySize = 1;
  	DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
  	bool isCubeMap = false;
  
  	size_t mipCount = header->mipMapCount;
  	if (0 == mipCount)
  	{
  		mipCount = 1;
  	}
  
  	if ((header->ddspf.flags & DDS_FOURCC) &&
  		(MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC))
  	{
  		auto d3d10ext = reinterpret_cast<const DDS_HEADER_DXT10*>((const char*)header + sizeof(DDS_HEADER));
  
  		arraySize = d3d10ext->arraySize;
  		if (arraySize == 0)
  		{
  			return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  		}
  
  		switch (d3d10ext->dxgiFormat)
  		{
  		case DXGI_FORMAT_AI44:
  		case DXGI_FORMAT_IA44:
  		case DXGI_FORMAT_P8:
  		case DXGI_FORMAT_A8P8:
  			return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  
  		default:
  			if (BitsPerPixel(d3d10ext->dxgiFormat) == 0)
  			{
  				return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  			}
  		}
  
  		format = d3d10ext->dxgiFormat;
  
  		switch (d3d10ext->resourceDimension)
  		{
  		case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
  			// D3DX writes 1D textures with a fixed Height of 1
  			if ((header->flags & DDS_HEIGHT) && height != 1)
  			{
  				return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  			}
  			height = depth = 1;
  			break;
  
  		case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
  			if (d3d10ext->miscFlag & D3D11_RESOURCE_MISC_TEXTURECUBE)
  			{
  				arraySize *= 6;
  				isCubeMap = true;
  			}
  			depth = 1;
  			break;
  
  		case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
  			if (!(header->flags & DDS_HEADER_FLAGS_VOLUME))
  			{
  				return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  			}
  
  			if (arraySize > 1)
  			{
  				return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  			}
  			break;
  
  		default:
  			return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  		}
  
  		resDim = d3d10ext->resourceDimension;
  	}
  	else
  	{
  		format = GetDXGIFormat(header->ddspf);
  
  		if (format == DXGI_FORMAT_UNKNOWN)
  		{
  			return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  		}
  
  		if (header->flags & DDS_HEADER_FLAGS_VOLUME)
  		{
  			resDim = D3D11_RESOURCE_DIMENSION_TEXTURE3D;
  		}
  		else
  		{
  			if (header->caps2 & DDS_CUBEMAP)
  			{
  				// We require all six faces to be defined
  				if ((header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES)
  				{
  					return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  				}
  
  				arraySize = 6;
  				isCubeMap = true;
  			}
  
  			depth = 1;
  			resDim = D3D11_RESOURCE_DIMENSION_TEXTURE2D;
  
  			// Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture
  		}
  
  		assert(BitsPerPixel(format) != 0);
  	}
  
  	// Bound sizes (for security purposes we don't trust DDS file metadata larger than the D3D 11.x hardware requirements)
  	if (mipCount > D3D11_REQ_MIP_LEVELS)
  	{
  		return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  	}
  
  	switch (resDim)
  	{
  	case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
  		if ((arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) ||
  			(width > D3D11_REQ_TEXTURE1D_U_DIMENSION))
  		{
  			return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  		}
  		break;
  
  	case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
  		if (isCubeMap)
  		{
  			// This is the right bound because we set arraySize to (NumCubes*6) above
  			if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) ||
  				(width > D3D11_REQ_TEXTURECUBE_DIMENSION) ||
  				(height > D3D11_REQ_TEXTURECUBE_DIMENSION))
  			{
  				return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  			}
  		}
  		else if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) ||
  			(width > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION) ||
  			(height > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION))
  		{
  			return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  		}
  		break;
  
  	case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
  		if ((arraySize > 1) ||
  			(width > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ||
  			(height > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ||
  			(depth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION))
  		{
  			return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  		}
  		break;
  	}
  
  	bool autogen = false;
  	if (mipCount == 1 && d3dContext != 0 && textureView != 0) // Must have context and shader-view to auto generate mipmaps
  	{
  		// See if format is supported for auto-gen mipmaps (varies by feature level)
  		UINT fmtSupport = 0;
  		hr = d3dDevice->CheckFormatSupport(format, &fmtSupport);
  		if (SUCCEEDED(hr) && (fmtSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN))
  		{
  			// 10level9 feature levels do not support auto-gen mipgen for volume textures
  			if ((resDim != D3D11_RESOURCE_DIMENSION_TEXTURE3D)
  				|| (d3dDevice->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0))
  			{
  				autogen = true;
  			}
  		}
  	}
  
  	if (autogen)
  	{
  		// Create texture with auto-generated mipmaps
  		ID3D11Resource* tex = nullptr;
  		hr = CreateD3DResources(d3dDevice, resDim, width, height, depth, 0, arraySize,
  			format, usage,
  			bindFlags | D3D11_BIND_RENDER_TARGET,
  			cpuAccessFlags,
  			miscFlags | D3D11_RESOURCE_MISC_GENERATE_MIPS, forceSRGB,
  			isCubeMap, nullptr, &tex, textureView);
  		if (SUCCEEDED(hr))
  		{
  			size_t numBytes = 0;
  			size_t rowBytes = 0;
  			GetSurfaceInfo(width, height, format, &numBytes, &rowBytes, nullptr);
  
  			if (numBytes > bitSize)
  			{
  				(*textureView)->Release();
  				*textureView = nullptr;
  				tex->Release();
  				return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
  			}
  
  			if (arraySize > 1)
  			{
  				D3D11_SHADER_RESOURCE_VIEW_DESC desc;
  				(*textureView)->GetDesc(&desc);
  
  				UINT mipLevels = 1;
  
  				switch (desc.ViewDimension)
  				{
  				case D3D_SRV_DIMENSION_TEXTURE1D:       mipLevels = desc.Texture1D.MipLevels; break;
  				case D3D_SRV_DIMENSION_TEXTURE1DARRAY:  mipLevels = desc.Texture1DArray.MipLevels; break;
  				case D3D_SRV_DIMENSION_TEXTURE2D:       mipLevels = desc.Texture2D.MipLevels; break;
  				case D3D_SRV_DIMENSION_TEXTURE2DARRAY:  mipLevels = desc.Texture2DArray.MipLevels; break;
  				case D3D_SRV_DIMENSION_TEXTURECUBE:     mipLevels = desc.TextureCube.MipLevels; break;
  				case D3D_SRV_DIMENSION_TEXTURECUBEARRAY:mipLevels = desc.TextureCubeArray.MipLevels; break;
  				default:
  					(*textureView)->Release();
  					*textureView = nullptr;
  					tex->Release();
  					return E_UNEXPECTED;
  				}
  
  				const uint8_t* pSrcBits = bitData;
  				const uint8_t* pEndBits = bitData + bitSize;
  				for (UINT item = 0; item < arraySize; ++item)
  				{
  					if ((pSrcBits + numBytes) > pEndBits)
  					{
  						(*textureView)->Release();
  						*textureView = nullptr;
  						tex->Release();
  						return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
  					}
  
  					UINT res = D3D11CalcSubresource(0, item, mipLevels);
  					d3dContext->UpdateSubresource(tex, res, nullptr, pSrcBits, static_cast<UINT>(rowBytes), static_cast<UINT>(numBytes));
  					pSrcBits += numBytes;
  				}
  			}
  			else
  			{
  				d3dContext->UpdateSubresource(tex, 0, nullptr, bitData, static_cast<UINT>(rowBytes), static_cast<UINT>(numBytes));
  			}
  
  			d3dContext->GenerateMips(*textureView);
  
  			if (texture)
  			{
  				*texture = tex;
  			}
  			else
  			{
  				tex->Release();
  			}
  		}
  	}
  	else
  	{
  		// Create the texture
  		std::unique_ptr<D3D11_SUBRESOURCE_DATA[]> initData(new (std::nothrow) D3D11_SUBRESOURCE_DATA[mipCount * arraySize]);
  		if (!initData)
  		{
  			return E_OUTOFMEMORY;
  		}
  
  		size_t skipMip = 0;
  		size_t twidth = 0;
  		size_t theight = 0;
  		size_t tdepth = 0;
  		hr = FillInitData(width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData,
  			twidth, theight, tdepth, skipMip, initData.get());
  
  		if (SUCCEEDED(hr))
  		{
  			hr = CreateD3DResources(d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize,
  				format, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB,
  				isCubeMap, initData.get(), texture, textureView);
  
  			if (FAILED(hr) && !maxsize && (mipCount > 1))
  			{
  				// Retry with a maxsize determined by feature level
  				switch (d3dDevice->GetFeatureLevel())
  				{
  				case D3D_FEATURE_LEVEL_9_1:
  				case D3D_FEATURE_LEVEL_9_2:
  					if (isCubeMap)
  					{
  						maxsize = 512 /*D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION*/;
  					}
  					else
  					{
  						maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D)
  							? 256 /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/
  							: 2048 /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/;
  					}
  					break;
  
  				case D3D_FEATURE_LEVEL_9_3:
  					maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D)
  						? 256 /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/
  						: 4096 /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/;
  					break;
  
  				default: // D3D_FEATURE_LEVEL_10_0 & D3D_FEATURE_LEVEL_10_1
  					maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D)
  						? 2048 /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/
  						: 8192 /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/;
  					break;
  				}
  
  				hr = FillInitData(width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData,
  					twidth, theight, tdepth, skipMip, initData.get());
  				if (SUCCEEDED(hr))
  				{
  					hr = CreateD3DResources(d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize,
  						format, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB,
  						isCubeMap, initData.get(), texture, textureView);
  				}
  			}
  		}
  	}
  
  	return hr;
  }
  
  
  //--------------------------------------------------------------------------------------
  static DDS_ALPHA_MODE GetAlphaMode(_In_ const DDS_HEADER* header)
  {
  	if (header->ddspf.flags & DDS_FOURCC)
  	{
  		if (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)
  		{
  			auto d3d10ext = reinterpret_cast<const DDS_HEADER_DXT10*>((const char*)header + sizeof(DDS_HEADER));
  			auto mode = static_cast<DDS_ALPHA_MODE>(d3d10ext->miscFlags2 & DDS_MISC_FLAGS2_ALPHA_MODE_MASK);
  			switch (mode)
  			{
  			case DDS_ALPHA_MODE_STRAIGHT:
  			case DDS_ALPHA_MODE_PREMULTIPLIED:
  			case DDS_ALPHA_MODE_OPAQUE:
  			case DDS_ALPHA_MODE_CUSTOM:
  				return mode;
  			}
  		}
  		else if ((MAKEFOURCC('D', 'X', 'T', '2') == header->ddspf.fourCC)
  			|| (MAKEFOURCC('D', 'X', 'T', '4') == header->ddspf.fourCC))
  		{
  			return DDS_ALPHA_MODE_PREMULTIPLIED;
  		}
  	}
  
  	return DDS_ALPHA_MODE_UNKNOWN;
  }
  
  
  //--------------------------------------------------------------------------------------
  _Use_decl_annotations_
  HRESULT DirectX::CreateDDSTextureFromMemory(ID3D11Device* d3dDevice,
  	const uint8_t* ddsData,
  	size_t ddsDataSize,
  	ID3D11Resource** texture,
  	ID3D11ShaderResourceView** textureView,
  	size_t maxsize,
  	DDS_ALPHA_MODE* alphaMode)
  {
  	return CreateDDSTextureFromMemoryEx(d3dDevice, nullptr, ddsData, ddsDataSize, maxsize,
  		D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false,
  		texture, textureView, alphaMode);
  }
  
  _Use_decl_annotations_
  HRESULT DirectX::CreateDDSTextureFromMemory(ID3D11Device* d3dDevice,
  	ID3D11DeviceContext* d3dContext,
  	const uint8_t* ddsData,
  	size_t ddsDataSize,
  	ID3D11Resource** texture,
  	ID3D11ShaderResourceView** textureView,
  	size_t maxsize,
  	DDS_ALPHA_MODE* alphaMode)
  {
  	return CreateDDSTextureFromMemoryEx(d3dDevice, d3dContext, ddsData, ddsDataSize, maxsize,
  		D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false,
  		texture, textureView, alphaMode);
  }
  
  _Use_decl_annotations_
  HRESULT DirectX::CreateDDSTextureFromMemoryEx(ID3D11Device* d3dDevice,
  	const uint8_t* ddsData,
  	size_t ddsDataSize,
  	size_t maxsize,
  	D3D11_USAGE usage,
  	unsigned int bindFlags,
  	unsigned int cpuAccessFlags,
  	unsigned int miscFlags,
  	bool forceSRGB,
  	ID3D11Resource** texture,
  	ID3D11ShaderResourceView** textureView,
  	DDS_ALPHA_MODE* alphaMode)
  {
  	return CreateDDSTextureFromMemoryEx(d3dDevice, nullptr, ddsData, ddsDataSize, maxsize,
  		usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB,
  		texture, textureView, alphaMode);
  }
  
  _Use_decl_annotations_
  HRESULT DirectX::CreateDDSTextureFromMemoryEx(ID3D11Device* d3dDevice,
  	ID3D11DeviceContext* d3dContext,
  	const uint8_t* ddsData,
  	size_t ddsDataSize,
  	size_t maxsize,
  	D3D11_USAGE usage,
  	unsigned int bindFlags,
  	unsigned int cpuAccessFlags,
  	unsigned int miscFlags,
  	bool forceSRGB,
  	ID3D11Resource** texture,
  	ID3D11ShaderResourceView** textureView,
  	DDS_ALPHA_MODE* alphaMode)
  {
  	if (texture)
  	{
  		*texture = nullptr;
  	}
  	if (textureView)
  	{
  		*textureView = nullptr;
  	}
  	if (alphaMode)
  	{
  		*alphaMode = DDS_ALPHA_MODE_UNKNOWN;
  	}
  
  	if (!d3dDevice || !ddsData || (!texture && !textureView))
  	{
  		return E_INVALIDARG;
  	}
  
  	// Validate DDS file in memory
  	if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER)))
  	{
  		return E_FAIL;
  	}
  
  	uint32_t dwMagicNumber = *(const uint32_t*)(ddsData);
  	if (dwMagicNumber != DDS_MAGIC)
  	{
  		return E_FAIL;
  	}
  
  	auto header = reinterpret_cast<const DDS_HEADER*>(ddsData + sizeof(uint32_t));
  
  	// Verify header to validate DDS file
  	if (header->size != sizeof(DDS_HEADER) ||
  		header->ddspf.size != sizeof(DDS_PIXELFORMAT))
  	{
  		return E_FAIL;
  	}
  
  	// Check for DX10 extension
  	bool bDXT10Header = false;
  	if ((header->ddspf.flags & DDS_FOURCC) &&
  		(MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC))
  	{
  		// Must be long enough for both headers and magic value
  		if (ddsDataSize < (sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10)))
  		{
  			return E_FAIL;
  		}
  
  		bDXT10Header = true;
  	}
  
  	ptrdiff_t offset = sizeof(uint32_t)
  		+ sizeof(DDS_HEADER)
  		+ (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0);
  
  	HRESULT hr = CreateTextureFromDDS(d3dDevice, d3dContext, header,
  		ddsData + offset, ddsDataSize - offset, maxsize,
  		usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB,
  		texture, textureView);
  	if (SUCCEEDED(hr))
  	{
  		if (texture != 0 && *texture != 0)
  		{
  			SetDebugObjectName(*texture, "DDSTextureLoader");
  		}
  
  		if (textureView != 0 && *textureView != 0)
  		{
  			SetDebugObjectName(*textureView, "DDSTextureLoader");
  		}
  
  		if (alphaMode)
  			*alphaMode = GetAlphaMode(header);
  	}
  
  	return hr;
  }
  
  //--------------------------------------------------------------------------------------
  _Use_decl_annotations_
  HRESULT DirectX::CreateDDSTextureFromFile(ID3D11Device* d3dDevice,
  	const wchar_t* fileName,
  	ID3D11Resource** texture,
  	ID3D11ShaderResourceView** textureView,
  	size_t maxsize,
  	DDS_ALPHA_MODE* alphaMode)
  {
  	return CreateDDSTextureFromFileEx(d3dDevice, nullptr, fileName, maxsize,
  		D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false,
  		texture, textureView, alphaMode);
  }
  
  _Use_decl_annotations_
  HRESULT DirectX::CreateDDSTextureFromFile(ID3D11Device* d3dDevice,
  	ID3D11DeviceContext* d3dContext,
  	const wchar_t* fileName,
  	ID3D11Resource** texture,
  	ID3D11ShaderResourceView** textureView,
  	size_t maxsize,
  	DDS_ALPHA_MODE* alphaMode)
  {
  	return CreateDDSTextureFromFileEx(d3dDevice, d3dContext, fileName, maxsize,
  		D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false,
  		texture, textureView, alphaMode);
  }
  
  _Use_decl_annotations_
  HRESULT DirectX::CreateDDSTextureFromFileEx(ID3D11Device* d3dDevice,
  	const wchar_t* fileName,
  	size_t maxsize,
  	D3D11_USAGE usage,
  	unsigned int bindFlags,
  	unsigned int cpuAccessFlags,
  	unsigned int miscFlags,
  	bool forceSRGB,
  	ID3D11Resource** texture,
  	ID3D11ShaderResourceView** textureView,
  	DDS_ALPHA_MODE* alphaMode)
  {
  	return CreateDDSTextureFromFileEx(d3dDevice, nullptr, fileName, maxsize,
  		usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB,
  		texture, textureView, alphaMode);
  }
  
  _Use_decl_annotations_
  HRESULT DirectX::CreateDDSTextureFromFileEx(ID3D11Device* d3dDevice,
  	ID3D11DeviceContext* d3dContext,
  	const wchar_t* fileName,
  	size_t maxsize,
  	D3D11_USAGE usage,
  	unsigned int bindFlags,
  	unsigned int cpuAccessFlags,
  	unsigned int miscFlags,
  	bool forceSRGB,
  	ID3D11Resource** texture,
  	ID3D11ShaderResourceView** textureView,
  	DDS_ALPHA_MODE* alphaMode)
  {
  	if (texture)
  	{
  		*texture = nullptr;
  	}
  	if (textureView)
  	{
  		*textureView = nullptr;
  	}
  	if (alphaMode)
  	{
  		*alphaMode = DDS_ALPHA_MODE_UNKNOWN;
  	}
  
  	if (!d3dDevice || !fileName || (!texture && !textureView))
  	{
  		return E_INVALIDARG;
  	}
  
  	DDS_HEADER* header = nullptr;
  	uint8_t* bitData = nullptr;
  	size_t bitSize = 0;
  
  	std::unique_ptr<uint8_t[]> ddsData;
  	HRESULT hr = LoadTextureDataFromFile(fileName,
  		ddsData,
  		&header,
  		&bitData,
  		&bitSize
  	);
  	if (FAILED(hr))
  	{
  		return hr;
  	}
  
  	hr = CreateTextureFromDDS(d3dDevice, d3dContext, header,
  		bitData, bitSize, maxsize,
  		usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB,
  		texture, textureView);
  
  	if (SUCCEEDED(hr))
  	{
  #if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) )
  		if (texture != 0 || textureView != 0)
  		{
  			CHAR strFileA[MAX_PATH];
  			int result = WideCharToMultiByte(CP_ACP,
  				WC_NO_BEST_FIT_CHARS,
  				fileName,
  				-1,
  				strFileA,
  				MAX_PATH,
  				nullptr,
  				FALSE
  			);
  			if (result > 0)
  			{
  				const CHAR* pstrName = strrchr(strFileA, '\\');
  				if (!pstrName)
  				{
  					pstrName = strFileA;
  				}
  				else
  				{
  					pstrName++;
  				}
  
  				if (texture != 0 && *texture != 0)
  				{
  					(*texture)->SetPrivateData(WKPDID_D3DDebugObjectName,
  						static_cast<UINT>(strnlen_s(pstrName, MAX_PATH)),
  						pstrName
  					);
  				}
  
  				if (textureView != 0 && *textureView != 0)
  				{
  					(*textureView)->SetPrivateData(WKPDID_D3DDebugObjectName,
  						static_cast<UINT>(strnlen_s(pstrName, MAX_PATH)),
  						pstrName
  					);
  				}
  			}
  		}
  #endif
  
  		if (alphaMode)
  			*alphaMode = GetAlphaMode(header);
  	}
  
  	return hr;
  }
  