/*
	Copyright(c) 2015 Neodymium

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

//DDSIO: stolen and translated




// #region License
// /*
// Microsoft Public License (Ms-PL)
// MonoGame - Copyright © 2009 The MonoGame Team
// 
// All rights reserved.
// 
// This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
// accept the license, do not use the software.
// 
// 1. Definitions
// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under 
// U.S. copyright law.
// 
// A "contribution" is the original software, or any additions or changes to the software.
// A "contributor" is any person that distributes its contribution under this license.
// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
// 
// 2. Grant of Rights
// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
// each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
// each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
// 
// 3. Conditions and Limitations
// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, 
// your patent license from such contributor to the software ends automatically.
// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution 
// notices that are present in the software.
// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including 
// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object 
// code form, you may only do so under a license that complies with this license.
// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
// permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
// purpose and non-infringement.
// */
// #endregion License

//DecompressDXT: ripped from MonoGame - todo: reinterpret to remove stupid license



//additional dds importing code modified from: https://gist.github.com/spazzarama/2710d020d1d615cde20c607711655167
// DDSTextureLoader Ported to C# by Justin Stenning, March 2017
//--------------------------------------------------------------------------------------
// File: DDSTextureLoader.cpp
//
// Functions for loading a DDS texture and creating a Direct3D 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
//--------------------------------------------------------------------------------------





using CodeWalker.GameFiles;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeWalker.Utils
{

    public static class DDSIO
    {


        public static byte[] GetPixels(Texture texture, int mip)
        {
            //dexyfex version
            var format = GetDXGIFormat(texture.Format);
            ImageStruct img = GetImageStruct(texture, format);
            Image[] images = GetMipmapImages(img, format);
            TexMetadata meta = GetImageMetadata(img, format);

            if (meta.dimension != TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D)
                throw new Exception("Can only GetPixels from Texture2D.");


            var i0 = images[Math.Min(Math.Max(mip, 0), images.Length - 1)];

            int ddsRowPitch, ddsSlicePitch;
            DXTex.ComputePitch(meta.format, i0.width, i0.height, out ddsRowPitch, out ddsSlicePitch, 0);

            if (i0.slicePitch == ddsSlicePitch)
            { }
            else
            { }

            int w = i0.width;
            int h = i0.height;
            int imglen = i0.slicePitch;// h * i0.rowPitch;
            byte[] imgdata = new byte[imglen];
            byte[] px = null;// = new byte[w * h * 4];

            if (i0.pixels + imglen > img.Data.Length)
            { throw new Exception("Mipmap not in texture!"); }

            Buffer.BlockCopy(img.Data, i0.pixels, imgdata, 0, imglen);

            bool swaprb = true;

            switch (format)
            {
                // compressed
                case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: // TextureFormat.D3DFMT_DXT1
                    px = DecompressDxt1(imgdata, w, h);
                    break;
                case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: // TextureFormat.D3DFMT_DXT3
                    px = DecompressDxt3(imgdata, w, h);
                    break;
                case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: // TextureFormat.D3DFMT_DXT5
                    px = DecompressDxt5(imgdata, w, h);
                    break;
                case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: // TextureFormat.D3DFMT_ATI1
                    px = DecompressBC4(imgdata, w, h);
                    break;
                case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: // TextureFormat.D3DFMT_ATI2
                    px = DecompressBC5(imgdata, w, h);
                    break;
                case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM: // TextureFormat.D3DFMT_BC7
                    //BC7 TODO!!
                    break;

                // uncompressed
                case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: // TextureFormat.D3DFMT_A1R5G5B5
                    px = ConvertBGR5A1ToRGBA8(imgdata, w, h); //needs testing
                    break;
                case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM:       // TextureFormat.D3DFMT_A8
                    px = ConvertA8ToRGBA8(imgdata, w, h);
                    swaprb = false;
                    break;
                case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: // TextureFormat.D3DFMT_A8B8G8R8
                    px = imgdata;
                    break;
                case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM:       // TextureFormat.D3DFMT_L8
                    px = ConvertR8ToRGBA8(imgdata, w, h);
                    break;
                case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: // TextureFormat.D3DFMT_A8R8G8B8
                    px = imgdata;
                    swaprb = false;
                    break;
                default:
                    break; //shouldn't get here...
            }

            if (swaprb && (px != null))
            {
                int pxc = px.Length / 4;// w * h;
                for (int i = 0; i < pxc; i++)
                {
                    int ib = i * 4;
                    byte p2 = px[ib + 2];
                    px[ib + 2] = px[ib];
                    px[ib] = p2;
                }
            }


            return px;
        }



        public static byte[] GetDDSFile(Texture texture)
        {
            var format = GetDXGIFormat(texture.Format);
            ImageStruct img = GetImageStruct(texture, format);
            Image[] images = GetMipmapImages(img, format);
            TexMetadata meta = GetImageMetadata(img, format);




            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);

            int nimages = img.MipMapLevels;

            // Create DDS Header
            int required;
            if (!DXTex._EncodeDDSHeader(meta, 0, bw, out required))
            {
                throw new Exception("Couldn't make DDS header");
            }

            // Write images
            switch (meta.dimension)
            {
                case TEX_DIMENSION.TEX_DIMENSION_TEXTURE1D:
                case TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D:
                    {
                        int index = 0;
                        for (int item = 0; item < meta.arraySize; ++item)
                        {
                            for (int level = 0; level < meta.mipLevels; ++level, ++index)
                            {
                                if (index >= nimages)
                                    throw new Exception("Tried to write mip out of range");
                                if (images[index].rowPitch <= 0)
                                    throw new Exception("Invalid row pitch.");
                                if (images[index].slicePitch <= 0)
                                    throw new Exception("Invalid slice pitch.");
                                //if (images[index].pixels)
                                //    return E_POINTER;

                                int ddsRowPitch, ddsSlicePitch;
                                DXTex.ComputePitch(meta.format, images[index].width, images[index].height, out ddsRowPitch, out ddsSlicePitch, 0);// CP_FLAGS.CP_FLAGS_NONE);

                                if (images[index].slicePitch == ddsSlicePitch)
                                {
                                    int lengt = ddsSlicePitch;
                                    if (images[index].pixels + ddsSlicePitch > img.Data.Length)
                                    {
                                        lengt = img.Data.Length - images[index].pixels;
                                        if (lengt <= 0)
                                        {
                                            //throw new Exception("Not enough data to read...");
                                        }
                                    }
                                    if (lengt > 0)
                                    {
                                        bw.Write(img.Data, images[index].pixels, lengt);
                                    }
                                }
                                else
                                {
                                    int rowPitch = images[index].rowPitch;
                                    if (rowPitch < ddsRowPitch)
                                    {
                                        // DDS uses 1-byte alignment, so if this is happening then the input pitch isn't actually a full line of data
                                        throw new Exception("Input pitch isn't a full line of data");
                                    }

                                    int sPtr = images[index].pixels;

                                    int lines = DXTex.ComputeScanlines(meta.format, images[index].height);
                                    for (int j = 0; j < lines; ++j)
                                    {
                                        bw.Write(img.Data, sPtr, ddsRowPitch);
                                        sPtr += rowPitch;
                                    }
                                }
                            }
                        }
                    }
                    break;

                case TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D:
                    {
                        if (meta.arraySize != 1)
                            throw new Exception("Texture3D must have arraySize == 1"); //return null;// E_FAIL;

                        int d = meta.depth;

                        int index = 0;
                        for (int level = 0; level < meta.mipLevels; ++level)
                        {
                            for (int slice = 0; slice < d; ++slice, ++index)
                            {
                                if (index >= nimages)
                                    throw new Exception("Tried to write mip out of range");
                                if (images[index].rowPitch <= 0)
                                    throw new Exception("Invalid row pitch.");
                                if (images[index].slicePitch <= 0)
                                    throw new Exception("Invalid slice pitch.");
                                //if (!images[index].pixels)
                                //    return E_POINTER;

                                int ddsRowPitch, ddsSlicePitch;
                                DXTex.ComputePitch(meta.format, images[index].width, images[index].height, out ddsRowPitch, out ddsSlicePitch, 0);// CP_FLAGS_NONE);

                                if (images[index].slicePitch == ddsSlicePitch)
                                {
                                    bw.Write(img.Data, images[index].pixels, ddsSlicePitch);
                                }
                                else
                                {
                                    int rowPitch = images[index].rowPitch;
                                    if (rowPitch < ddsRowPitch)
                                    {
                                        // DDS uses 1-byte alignment, so if this is happening then the input pitch isn't actually a full line of data
                                        throw new Exception("Input pitch isn't a full line of data");
                                    }

                                    int sPtr = images[index].pixels;

                                    int lines = DXTex.ComputeScanlines(meta.format, images[index].height);
                                    for (int j = 0; j < lines; ++j)
                                    {
                                        bw.Write(img.Data, sPtr, ddsRowPitch);
                                        sPtr += rowPitch;
                                    }
                                }
                            }

                            if (d > 1)
                                d >>= 1;
                        }
                    }
                    break;

                default:
                    throw new Exception("Unsupported texture dimension");
            }


            byte[] buff = ms.GetBuffer();
            byte[] outbuf = new byte[ms.Length]; //need to copy to the right size buffer for File.WriteAllBytes().
            Array.Copy(buff, outbuf, outbuf.Length);

            return outbuf;
        }


        public static Texture GetTexture(byte[] ddsfile)
        {
            var ms = new MemoryStream(ddsfile);
            var br = new BinaryReader(ms);

            var header = new DDS_HEADER();
            var header10 = new DDS_HEADER_DXT10();
            var useheader10 = false;

            if (!DXTex._ReadDDSHeader(br, out header, out header10, out useheader10))
            { return null; }

            var width = header.dwWidth;
            var height = header.dwHeight;
            var depth = header.dwDepth;
            var mipCount = header.dwMipMapCount;
            if (mipCount == 0)
            {
                mipCount = 1;
            }

            TEX_DIMENSION resDim = 0;
            uint arraySize = 1;
            DXGI_FORMAT format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;
            bool isCubeMap = false;

            if (useheader10)
            {
                arraySize = header10.arraySize;
                format = header10.dxgiFormat;
                resDim = (TEX_DIMENSION)header10.resourceDimension;

                if (arraySize == 0) throw new Exception("ArraySize was 0! This isn't supported...");
                switch (format)
                {
                    case DXGI_FORMAT.DXGI_FORMAT_AI44:
                    case DXGI_FORMAT.DXGI_FORMAT_IA44:
                    case DXGI_FORMAT.DXGI_FORMAT_P8:
                    case DXGI_FORMAT.DXGI_FORMAT_A8P8:
                        throw new NotSupportedException(string.Format("{0} DXGI format is not supported", format.ToString()));
                    default:
                        if (DXTex.BitsPerPixel(format) == 0)
                            throw new NotSupportedException(string.Format("{0} DXGI format is not supported", format.ToString()));
                        break;
                }
                switch (resDim)
                {
                    case TEX_DIMENSION.TEX_DIMENSION_TEXTURE1D:
                        if ((header.dwFlags & DXTex.DDS_HEIGHT) > 0 && height != 1) throw new NotSupportedException("1D texture's height wasn't 1!");
                        height = depth = 1; // D3DX writes 1D textures with a fixed Height of 1
                        break;
                    case TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D:
                        if ((header10.miscFlag & (uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE) > 0) { arraySize *= 6; isCubeMap = true; }
                        depth = 1;
                        break;
                    case TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D:
                        if ((header.dwFlags & DXTex.DDS_HEADER_FLAGS_VOLUME) == 0) throw new ArgumentException("3D texture without volume flag!");
                        if (arraySize > 1) throw new ArgumentException("3D texture with ArraySize > 1!");
                        break;
                    default: throw new ArgumentException("Unknown resource dimension!");
                }
            }
            else
            {
                format = header.ddspf.GetDXGIFormat();

                if (format == DXGI_FORMAT.DXGI_FORMAT_UNKNOWN) throw new ArgumentException("Unknown DDS format.");
                if ((header.dwFlags & DXTex.DDS_HEADER_FLAGS_VOLUME) > 0)
                {
                    resDim = TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D;
                }
                else
                {
                    if ((header.dwCaps2 & DXTex.DDS_CUBEMAP) > 0)
                    {
                        if ((header.dwCaps2 & DXTex.DDS_CUBEMAP_ALLFACES) != DXTex.DDS_CUBEMAP_ALLFACES) throw new ArgumentException("Not all faces in cubemap exist!");//requires all 6 faces
                        arraySize = 6;
                        isCubeMap = true;
                    }
                    depth = 1;
                    resDim = TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D;
                    // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture
                }
                if (DXTex.BitsPerPixel(format) == 0) throw new Exception(string.Format("{0} DXGI format is not supported", format.ToString()));
            }

            if (isCubeMap)
            { }

            DXTex.ComputePitch(format, (int)width, (int)height, out int rowPitch, out int slicePitch, 0);
            var stride = slicePitch / height;
            var scanlines = DXTex.ComputeScanlines(format, (int)height);

            var brem = ms.Length - ms.Position;
            var ddsdata = br.ReadBytes((int)brem);

            var tex = new Texture();
            tex.Width = (ushort)width;
            tex.Height = (ushort)height;
            tex.Depth = (ushort)depth;
            tex.Levels = (byte)mipCount;
            tex.Format = GetTextureFormat(format);
            tex.Stride = (ushort)stride;
            tex.Data = new TextureData();
            tex.Data.FullData = ddsdata;

            //tex.Unknown_43h = (byte)header.ddspf.dwSize;

            return tex;
        }




        private static TextureFormat GetTextureFormat(DXGI_FORMAT f)
        {
            var format = (TextureFormat)0;
            switch (f)
            {
                // compressed
                case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: format = TextureFormat.D3DFMT_DXT1; break;
                case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: format = TextureFormat.D3DFMT_DXT3; break;
                case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: format = TextureFormat.D3DFMT_DXT5; break;
                case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: format = TextureFormat.D3DFMT_ATI1; break;
                case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: format = TextureFormat.D3DFMT_ATI2; break;
                case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM: format = TextureFormat.D3DFMT_BC7; break;

                // uncompressed
                case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: format = TextureFormat.D3DFMT_A1R5G5B5; break;
                case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM: format = TextureFormat.D3DFMT_A8; break;
                case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: format = TextureFormat.D3DFMT_A8B8G8R8; break;
                case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: format = TextureFormat.D3DFMT_L8; break;
                case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: format = TextureFormat.D3DFMT_A8R8G8B8; break;
            }
            return format;
        }

        private static DXGI_FORMAT GetDXGIFormat(TextureFormat f)
        {
            var format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;
            switch (f)
            {
                // compressed
                case TextureFormat.D3DFMT_DXT1: format = DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM; break;
                case TextureFormat.D3DFMT_DXT3: format = DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM; break;
                case TextureFormat.D3DFMT_DXT5: format = DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM; break;
                case TextureFormat.D3DFMT_ATI1: format = DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM; break;
                case TextureFormat.D3DFMT_ATI2: format = DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM; break;
                case TextureFormat.D3DFMT_BC7: format = DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM; break;

                // uncompressed
                case TextureFormat.D3DFMT_A1R5G5B5: format = DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM; break;
                case TextureFormat.D3DFMT_A8: format = DXGI_FORMAT.DXGI_FORMAT_A8_UNORM; break;
                case TextureFormat.D3DFMT_A8B8G8R8: format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM; break;
                case TextureFormat.D3DFMT_L8: format = DXGI_FORMAT.DXGI_FORMAT_R8_UNORM; break;
                case TextureFormat.D3DFMT_A8R8G8B8: format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM; break;
            }
            return format;
        }

        private static ImageStruct GetImageStruct(Texture texture, DXGI_FORMAT format)
        {
            ImageStruct img = new ImageStruct();
            img.Data = texture.Data.FullData;
            img.Width = texture.Width;
            img.Height = texture.Height;
            img.MipMapLevels = texture.Levels;
            img.Format = (int)format;
            return img;
        }

        private static Image[] GetMipmapImages(ImageStruct img, DXGI_FORMAT format)
        {
            Image[] images = new Image[img.MipMapLevels];

            int buf = 0;
            int div = 1;
            int add = 0;
            for (int i = 0; i < img.MipMapLevels; i++)
            {
                images[i].width = img.Width / div;
                images[i].height = img.Height / div;
                images[i].format = format; //(DXGI_FORMAT)img.Format;
                images[i].pixels = buf + add;

                DXTex.ComputePitch(images[i].format, images[i].width, images[i].height, out images[i].rowPitch, out images[i].slicePitch, 0);

                add += images[i].slicePitch;
                div *= 2;
            }

            return images;
        }

        private static TexMetadata GetImageMetadata(ImageStruct img, DXGI_FORMAT format)
        {
            TexMetadata meta = new TexMetadata();
            meta.width = img.Width;
            meta.height = img.Height;
            meta.depth = 1;
            meta.arraySize = 1; // ???
            meta.mipLevels = img.MipMapLevels;
            meta.miscFlags = 0; // ???
            meta.miscFlags2 = 0; // ???
            meta.format = format; //(DXGI_FORMAT)img.Format;
            meta.dimension = TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D;
            return meta;
        }


        public struct TexMetadata
        {
            public int width;
            public int height;     // Should be 1 for 1D textures
            public int depth;      // Should be 1 for 1D or 2D textures
            public int arraySize;  // For cubemap, this is a multiple of 6
            public int mipLevels;
            public uint miscFlags;
            public uint miscFlags2;
            public DXGI_FORMAT format;
            public TEX_DIMENSION dimension;

            // Returns size_t(-1) to indicate an out-of-range error
            public int ComputeIndex(int mip, int item, int slice)
            {
                if (mip >= mipLevels)
                    return -1;

                switch (dimension)
                {
                    case TEX_DIMENSION.TEX_DIMENSION_TEXTURE1D:
                    case TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D:
                        if (slice > 0)
                            return -1;

                        if (item >= arraySize)
                            return -1;

                        return (item * (mipLevels) + mip);

                    case TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D:
                        if (item > 0)
                        {
                            // No support for arrays of volumes
                            return -1;
                        }
                        else
                        {
                            int index = 0;
                            int d = depth;

                            for (int level = 0; level < mip; ++level)
                            {
                                index += d;
                                if (d > 1)
                                    d >>= 1;
                            }

                            if (slice >= d)
                                return -1;

                            index += slice;

                            return index;
                        }
                    //break;

                    default:
                        return -1;
                }
            }

            // Helper for miscFlags
            public bool IsCubemap()
            {
                return (miscFlags & (uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE) != 0;
            }

            public bool IsPMAlpha()
            {
                return ((miscFlags2 & (uint)TEX_MISC_FLAG.TEX_MISC2_ALPHA_MODE_MASK) == (uint)TEX_ALPHA_MODE.TEX_ALPHA_MODE_PREMULTIPLIED);
            }
            // Helpers for miscFlags2
            //public void SetAlphaMode(TEX_ALPHA_MODE mode)
            //{
            //    miscFlags2 = (miscFlags2 & ~TEX_MISC2_ALPHA_MODE_MASK) | static_cast<uint32_t>(mode);
            //}

            // Helper for dimension
            public bool IsVolumemap()
            {
                return (dimension == TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D);
            }
        }

        public struct Image
        {
            public int width;
            public int height;
            public DXGI_FORMAT format;
            public int rowPitch;
            public int slicePitch;
            public int pixels; //offset into data array...
        };

        public static class DXTex
        {
            public static void ComputePitch(DXGI_FORMAT fmt, int width, int height, out int rowPitch, out int slicePitch, uint flags)
            {
                switch (fmt)
                {
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
                        assert(IsCompressed(fmt));
                        {
                            int nbw = Math.Max(1, (width + 3) / 4);
                            int nbh = Math.Max(1, (height + 3) / 4);
                            rowPitch = nbw * 8;

                            slicePitch = rowPitch * nbh;
                        }
                        break;

                    case DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
                        assert(IsCompressed(fmt));
                        {
                            int nbw = Math.Max(1, (width + 3) / 4);
                            int nbh = Math.Max(1, (height + 3) / 4);
                            rowPitch = nbw * 16;

                            slicePitch = rowPitch * nbh;
                        }
                        break;

                    case DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_YUY2:
                        assert(IsPacked(fmt));
                        rowPitch = ((width + 1) >> 1) * 4;
                        slicePitch = rowPitch * height;
                        break;

                    case DXGI_FORMAT.DXGI_FORMAT_Y210:
                    case DXGI_FORMAT.DXGI_FORMAT_Y216:
                        assert(IsPacked(fmt));
                        rowPitch = ((width + 1) >> 1) * 8;
                        slicePitch = rowPitch * height;
                        break;

                    case DXGI_FORMAT.DXGI_FORMAT_NV12:
                    case DXGI_FORMAT.DXGI_FORMAT_420_OPAQUE:
                        assert(IsPlanar(fmt));
                        rowPitch = ((width + 1) >> 1) * 2;
                        slicePitch = rowPitch * (height + ((height + 1) >> 1));
                        break;

                    case DXGI_FORMAT.DXGI_FORMAT_P010:
                    case DXGI_FORMAT.DXGI_FORMAT_P016:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT:
                        assert(IsPlanar(fmt));
                        rowPitch = ((width + 1) >> 1) * 4;
                        slicePitch = rowPitch * (height + ((height + 1) >> 1));
                        break;

                    case DXGI_FORMAT.DXGI_FORMAT_NV11:
                        assert(IsPlanar(fmt));
                        rowPitch = ((width + 3) >> 2) * 4;
                        slicePitch = rowPitch * height * 2;
                        break;

                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_P208:
                        assert(IsPlanar(fmt));
                        rowPitch = ((width + 1) >> 1) * 2;
                        slicePitch = rowPitch * height * 2;
                        break;

                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_V208:
                        assert(IsPlanar(fmt));
                        rowPitch = width;
                        slicePitch = rowPitch * (height + (((height + 1) >> 1) * 2));
                        break;

                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_V408:
                        assert(IsPlanar(fmt));
                        rowPitch = width;
                        slicePitch = rowPitch * (height + ((height >> 1) * 4));
                        break;

                    default:
                        assert(IsValid(fmt));
                        assert(!IsCompressed(fmt) && !IsPacked(fmt) && !IsPlanar(fmt));
                        {

                            int bpp;

                            if ((flags & (uint)CP_FLAGS.CP_FLAGS_24BPP) != 0)
                                bpp = 24;
                            else if ((flags & (uint)CP_FLAGS.CP_FLAGS_16BPP) != 0)
                                bpp = 16;
                            else if ((flags & (uint)CP_FLAGS.CP_FLAGS_8BPP) != 0)
                                bpp = 8;
                            else
                                bpp = BitsPerPixel(fmt);

                            if ((flags & (uint)(CP_FLAGS.CP_FLAGS_LEGACY_DWORD | CP_FLAGS.CP_FLAGS_PARAGRAPH | CP_FLAGS.CP_FLAGS_YMM | CP_FLAGS.CP_FLAGS_ZMM | CP_FLAGS.CP_FLAGS_PAGE4K)) != 0)
                            {
                                if ((flags & (uint)CP_FLAGS.CP_FLAGS_PAGE4K) != 0)
                                {
                                    rowPitch = ((width * bpp + 32767) / 32768) * 4096;
                                    slicePitch = rowPitch * height;
                                }
                                else if ((flags & (uint)CP_FLAGS.CP_FLAGS_ZMM) != 0)
                                {
                                    rowPitch = ((width * bpp + 511) / 512) * 64;
                                    slicePitch = rowPitch * height;
                                }
                                else if ((flags & (uint)CP_FLAGS.CP_FLAGS_YMM) != 0)
                                {
                                    rowPitch = ((width * bpp + 255) / 256) * 32;
                                    slicePitch = rowPitch * height;
                                }
                                else if ((flags & (uint)CP_FLAGS.CP_FLAGS_PARAGRAPH) != 0)
                                {
                                    rowPitch = ((width * bpp + 127) / 128) * 16;
                                    slicePitch = rowPitch * height;
                                }
                                else // DWORD alignment
                                {
                                    // Special computation for some incorrectly created DDS files based on
                                    // legacy DirectDraw assumptions about pitch alignment
                                    rowPitch = ((width * bpp + 31) / 32) * 4;// sizeof(uint32_t);
                                    slicePitch = rowPitch * height;
                                }
                            }
                            else
                            {
                                // Default byte alignment
                                rowPitch = (width * bpp + 7) / 8;
                                slicePitch = rowPitch * height;
                            }
                        }
                        break;
                }
            }

            public static bool IsCompressed(DXGI_FORMAT fmt)
            {
                switch (fmt)
                {
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
                        return true;

                    default:
                        return false;
                }
            }

            public static bool IsPlanar(DXGI_FORMAT fmt)
            {
                switch (fmt)
                {
                    case DXGI_FORMAT.DXGI_FORMAT_NV12:      // 4:2:0 8-bit
                    case DXGI_FORMAT.DXGI_FORMAT_P010:      // 4:2:0 10-bit
                    case DXGI_FORMAT.DXGI_FORMAT_P016:      // 4:2:0 16-bit
                    case DXGI_FORMAT.DXGI_FORMAT_420_OPAQUE:// 4:2:0 8-bit
                    case DXGI_FORMAT.DXGI_FORMAT_NV11:      // 4:1:1 8-bit

                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_P208: // 4:2:2 8-bit
                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_V208: // 4:4:0 8-bit
                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_V408: // 4:4:4 8-bit
                                                             // These are JPEG Hardware decode formats (DXGI 1.4)

                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT:
                        // These are Xbox One platform specific types
                        return true;

                    default:
                        return false;
                }

            }

            public static bool IsPacked(DXGI_FORMAT fmt)
            {
                switch (fmt)
                {
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_YUY2: // 4:2:2 8-bit
                    case DXGI_FORMAT.DXGI_FORMAT_Y210: // 4:2:2 10-bit
                    case DXGI_FORMAT.DXGI_FORMAT_Y216: // 4:2:2 16-bit
                        return true;

                    default:
                        return false;
                }
            }

            public static void assert(bool b)
            {
                if (!b)
                {
                    throw new Exception();
                }
            }



            public static bool IsValid(DXGI_FORMAT fmt)
            {
                return (((int)fmt) >= 1 && ((int)fmt) <= 190);
            }
            public static bool IsPalettized(DXGI_FORMAT fmt)
            {
                switch (fmt)
                {
                    case DXGI_FORMAT.DXGI_FORMAT_AI44:
                    case DXGI_FORMAT.DXGI_FORMAT_IA44:
                    case DXGI_FORMAT.DXGI_FORMAT_P8:
                    case DXGI_FORMAT.DXGI_FORMAT_A8P8:
                        return true;

                    default:
                        return false;
                }
            }


            public static int BitsPerPixel(DXGI_FORMAT fmt)
            {
                switch (fmt)
                {
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_SINT:
                        return 128;

                    case DXGI_FORMAT.DXGI_FORMAT_R32G32B32_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32B32_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32B32_SINT:
                        return 96;

                    case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G32_SINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32G8X24_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_Y416:
                    case DXGI_FORMAT.DXGI_FORMAT_Y210:
                    case DXGI_FORMAT.DXGI_FORMAT_Y216:
                        return 64;

                    case DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R11G11B10_FLOAT:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_SINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R16G16_SINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_D32_FLOAT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R32_SINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R24G8_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_D24_UNORM_S8_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_X24_TYPELESS_G8_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R9G9B9E5_SHAREDEXP:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_AYUV:
                    case DXGI_FORMAT.DXGI_FORMAT_Y410:
                    case DXGI_FORMAT.DXGI_FORMAT_YUY2:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_R10G10B10_7E3_A2_FLOAT:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_R10G10B10_6E4_A2_FLOAT:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM:
                        return 32;

                    case DXGI_FORMAT.DXGI_FORMAT_P010:
                    case DXGI_FORMAT.DXGI_FORMAT_P016:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT:
                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_V408:
                        return 24;

                    case DXGI_FORMAT.DXGI_FORMAT_R8G8_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R8G8_SINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R16_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R16_FLOAT:
                    case DXGI_FORMAT.DXGI_FORMAT_D16_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R16_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R16_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R16_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R16_SINT:
                    case DXGI_FORMAT.DXGI_FORMAT_B5G6R5_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_A8P8:
                    case DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM:
                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_P208:
                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_V208:
                        return 16;

                    case DXGI_FORMAT.DXGI_FORMAT_NV12:
                    case DXGI_FORMAT.DXGI_FORMAT_420_OPAQUE:
                    case DXGI_FORMAT.DXGI_FORMAT_NV11:
                        return 12;

                    case DXGI_FORMAT.DXGI_FORMAT_R8_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R8_UINT:
                    case DXGI_FORMAT.DXGI_FORMAT_R8_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_R8_SINT:
                    case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_AI44:
                    case DXGI_FORMAT.DXGI_FORMAT_IA44:
                    case DXGI_FORMAT.DXGI_FORMAT_P8:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_R4G4_UNORM:
                        return 8;

                    case DXGI_FORMAT.DXGI_FORMAT_R1_UNORM:
                        return 1;

                    case DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
                        return 4;

                    case DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
                        return 8;

                    default:
                        return 0;
                }
            }



            public static int ComputeScanlines(DXGI_FORMAT fmt, int height)
            {
                switch (fmt)
                {
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
                    case DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_TYPELESS:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
                    case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
                        assert(IsCompressed(fmt));
                        return Math.Max(1, (height + 3) / 4);

                    case DXGI_FORMAT.DXGI_FORMAT_NV11:
                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_P208:
                        assert(IsPlanar(fmt));
                        return height * 2;

                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_V208:
                        assert(IsPlanar(fmt));
                        return height + (((height + 1) >> 1) * 2);

                    case DXGI_FORMAT.WIN10_DXGI_FORMAT_V408:
                        assert(IsPlanar(fmt));
                        return height + ((height >> 1) * 4);

                    case DXGI_FORMAT.DXGI_FORMAT_NV12:
                    case DXGI_FORMAT.DXGI_FORMAT_P010:
                    case DXGI_FORMAT.DXGI_FORMAT_P016:
                    case DXGI_FORMAT.DXGI_FORMAT_420_OPAQUE:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
                    case DXGI_FORMAT.XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT:
                        assert(IsPlanar(fmt));
                        return height + ((height + 1) >> 1);

                    default:
                        assert(IsValid(fmt));
                        assert(!IsCompressed(fmt) && !IsPlanar(fmt));
                        return height;
                }
            }





            public static uint DDS_HEADER_FLAGS_TEXTURE = 0x00001007; // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT 
            public static uint DDS_HEADER_FLAGS_MIPMAP = 0x00020000;  // DDSD_MIPMAPCOUNT
            public static uint DDS_HEADER_FLAGS_VOLUME = 0x00800000;  // DDSD_DEPTH
            public static uint DDS_HEADER_FLAGS_PITCH = 0x00000008; // DDSD_PITCH
            public static uint DDS_HEADER_FLAGS_LINEARSIZE = 0x00080000;  // DDSD_LINEARSIZE

            public static uint DDS_HEIGHT = 0x00000002; // DDSD_HEIGHT
            public static uint DDS_WIDTH = 0x00000004; // DDSD_WIDTH

            public static uint DDS_SURFACE_FLAGS_TEXTURE = 0x00001000; // DDSCAPS_TEXTURE
            public static uint DDS_SURFACE_FLAGS_MIPMAP = 0x00400008; // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
            public static uint DDS_SURFACE_FLAGS_CUBEMAP = 0x00000008; // DDSCAPS_COMPLEX

            public static uint DDS_CUBEMAP_POSITIVEX = 0x00000600; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
            public static uint DDS_CUBEMAP_NEGATIVEX = 0x00000a00; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
            public static uint DDS_CUBEMAP_POSITIVEY = 0x00001200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
            public static uint DDS_CUBEMAP_NEGATIVEY = 0x00002200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
            public static uint DDS_CUBEMAP_POSITIVEZ = 0x00004200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
            public static uint DDS_CUBEMAP_NEGATIVEZ = 0x00008200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ

            public static uint DDS_CUBEMAP_ALLFACES = (DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |
                               DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |
                               DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ);

            public static uint DDS_CUBEMAP = 0x00000200; // DDSCAPS2_CUBEMAP

            public static uint DDS_FLAGS_VOLUME = 0x00200000; // DDSCAPS2_VOLUME

            public static uint DDS_MAGIC = 0x20534444; // "DDS "



            //-------------------------------------------------------------------------------------
            // Encodes DDS file header (magic value, header, optional DX10 extended header)
            //-------------------------------------------------------------------------------------
            public static bool _EncodeDDSHeader(TexMetadata metadata, uint flags, BinaryWriter bw, out int required)
            {
                //const uint MAX_HEADER_SIZE = sizeof(uint) + 31*4/*sizeof(DDS_HEADER)*/ + 5*4/*sizeof(DDS_HEADER_DXT10)*/;
                //byte header[MAX_HEADER_SIZE];

                required = 0;
                if (!IsValid(metadata.format))
                    return false;

                if (IsPalettized(metadata.format))
                    return false;// HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);

                if (metadata.arraySize > 1)
                {
                    if ((metadata.arraySize != 6) || (metadata.dimension != TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D) || !(metadata.IsCubemap()))
                    {
                        // Texture1D arrays, Texture2D arrays, and Cubemap arrays must be stored using 'DX10' extended header
                        flags |= (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT;
                    }
                }

                if ((flags & (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT_MISC2)!=0)
                {
                    flags |= (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT;
                }

                DDS_PIXELFORMAT ddpf = new DDS_PIXELFORMAT(0);
                if (!((flags & (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT)!=0))
                {
                    switch (metadata.format)
                    {
                        case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A8B8G8R8; break;
                        case DXGI_FORMAT.DXGI_FORMAT_R16G16_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_G16R16; break;
                        case DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A8L8; break;
                        case DXGI_FORMAT.DXGI_FORMAT_R16_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_L16; break;
                        case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_L8; break;
                        case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A8; break;
                        case DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_R8G8_B8G8; break;
                        case DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_G8R8_G8B8; break;
                        case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_DXT1; break;
                        case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: ddpf = metadata.IsPMAlpha() ? (DDS_PIXELFORMAT.DDSPF_DXT2) : (DDS_PIXELFORMAT.DDSPF_DXT3); break;
                        case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: ddpf = metadata.IsPMAlpha() ? (DDS_PIXELFORMAT.DDSPF_DXT4) : (DDS_PIXELFORMAT.DDSPF_DXT5); break;
                        case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_BC4_UNORM; break;
                        case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM: ddpf = DDS_PIXELFORMAT.DDSPF_BC4_SNORM; break;
                        case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_BC5_UNORM; break;
                        case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM: ddpf = DDS_PIXELFORMAT.DDSPF_BC5_SNORM; break;
                        case DXGI_FORMAT.DXGI_FORMAT_B5G6R5_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_R5G6B5; break;
                        case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A1R5G5B5; break;
                        case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A8R8G8B8; break; // DXGI 1.1
                        case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_X8R8G8B8; break; // DXGI 1.1
                        case DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM: ddpf = DDS_PIXELFORMAT.DDSPF_A4R4G4B4; break; // DXGI 1.2
                        case DXGI_FORMAT.DXGI_FORMAT_YUY2: ddpf = DDS_PIXELFORMAT.DDSPF_YUY2; break; // DXGI 1.2

                        // Legacy D3DX formats using D3DFMT enum value as FourCC
                        case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT:
                            ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 116;  // D3DFMT_A32B32G32R32F
                            break;
                        case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT:
                            ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 113;  // D3DFMT_A16B16G16R16F
                            break;
                        case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM:
                            ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 36;  // D3DFMT_A16B16G16R16
                            break;
                        case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SNORM:
                            ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 110;  // D3DFMT_Q16W16V16U16
                            break;
                        case DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT:
                            ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 115;  // D3DFMT_G32R32F
                            break;
                        case DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT:
                            ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 112;  // D3DFMT_G16R16F
                            break;
                        case DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT:
                            ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 114;  // D3DFMT_R32F
                            break;
                        case DXGI_FORMAT.DXGI_FORMAT_R16_FLOAT:
                            ddpf.dwSize = 32/*sizeof(DDS_PIXELFORMAT)*/; ddpf.dwFlags = DDS_PIXELFORMAT.DDS_FOURCC; ddpf.dwFourCC = 111;  // D3DFMT_R16F
                            break;
                    }
                }

                int sizeofddsheader = 31 * 4;
                int sizeofddsheader10 = 5 * 4;

                required = 4/*sizeof(uint32_t)*/ + sizeofddsheader/*sizeof(DDS_HEADER)*/;

                if (ddpf.dwSize == 0)
                    required += sizeofddsheader10;// sizeof(DDS_HEADER_DXT10);

                //if (!pDestination)
                //    return S_OK;

                //if (maxsize < required)
                //    return E_NOT_SUFFICIENT_BUFFER;

                //*reinterpret_cast<uint32_t*>(pDestination) = DDS_MAGIC;
                //bw.Write(DDS_MAGIC);

                var header = new DDS_HEADER();
                //auto header = reinterpret_cast<DDS_HEADER*>(reinterpret_cast<uint8_t*>(pDestination) + sizeof(uint32_t));
                //assert(header);

                //memset(header, 0, sizeof(DDS_HEADER));
                header.dwSize = (uint)sizeofddsheader;// sizeof(DDS_HEADER);
                header.dwFlags = DDS_HEADER_FLAGS_TEXTURE;
                header.dwCaps = DDS_SURFACE_FLAGS_TEXTURE;

                if (metadata.mipLevels > 0)
                {
                    header.dwFlags |= DDS_HEADER_FLAGS_MIPMAP;

//# ifdef _M_X64
//                    if (metadata.mipLevels > 0xFFFFFFFF)
//                        return E_INVALIDARG;
//#endif

                    header.dwMipMapCount = (uint)(metadata.mipLevels);

                    if (header.dwMipMapCount > 1)
                        header.dwCaps |= DDS_SURFACE_FLAGS_MIPMAP;
                }

                switch (metadata.dimension)
                {
                    case TEX_DIMENSION.TEX_DIMENSION_TEXTURE1D:
//# ifdef _M_X64
//                        if (metadata.width > 0xFFFFFFFF)
//                            return E_INVALIDARG;
//#endif

                        header.dwWidth = (uint)(metadata.width);
                        header.dwHeight = header.dwDepth = 1;
                        break;

                    case TEX_DIMENSION.TEX_DIMENSION_TEXTURE2D:
//# ifdef _M_X64
//                        if (metadata.height > 0xFFFFFFFF
//                             || metadata.width > 0xFFFFFFFF)
//                            return E_INVALIDARG;
//#endif

                        header.dwHeight = (uint)(metadata.height);
                        header.dwWidth = (uint)(metadata.width);
                        header.dwDepth = 1;

                        if (metadata.IsCubemap())
                        {
                            header.dwCaps |= DDS_SURFACE_FLAGS_CUBEMAP;
                            header.dwCaps2 |= DDS_CUBEMAP_ALLFACES;
                        }
                        break;

                    case TEX_DIMENSION.TEX_DIMENSION_TEXTURE3D:
//# ifdef _M_X64
//                        if (metadata.height > 0xFFFFFFFF
//                             || metadata.width > 0xFFFFFFFF
//                             || metadata.depth > 0xFFFFFFFF)
//                            return E_INVALIDARG;
//#endif

                        header.dwFlags |= DDS_HEADER_FLAGS_VOLUME;
                        header.dwCaps2 |= DDS_FLAGS_VOLUME;
                        header.dwHeight = (uint)(metadata.height);
                        header.dwWidth = (uint)(metadata.width);
                        header.dwDepth = (uint)(metadata.depth);
                        break;

                    default:
                        return false;// E_FAIL;
                }

                int rowPitch, slicePitch;
                ComputePitch(metadata.format, metadata.width, metadata.height, out rowPitch, out slicePitch, 0);// (uint)CP_FLAGS.CP_FLAGS_NONE);

//# ifdef _M_X64
//                if (slicePitch > 0xFFFFFFFF
//                     || rowPitch > 0xFFFFFFFF)
//                    return E_FAIL;
//#endif

                if (IsCompressed(metadata.format))
                {
                    header.dwFlags |= DDS_HEADER_FLAGS_LINEARSIZE;
                    header.dwPitchOrLinearSize = (uint)(slicePitch);
                }
                else
                {
                    header.dwFlags |= DDS_HEADER_FLAGS_PITCH;
                    header.dwPitchOrLinearSize = (uint)(rowPitch);
                }

                var ext = new DDS_HEADER_DXT10();
                if (ddpf.dwSize == 0)
                {
                    header.ddspf = DDS_PIXELFORMAT.DDSPF_DX10;
                    //memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT) );

                    //auto ext = reinterpret_cast<DDS_HEADER_DXT10*>(reinterpret_cast<uint8_t*>(header) + sizeof(DDS_HEADER));
                    //assert(ext);
                    //var ext = new DDS_HEADER_DXT10();

                    //memset(ext, 0, sizeof(DDS_HEADER_DXT10));
                    ext.dxgiFormat = metadata.format;
                    ext.resourceDimension = (uint)metadata.dimension;

//# ifdef _M_X64
//                    if (metadata.arraySize > 0xFFFFFFFF)
//                        return E_INVALIDARG;
//#endif

                    //static_assert(TEX_MISC_TEXTURECUBE == DDS_RESOURCE_MISC_TEXTURECUBE, "DDS header mismatch");

                    ext.miscFlag = metadata.miscFlags & ~((uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE);

                    if ((metadata.miscFlags & (uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE)!=0)
                    {
                        ext.miscFlag |= (uint)TEX_MISC_FLAG.TEX_MISC_TEXTURECUBE;
                        assert((metadata.arraySize % 6) == 0);
                        ext.arraySize = (uint)(metadata.arraySize / 6);
                    }
                    else
                    {
                        ext.arraySize = (uint)(metadata.arraySize);
                    }

                    //static_assert(TEX_MISC2_ALPHA_MODE_MASK == DDS_MISC_FLAGS2_ALPHA_MODE_MASK, "DDS header mismatch");
                    //static_assert(TEX_ALPHA_MODE_UNKNOWN == DDS_ALPHA_MODE_UNKNOWN, "DDS header mismatch");
                    //static_assert(TEX_ALPHA_MODE_STRAIGHT == DDS_ALPHA_MODE_STRAIGHT, "DDS header mismatch");
                    //static_assert(TEX_ALPHA_MODE_PREMULTIPLIED == DDS_ALPHA_MODE_PREMULTIPLIED, "DDS header mismatch");
                    //static_assert(TEX_ALPHA_MODE_OPAQUE == DDS_ALPHA_MODE_OPAQUE, "DDS header mismatch");
                    //static_assert(TEX_ALPHA_MODE_CUSTOM == DDS_ALPHA_MODE_CUSTOM, "DDS header mismatch");

                    if ((flags & (uint)DDS_FLAGS.DDS_FLAGS_FORCE_DX10_EXT_MISC2)!=0)
                    {
                        // This was formerly 'reserved'. D3DX10 and D3DX11 will fail if this value is anything other than 0
                        ext.miscFlags2 = metadata.miscFlags2;
                    }
                }
                else
                {
                    header.ddspf = ddpf;
                    //memcpy_s(&header.ddspf, sizeof(header.ddspf), &ddpf, sizeof(ddpf) );
                }


                //magic write
                bw.Write(DDS_MAGIC);

                //write header
                bw.Write(header.dwSize);
                bw.Write(header.dwFlags);
                bw.Write(header.dwHeight);
                bw.Write(header.dwWidth);
                bw.Write(header.dwPitchOrLinearSize);
                bw.Write(header.dwDepth); // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags
                bw.Write(header.dwMipMapCount);

                //public uint dwReserved1[11]; //x11
                for (int i = 0; i < 11; i++) bw.Write(0);

                //bw.Write(header.ddspf);
                bw.Write(header.ddspf.dwSize);
                bw.Write(header.ddspf.dwFlags);
                bw.Write(header.ddspf.dwFourCC);
                bw.Write(header.ddspf.dwRGBBitCount);
                bw.Write(header.ddspf.dwRBitMask);
                bw.Write(header.ddspf.dwGBitMask);
                bw.Write(header.ddspf.dwBBitMask);
                bw.Write(header.ddspf.dwABitMask);

                bw.Write(header.dwCaps);
                bw.Write(header.dwCaps2);
                bw.Write(header.dwCaps3);
                bw.Write(header.dwCaps4);
                bw.Write(header.dwReserved2);


                if (ddpf.dwSize == 0)
                {
                    //write ext
                    bw.Write((uint)ext.dxgiFormat);
                    bw.Write(ext.resourceDimension);
                    bw.Write(ext.miscFlag); // see DDS_RESOURCE_MISC_FLAG
                    bw.Write(ext.arraySize);
                    bw.Write(ext.miscFlags2); // see DDS_MISC_FLAGS2
                }

                return true; //S_OK
            }


            public static bool _ReadDDSHeader(BinaryReader br, out DDS_HEADER header, out DDS_HEADER_DXT10 header10, out bool useheader10)
            {
                var magic = br.ReadUInt32();
                if (magic != DDS_MAGIC) throw new Exception("Invalid DDS magic!");

                //var header = new DDS_HEADER();
                //var header10 = new DDS_HEADER_DXT10();
                int sizeofddsheader = 31 * 4;
                int sizeofddspixelformat = 8 * 4;

                header.dwSize = br.ReadUInt32();
                header.dwFlags = br.ReadUInt32();
                header.dwHeight = br.ReadUInt32();
                header.dwWidth = br.ReadUInt32();
                header.dwPitchOrLinearSize = br.ReadUInt32();
                header.dwDepth = br.ReadUInt32(); // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags
                header.dwMipMapCount = br.ReadUInt32();

                //public uint dwReserved1[11]; //x11
                for (int i = 0; i < 11; i++) br.ReadUInt32();

                header.ddspf.dwSize = br.ReadUInt32();
                header.ddspf.dwFlags = br.ReadUInt32();
                header.ddspf.dwFourCC = br.ReadUInt32();
                header.ddspf.dwRGBBitCount = br.ReadUInt32();
                header.ddspf.dwRBitMask = br.ReadUInt32();
                header.ddspf.dwGBitMask = br.ReadUInt32();
                header.ddspf.dwBBitMask = br.ReadUInt32();
                header.ddspf.dwABitMask = br.ReadUInt32();

                header.dwCaps = br.ReadUInt32();
                header.dwCaps2 = br.ReadUInt32();
                header.dwCaps3 = br.ReadUInt32();
                header.dwCaps4 = br.ReadUInt32();
                header.dwReserved2 = br.ReadUInt32();



                if(((DDS_PIXELFORMAT.DDS_FOURCC & header.ddspf.dwFlags) > 0) && 
                    (DDS_PIXELFORMAT.MAKEFOURCC('D', 'X', '1', '0') == header.ddspf.dwFourCC))
                {
                    header10.dxgiFormat = (DXGI_FORMAT)br.ReadUInt32();
                    header10.resourceDimension = br.ReadUInt32();
                    header10.miscFlag = br.ReadUInt32(); // see DDS_RESOURCE_MISC_FLAG
                    header10.arraySize = br.ReadUInt32();
                    header10.miscFlags2 = br.ReadUInt32(); // see DDS_MISC_FLAGS2
                    useheader10 = true;
                }
                else
                {
                    header10 = new DDS_HEADER_DXT10();
                    useheader10 = false;
                }

                if (header.dwSize != sizeofddsheader || header.ddspf.dwSize != sizeofddspixelformat)
                {
                    throw new Exception("Invalid DDS header size");
                }


                return true;
            }


        }

        public enum CP_FLAGS : uint
        {
            CP_FLAGS_NONE = 0x0,      // Normal operation
            CP_FLAGS_LEGACY_DWORD = 0x1,      // Assume pitch is DWORD aligned instead of BYTE aligned
            CP_FLAGS_PARAGRAPH = 0x2,      // Assume pitch is 16-byte aligned instead of BYTE aligned
            CP_FLAGS_YMM = 0x4,      // Assume pitch is 32-byte aligned instead of BYTE aligned
            CP_FLAGS_ZMM = 0x8,      // Assume pitch is 64-byte aligned instead of BYTE aligned
            CP_FLAGS_PAGE4K = 0x200,    // Assume pitch is 4096-byte aligned instead of BYTE aligned
            CP_FLAGS_24BPP = 0x10000,  // Override with a legacy 24 bits-per-pixel format size
            CP_FLAGS_16BPP = 0x20000,  // Override with a legacy 16 bits-per-pixel format size
            CP_FLAGS_8BPP = 0x40000,  // Override with a legacy 8 bits-per-pixel format size
        }


        public class ImageStruct
        {
            public int Width;
            public int Height;
            //property int Stride;
            public int Format;
            public int MipMapLevels;
            public byte[] Data;

            int GetRowPitch()
            {
                int rowPitch;
                int slicePitch;
                DXTex.ComputePitch((DXGI_FORMAT)Format, Width, Height, out rowPitch, out slicePitch, 0);
                return rowPitch;
            }

            int GetSlicePitch()
            {
                int rowPitch;
                int slicePitch;
                DXTex.ComputePitch((DXGI_FORMAT)Format, Width, Height, out rowPitch, out slicePitch, 0);
                return slicePitch;
            }


        }

        public enum DXGI_FORMAT : uint
        {
            DXGI_FORMAT_UNKNOWN = 0,
            DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
            DXGI_FORMAT_R32G32B32A32_FLOAT = 2,
            DXGI_FORMAT_R32G32B32A32_UINT = 3,
            DXGI_FORMAT_R32G32B32A32_SINT = 4,
            DXGI_FORMAT_R32G32B32_TYPELESS = 5,
            DXGI_FORMAT_R32G32B32_FLOAT = 6,
            DXGI_FORMAT_R32G32B32_UINT = 7,
            DXGI_FORMAT_R32G32B32_SINT = 8,
            DXGI_FORMAT_R16G16B16A16_TYPELESS = 9,
            DXGI_FORMAT_R16G16B16A16_FLOAT = 10,
            DXGI_FORMAT_R16G16B16A16_UNORM = 11,
            DXGI_FORMAT_R16G16B16A16_UINT = 12,
            DXGI_FORMAT_R16G16B16A16_SNORM = 13,
            DXGI_FORMAT_R16G16B16A16_SINT = 14,
            DXGI_FORMAT_R32G32_TYPELESS = 15,
            DXGI_FORMAT_R32G32_FLOAT = 16,
            DXGI_FORMAT_R32G32_UINT = 17,
            DXGI_FORMAT_R32G32_SINT = 18,
            DXGI_FORMAT_R32G8X24_TYPELESS = 19,
            DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20,
            DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
            DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22,
            DXGI_FORMAT_R10G10B10A2_TYPELESS = 23,
            DXGI_FORMAT_R10G10B10A2_UNORM = 24,
            DXGI_FORMAT_R10G10B10A2_UINT = 25,
            DXGI_FORMAT_R11G11B10_FLOAT = 26,
            DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
            DXGI_FORMAT_R8G8B8A8_UNORM = 28,
            DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
            DXGI_FORMAT_R8G8B8A8_UINT = 30,
            DXGI_FORMAT_R8G8B8A8_SNORM = 31,
            DXGI_FORMAT_R8G8B8A8_SINT = 32,
            DXGI_FORMAT_R16G16_TYPELESS = 33,
            DXGI_FORMAT_R16G16_FLOAT = 34,
            DXGI_FORMAT_R16G16_UNORM = 35,
            DXGI_FORMAT_R16G16_UINT = 36,
            DXGI_FORMAT_R16G16_SNORM = 37,
            DXGI_FORMAT_R16G16_SINT = 38,
            DXGI_FORMAT_R32_TYPELESS = 39,
            DXGI_FORMAT_D32_FLOAT = 40,
            DXGI_FORMAT_R32_FLOAT = 41,
            DXGI_FORMAT_R32_UINT = 42,
            DXGI_FORMAT_R32_SINT = 43,
            DXGI_FORMAT_R24G8_TYPELESS = 44,
            DXGI_FORMAT_D24_UNORM_S8_UINT = 45,
            DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46,
            DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47,
            DXGI_FORMAT_R8G8_TYPELESS = 48,
            DXGI_FORMAT_R8G8_UNORM = 49,
            DXGI_FORMAT_R8G8_UINT = 50,
            DXGI_FORMAT_R8G8_SNORM = 51,
            DXGI_FORMAT_R8G8_SINT = 52,
            DXGI_FORMAT_R16_TYPELESS = 53,
            DXGI_FORMAT_R16_FLOAT = 54,
            DXGI_FORMAT_D16_UNORM = 55,
            DXGI_FORMAT_R16_UNORM = 56,
            DXGI_FORMAT_R16_UINT = 57,
            DXGI_FORMAT_R16_SNORM = 58,
            DXGI_FORMAT_R16_SINT = 59,
            DXGI_FORMAT_R8_TYPELESS = 60,
            DXGI_FORMAT_R8_UNORM = 61,
            DXGI_FORMAT_R8_UINT = 62,
            DXGI_FORMAT_R8_SNORM = 63,
            DXGI_FORMAT_R8_SINT = 64,
            DXGI_FORMAT_A8_UNORM = 65,
            DXGI_FORMAT_R1_UNORM = 66,
            DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67,
            DXGI_FORMAT_R8G8_B8G8_UNORM = 68,
            DXGI_FORMAT_G8R8_G8B8_UNORM = 69,
            DXGI_FORMAT_BC1_TYPELESS = 70,
            DXGI_FORMAT_BC1_UNORM = 71,
            DXGI_FORMAT_BC1_UNORM_SRGB = 72,
            DXGI_FORMAT_BC2_TYPELESS = 73,
            DXGI_FORMAT_BC2_UNORM = 74,
            DXGI_FORMAT_BC2_UNORM_SRGB = 75,
            DXGI_FORMAT_BC3_TYPELESS = 76,
            DXGI_FORMAT_BC3_UNORM = 77,
            DXGI_FORMAT_BC3_UNORM_SRGB = 78,
            DXGI_FORMAT_BC4_TYPELESS = 79,
            DXGI_FORMAT_BC4_UNORM = 80,
            DXGI_FORMAT_BC4_SNORM = 81,
            DXGI_FORMAT_BC5_TYPELESS = 82,
            DXGI_FORMAT_BC5_UNORM = 83,
            DXGI_FORMAT_BC5_SNORM = 84,
            DXGI_FORMAT_B5G6R5_UNORM = 85,
            DXGI_FORMAT_B5G5R5A1_UNORM = 86,
            DXGI_FORMAT_B8G8R8A8_UNORM = 87,
            DXGI_FORMAT_B8G8R8X8_UNORM = 88,
            DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
            DXGI_FORMAT_B8G8R8A8_TYPELESS = 90,
            DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91,
            DXGI_FORMAT_B8G8R8X8_TYPELESS = 92,
            DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93,
            DXGI_FORMAT_BC6H_TYPELESS = 94,
            DXGI_FORMAT_BC6H_UF16 = 95,
            DXGI_FORMAT_BC6H_SF16 = 96,
            DXGI_FORMAT_BC7_TYPELESS = 97,
            DXGI_FORMAT_BC7_UNORM = 98,
            DXGI_FORMAT_BC7_UNORM_SRGB = 99,
            DXGI_FORMAT_AYUV = 100,
            DXGI_FORMAT_Y410 = 101,
            DXGI_FORMAT_Y416 = 102,
            DXGI_FORMAT_NV12 = 103,
            DXGI_FORMAT_P010 = 104,
            DXGI_FORMAT_P016 = 105,
            DXGI_FORMAT_420_OPAQUE = 106,
            DXGI_FORMAT_YUY2 = 107,
            DXGI_FORMAT_Y210 = 108,
            DXGI_FORMAT_Y216 = 109,
            DXGI_FORMAT_NV11 = 110,
            DXGI_FORMAT_AI44 = 111,
            DXGI_FORMAT_IA44 = 112,
            DXGI_FORMAT_P8 = 113,
            DXGI_FORMAT_A8P8 = 114,
            DXGI_FORMAT_B4G4R4A4_UNORM = 115,
            DXGI_FORMAT_FORCE_UINT = 0xffffffff,


            XBOX_DXGI_FORMAT_R10G10B10_7E3_A2_FLOAT = 116,
            XBOX_DXGI_FORMAT_R10G10B10_6E4_A2_FLOAT = 117,
            XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT = 118,
            XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS = 119,
            XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT = 120,

            WIN10_DXGI_FORMAT_P208 = 130,
            WIN10_DXGI_FORMAT_V208 = 131,
            WIN10_DXGI_FORMAT_V408 = 132,

            XBOX_DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM = 189,
            XBOX_DXGI_FORMAT_R4G4_UNORM = 190,


        }

        public enum TEX_DIMENSION : uint
        { // Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION
            TEX_DIMENSION_TEXTURE1D = 2,
            TEX_DIMENSION_TEXTURE2D = 3,
            TEX_DIMENSION_TEXTURE3D = 4,
        }

        public enum TEX_MISC_FLAG : long // Subset here matches D3D10_RESOURCE_MISC_FLAG and D3D11_RESOURCE_MISC_FLAG
        {
            TEX_MISC_TEXTURECUBE = 0x4L,
            TEX_MISC2_ALPHA_MODE_MASK = 0x7L,
        }

        public enum TEX_ALPHA_MODE    // Matches DDS_ALPHA_MODE, encoded in MISC_FLAGS2
        {
            TEX_ALPHA_MODE_UNKNOWN = 0,
            TEX_ALPHA_MODE_STRAIGHT = 1,
            TEX_ALPHA_MODE_PREMULTIPLIED = 2,
            TEX_ALPHA_MODE_OPAQUE = 3,
            TEX_ALPHA_MODE_CUSTOM = 4,
        }


        public struct DDS_HEADER
        {
            public uint dwSize;
            public uint dwFlags;
            public uint dwHeight;
            public uint dwWidth;
            public uint dwPitchOrLinearSize;
            public uint dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags
            public uint dwMipMapCount;
            //public uint dwReserved1[11]; //x11
            public DDS_PIXELFORMAT ddspf;
            public uint dwCaps;
            public uint dwCaps2;
            public uint dwCaps3;
            public uint dwCaps4;
            public uint dwReserved2;
        };

        public struct DDS_HEADER_DXT10
        {
            public DXGI_FORMAT dxgiFormat;
            public uint resourceDimension;
            public uint miscFlag; // see DDS_RESOURCE_MISC_FLAG
            public uint arraySize;
            public uint miscFlags2; // see DDS_MISC_FLAGS2
        };

        public struct DDS_PIXELFORMAT
        {
            public uint dwSize;
            public uint dwFlags;
            public uint dwFourCC;
            public uint dwRGBBitCount;
            public uint dwRBitMask;
            public uint dwGBitMask;
            public uint dwBBitMask;
            public uint dwABitMask;

            public DDS_PIXELFORMAT(uint val)
            {
                dwSize = val;
                dwFlags = val;
                dwFourCC = val;
                dwRGBBitCount = val;
                dwRBitMask = val;
                dwGBitMask = val;
                dwBBitMask = val;
                dwABitMask = val;
            }
            public DDS_PIXELFORMAT(uint size, uint flags, uint fourcc, uint rgbbitcount, uint rmask, uint gmask, uint bmask, uint amask)
            {
                dwSize = size;
                dwFlags = flags;
                dwFourCC = fourcc;
                dwRGBBitCount = rgbbitcount;
                dwRBitMask = rmask;
                dwGBitMask = gmask;
                dwBBitMask = bmask;
                dwABitMask = amask;
            }


            public static uint MAKEFOURCC(char ch0, char ch1, char ch2, char ch3)
            {
                return
                    ((uint)(byte)(ch0) | ((uint)(byte)(ch1) << 8) |
                    ((uint)(byte)(ch2) << 16) | ((uint)(byte)(ch3) << 24));
            }
            public static uint DDS_FOURCC = 0x00000004; // DDPF_FOURCC
            public static uint DDS_RGB = 0x00000040;  // DDPF_RGB
            public static uint DDS_RGBA = 0x00000041;  // DDPF_RGB | DDPF_ALPHAPIXELS
            public static uint DDS_LUMINANCE = 0x00020000; // DDPF_LUMINANCE
            public static uint DDS_LUMINANCEA = 0x00020001;  // DDPF_LUMINANCE | DDPF_ALPHAPIXELS
            public static uint DDS_ALPHA = 0x00000002; // DDPF_ALPHA
            public static uint DDS_PAL8 = 0x00000020;  // DDPF_PALETTEINDEXED8


            public static DDS_PIXELFORMAT DDSPF_DXT1 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','T','1'), 0, 0, 0, 0, 0 );
            public static DDS_PIXELFORMAT DDSPF_DXT2 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D', 'X', 'T', '2'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_DXT3 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','T','3'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_DXT4 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','T','4'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_DXT5 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','T','5'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_BC4_UNORM = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('B','C','4','U'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_BC4_SNORM = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('B','C','4','S'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_BC5_UNORM = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('B','C','5','U'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_BC5_SNORM = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('B','C','5','S'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_R8G8_B8G8 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('R','G','B','G'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_G8R8_G8B8 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('G','R','G','B'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_YUY2 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0);
            public static DDS_PIXELFORMAT DDSPF_A8R8G8B8 = new DDS_PIXELFORMAT(32, DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
            public static DDS_PIXELFORMAT DDSPF_X8R8G8B8 = new DDS_PIXELFORMAT(32, DDS_RGB,  0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
            public static DDS_PIXELFORMAT DDSPF_A8B8G8R8 = new DDS_PIXELFORMAT(32, DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
            public static DDS_PIXELFORMAT DDSPF_X8B8G8R8 = new DDS_PIXELFORMAT(32, DDS_RGB,  0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000);
            public static DDS_PIXELFORMAT DDSPF_G16R16 = new DDS_PIXELFORMAT(32, DDS_RGB,  0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000);
            public static DDS_PIXELFORMAT DDSPF_R5G6B5 = new DDS_PIXELFORMAT(32, DDS_RGB, 0, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000);
            public static DDS_PIXELFORMAT DDSPF_A1R5G5B5 = new DDS_PIXELFORMAT(32, DDS_RGBA, 0, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000);
            public static DDS_PIXELFORMAT DDSPF_A4R4G4B4 = new DDS_PIXELFORMAT(32, DDS_RGBA, 0, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000);
            public static DDS_PIXELFORMAT DDSPF_R8G8B8 = new DDS_PIXELFORMAT(32, DDS_RGB, 0, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
            public static DDS_PIXELFORMAT DDSPF_L8 = new DDS_PIXELFORMAT(32, DDS_LUMINANCE, 0,  8, 0xff, 0x00, 0x00, 0x00);
            public static DDS_PIXELFORMAT DDSPF_L16 = new DDS_PIXELFORMAT(32, DDS_LUMINANCE, 0, 16, 0xffff, 0x0000, 0x0000, 0x0000);
            public static DDS_PIXELFORMAT DDSPF_A8L8 = new DDS_PIXELFORMAT(32, DDS_LUMINANCEA, 0, 16, 0x00ff, 0x0000, 0x0000, 0xff00);
            public static DDS_PIXELFORMAT DDSPF_A8 = new DDS_PIXELFORMAT(32, DDS_ALPHA, 0, 8, 0x00, 0x00, 0x00, 0xff);

            // D3DFMT_A2R10G10B10/D3DFMT_A2B10G10R10 should be written using DX10 extension to avoid D3DX 10:10:10:2 reversal issue

            // This indicates the DDS_HEADER_DXT10 extension is present (the format is in dxgiFormat)
            public static DDS_PIXELFORMAT DDSPF_DX10 = new DDS_PIXELFORMAT(32, DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0);





            private bool ISBITMASK(uint r, uint g, uint b, uint a)
            {
                return ((dwRBitMask == r) && (dwGBitMask == g) && (dwBBitMask == b) && (dwABitMask == a));
            }

            public DXGI_FORMAT GetDXGIFormat()
            {

                if ((dwFlags & DDS_RGB) > 0)
                {
                    // Note that sRGB formats are written using the "DX10" extended header

                    switch (dwRGBBitCount)
                    {
                        case 32:
                            if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000))
                            {
                                return DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM;
                            }

                            if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000))
                            {
                                return DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
                            }

                            if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000))
                            {
                                return DXGI_FORMAT.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.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.DXGI_FORMAT_R16G16_UNORM;
                            }

                            if (ISBITMASK(0xffffffff, 0x00000000, 0x00000000, 0x00000000))
                            {
                                // Only 32-bit color channel format in D3D9 was R32F
                                return DXGI_FORMAT.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.DXGI_FORMAT_B5G5R5A1_UNORM;
                            }
                            if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0x0000))
                            {
                                return DXGI_FORMAT.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.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 ((dwFlags & DDS_LUMINANCE) > 0)
                {
                    if (8 == dwRGBBitCount)
                    {
                        if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x00000000))
                        {
                            return DXGI_FORMAT.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 == dwRGBBitCount)
                    {
                        if (ISBITMASK(0x0000ffff, 0x00000000, 0x00000000, 0x00000000))
                        {
                            return DXGI_FORMAT.DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension
                        }
                        if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x0000ff00))
                        {
                            return DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension
                        }
                    }
                }
                else if ((dwFlags & DDS_ALPHA) > 0)
                {
                    if (8 == dwRGBBitCount)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_A8_UNORM;
                    }
                }
                else if ((dwFlags & DDS_FOURCC) > 0)
                {
                    if (MAKEFOURCC('D', 'X', 'T', '1') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM;
                    }
                    if (MAKEFOURCC('D', 'X', 'T', '3') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM;
                    }
                    if (MAKEFOURCC('D', 'X', 'T', '5') == dwFourCC)
                    {
                        return DXGI_FORMAT.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') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM;
                    }
                    if (MAKEFOURCC('D', 'X', 'T', '4') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM;
                    }

                    if (MAKEFOURCC('A', 'T', 'I', '1') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM;
                    }
                    if (MAKEFOURCC('B', 'C', '4', 'U') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM;
                    }
                    if (MAKEFOURCC('B', 'C', '4', 'S') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM;
                    }

                    if (MAKEFOURCC('A', 'T', 'I', '2') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM;
                    }
                    if (MAKEFOURCC('B', 'C', '5', 'U') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM;
                    }
                    if (MAKEFOURCC('B', 'C', '5', 'S') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM;
                    }

                    // BC6H and BC7 are written using the "DX10" extended header

                    if (MAKEFOURCC('R', 'G', 'B', 'G') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_R8G8_B8G8_UNORM;
                    }
                    if (MAKEFOURCC('G', 'R', 'G', 'B') == dwFourCC)
                    {
                        return DXGI_FORMAT.DXGI_FORMAT_G8R8_G8B8_UNORM;
                    }

                    // Check for D3DFORMAT enums being set here
                    switch (dwFourCC)
                    {
                        case 36: // D3DFMT_A16B16G16R16
                            return DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM;

                        case 110: // D3DFMT_Q16W16V16U16
                            return DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SNORM;

                        case 111: // D3DFMT_R16F
                            return DXGI_FORMAT.DXGI_FORMAT_R16_FLOAT;

                        case 112: // D3DFMT_G16R16F
                            return DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT;

                        case 113: // D3DFMT_A16B16G16R16F
                            return DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT;

                        case 114: // D3DFMT_R32F
                            return DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT;

                        case 115: // D3DFMT_G32R32F
                            return DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT;

                        case 116: // D3DFMT_A32B32G32R32F
                            return DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT;
                    }
                }

                return DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;
            }


        };

        public enum DDS_FLAGS : uint
        {
            DDS_FLAGS_NONE = 0x0,

            DDS_FLAGS_LEGACY_DWORD = 0x1,
            // Assume pitch is DWORD aligned instead of BYTE aligned (used by some legacy DDS files)

            DDS_FLAGS_NO_LEGACY_EXPANSION = 0x2,
            // Do not implicitly convert legacy formats that result in larger pixel sizes (24 bpp, 3:3:2, A8L8, A4L4, P8, A8P8) 

            DDS_FLAGS_NO_R10B10G10A2_FIXUP = 0x4,
            // Do not use work-around for long-standing D3DX DDS file format issue which reversed the 10:10:10:2 color order masks

            DDS_FLAGS_FORCE_RGB = 0x8,
            // Convert DXGI 1.1 BGR formats to DXGI_FORMAT_R8G8B8A8_UNORM to avoid use of optional WDDM 1.1 formats

            DDS_FLAGS_NO_16BPP = 0x10,
            // Conversions avoid use of 565, 5551, and 4444 formats and instead expand to 8888 to avoid use of optional WDDM 1.2 formats

            DDS_FLAGS_EXPAND_LUMINANCE = 0x20,
            // When loading legacy luminance formats expand replicating the color channels rather than leaving them packed (L8, L16, A8L8)

            DDS_FLAGS_FORCE_DX10_EXT = 0x10000,
            // Always use the 'DX10' header extension for DDS writer (i.e. don't try to write DX9 compatible DDS files)

            DDS_FLAGS_FORCE_DX10_EXT_MISC2 = 0x20000,
            // DDS_FLAGS_FORCE_DX10_EXT including miscFlags2 information (result may not be compatible with D3DX10 or D3DX11)
        };





















        internal static byte[] DecompressDxt1(byte[] imageData, int width, int height)
        {
            using (MemoryStream imageStream = new MemoryStream(imageData))
                return DecompressDxt1(imageStream, width, height);
        }

        internal static byte[] DecompressDxt1(Stream imageStream, int width, int height)
        {
            byte[] imageData = new byte[width * height * 4];

            using (BinaryReader imageReader = new BinaryReader(imageStream))
            {
                int blockCountX = (width + 3) / 4;
                int blockCountY = (height + 3) / 4;

                for (int y = 0; y < blockCountY; y++)
                {
                    for (int x = 0; x < blockCountX; x++)
                    {
                        DecompressDxt1Block(imageReader, x, y, blockCountX, width, height, imageData);
                    }
                }
            }

            return imageData;
        }

        private static void DecompressDxt1Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
        {
            ushort c0 = imageReader.ReadUInt16();
            ushort c1 = imageReader.ReadUInt16();

            byte r0, g0, b0;
            byte r1, g1, b1;
            ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
            ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);

            uint lookupTable = imageReader.ReadUInt32();

            for (int blockY = 0; blockY < 4; blockY++)
            {
                for (int blockX = 0; blockX < 4; blockX++)
                {
                    byte r = 0, g = 0, b = 0, a = 255;
                    uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;

                    if (c0 > c1)
                    {
                        switch (index)
                        {
                            case 0:
                                r = r0;
                                g = g0;
                                b = b0;
                                break;
                            case 1:
                                r = r1;
                                g = g1;
                                b = b1;
                                break;
                            case 2:
                                r = (byte)((2 * r0 + r1) / 3);
                                g = (byte)((2 * g0 + g1) / 3);
                                b = (byte)((2 * b0 + b1) / 3);
                                break;
                            case 3:
                                r = (byte)((r0 + 2 * r1) / 3);
                                g = (byte)((g0 + 2 * g1) / 3);
                                b = (byte)((b0 + 2 * b1) / 3);
                                break;
                        }
                    }
                    else
                    {
                        switch (index)
                        {
                            case 0:
                                r = r0;
                                g = g0;
                                b = b0;
                                break;
                            case 1:
                                r = r1;
                                g = g1;
                                b = b1;
                                break;
                            case 2:
                                r = (byte)((r0 + r1) / 2);
                                g = (byte)((g0 + g1) / 2);
                                b = (byte)((b0 + b1) / 2);
                                break;
                            case 3:
                                r = 0;
                                g = 0;
                                b = 0;
                                a = 0;
                                break;
                        }
                    }

                    int px = (x << 2) + blockX;
                    int py = (y << 2) + blockY;
                    if ((px < width) && (py < height))
                    {
                        int offset = ((py * width) + px) << 2;
                        imageData[offset] = r;
                        imageData[offset + 1] = g;
                        imageData[offset + 2] = b;
                        imageData[offset + 3] = a;
                    }
                }
            }
        }


        internal static byte[] DecompressDxt3(byte[] imageData, int width, int height)
        {
            using (MemoryStream imageStream = new MemoryStream(imageData))
                return DecompressDxt3(imageStream, width, height);
        }

        internal static byte[] DecompressDxt3(Stream imageStream, int width, int height)
        {
            byte[] imageData = new byte[width * height * 4];

            using (BinaryReader imageReader = new BinaryReader(imageStream))
            {
                int blockCountX = (width + 3) / 4;
                int blockCountY = (height + 3) / 4;

                for (int y = 0; y < blockCountY; y++)
                {
                    for (int x = 0; x < blockCountX; x++)
                    {
                        DecompressDxt3Block(imageReader, x, y, blockCountX, width, height, imageData);
                    }
                }
            }

            return imageData;
        }

        private static void DecompressDxt3Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
        {
            byte a0 = imageReader.ReadByte();
            byte a1 = imageReader.ReadByte();
            byte a2 = imageReader.ReadByte();
            byte a3 = imageReader.ReadByte();
            byte a4 = imageReader.ReadByte();
            byte a5 = imageReader.ReadByte();
            byte a6 = imageReader.ReadByte();
            byte a7 = imageReader.ReadByte();

            ushort c0 = imageReader.ReadUInt16();
            ushort c1 = imageReader.ReadUInt16();

            byte r0, g0, b0;
            byte r1, g1, b1;
            ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
            ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);

            uint lookupTable = imageReader.ReadUInt32();

            int alphaIndex = 0;
            for (int blockY = 0; blockY < 4; blockY++)
            {
                for (int blockX = 0; blockX < 4; blockX++)
                {
                    byte r = 0, g = 0, b = 0, a = 0;

                    uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;

                    switch (alphaIndex)
                    {
                        case 0:
                            a = (byte)((a0 & 0x0F) | ((a0 & 0x0F) << 4));
                            break;
                        case 1:
                            a = (byte)((a0 & 0xF0) | ((a0 & 0xF0) >> 4));
                            break;
                        case 2:
                            a = (byte)((a1 & 0x0F) | ((a1 & 0x0F) << 4));
                            break;
                        case 3:
                            a = (byte)((a1 & 0xF0) | ((a1 & 0xF0) >> 4));
                            break;
                        case 4:
                            a = (byte)((a2 & 0x0F) | ((a2 & 0x0F) << 4));
                            break;
                        case 5:
                            a = (byte)((a2 & 0xF0) | ((a2 & 0xF0) >> 4));
                            break;
                        case 6:
                            a = (byte)((a3 & 0x0F) | ((a3 & 0x0F) << 4));
                            break;
                        case 7:
                            a = (byte)((a3 & 0xF0) | ((a3 & 0xF0) >> 4));
                            break;
                        case 8:
                            a = (byte)((a4 & 0x0F) | ((a4 & 0x0F) << 4));
                            break;
                        case 9:
                            a = (byte)((a4 & 0xF0) | ((a4 & 0xF0) >> 4));
                            break;
                        case 10:
                            a = (byte)((a5 & 0x0F) | ((a5 & 0x0F) << 4));
                            break;
                        case 11:
                            a = (byte)((a5 & 0xF0) | ((a5 & 0xF0) >> 4));
                            break;
                        case 12:
                            a = (byte)((a6 & 0x0F) | ((a6 & 0x0F) << 4));
                            break;
                        case 13:
                            a = (byte)((a6 & 0xF0) | ((a6 & 0xF0) >> 4));
                            break;
                        case 14:
                            a = (byte)((a7 & 0x0F) | ((a7 & 0x0F) << 4));
                            break;
                        case 15:
                            a = (byte)((a7 & 0xF0) | ((a7 & 0xF0) >> 4));
                            break;
                    }
                    ++alphaIndex;

                    switch (index)
                    {
                        case 0:
                            r = r0;
                            g = g0;
                            b = b0;
                            break;
                        case 1:
                            r = r1;
                            g = g1;
                            b = b1;
                            break;
                        case 2:
                            r = (byte)((2 * r0 + r1) / 3);
                            g = (byte)((2 * g0 + g1) / 3);
                            b = (byte)((2 * b0 + b1) / 3);
                            break;
                        case 3:
                            r = (byte)((r0 + 2 * r1) / 3);
                            g = (byte)((g0 + 2 * g1) / 3);
                            b = (byte)((b0 + 2 * b1) / 3);
                            break;
                    }

                    int px = (x << 2) + blockX;
                    int py = (y << 2) + blockY;
                    if ((px < width) && (py < height))
                    {
                        int offset = ((py * width) + px) << 2;
                        imageData[offset] = r;
                        imageData[offset + 1] = g;
                        imageData[offset + 2] = b;
                        imageData[offset + 3] = a;
                    }
                }
            }
        }


        internal static byte[] DecompressDxt5(byte[] imageData, int width, int height)
        {
            using (MemoryStream imageStream = new MemoryStream(imageData))
                return DecompressDxt5(imageStream, width, height);
        }

        internal static byte[] DecompressDxt5(Stream imageStream, int width, int height)
        {
            byte[] imageData = new byte[width * height * 4];

            using (BinaryReader imageReader = new BinaryReader(imageStream))
            {
                int blockCountX = (width + 3) / 4;
                int blockCountY = (height + 3) / 4;

                for (int y = 0; y < blockCountY; y++)
                {
                    for (int x = 0; x < blockCountX; x++)
                    {
                        DecompressDxt5Block(imageReader, x, y, blockCountX, width, height, imageData);
                    }
                }
            }

            return imageData;
        }

        private static void DecompressDxt5Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
        {
            byte alpha0 = imageReader.ReadByte();
            byte alpha1 = imageReader.ReadByte();

            ulong alphaMask = (ulong)imageReader.ReadByte();
            alphaMask += (ulong)imageReader.ReadByte() << 8;
            alphaMask += (ulong)imageReader.ReadByte() << 16;
            alphaMask += (ulong)imageReader.ReadByte() << 24;
            alphaMask += (ulong)imageReader.ReadByte() << 32;
            alphaMask += (ulong)imageReader.ReadByte() << 40;

            ushort c0 = imageReader.ReadUInt16();
            ushort c1 = imageReader.ReadUInt16();

            byte r0, g0, b0;
            byte r1, g1, b1;
            ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
            ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);

            uint lookupTable = imageReader.ReadUInt32();

            for (int blockY = 0; blockY < 4; blockY++)
            {
                for (int blockX = 0; blockX < 4; blockX++)
                {
                    byte r = 0, g = 0, b = 0, a = 255;
                    uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;

                    uint alphaIndex = (uint)((alphaMask >> 3 * (4 * blockY + blockX)) & 0x07);
                    if (alphaIndex == 0)
                    {
                        a = alpha0;
                    }
                    else if (alphaIndex == 1)
                    {
                        a = alpha1;
                    }
                    else if (alpha0 > alpha1)
                    {
                        a = (byte)(((8 - alphaIndex) * alpha0 + (alphaIndex - 1) * alpha1) / 7);
                    }
                    else if (alphaIndex == 6)
                    {
                        a = 0;
                    }
                    else if (alphaIndex == 7)
                    {
                        a = 0xff;
                    }
                    else
                    {
                        a = (byte)(((6 - alphaIndex) * alpha0 + (alphaIndex - 1) * alpha1) / 5);
                    }

                    switch (index)
                    {
                        case 0:
                            r = r0;
                            g = g0;
                            b = b0;
                            break;
                        case 1:
                            r = r1;
                            g = g1;
                            b = b1;
                            break;
                        case 2:
                            r = (byte)((2 * r0 + r1) / 3);
                            g = (byte)((2 * g0 + g1) / 3);
                            b = (byte)((2 * b0 + b1) / 3);
                            break;
                        case 3:
                            r = (byte)((r0 + 2 * r1) / 3);
                            g = (byte)((g0 + 2 * g1) / 3);
                            b = (byte)((b0 + 2 * b1) / 3);
                            break;
                    }

                    int px = (x << 2) + blockX;
                    int py = (y << 2) + blockY;
                    if ((px < width) && (py < height))
                    {
                        int offset = ((py * width) + px) << 2;
                        imageData[offset] = r;
                        imageData[offset + 1] = g;
                        imageData[offset + 2] = b;
                        imageData[offset + 3] = a;
                    }
                }
            }
        }

        private static void ConvertRgb565ToRgb888(ushort color, out byte r, out byte g, out byte b)
        {
            int temp;
            temp = (color >> 11) * 255 + 16;
            r = (byte)((temp / 32 + temp) / 32);
            temp = ((color & 0x07E0) >> 5) * 255 + 32;
            g = (byte)((temp / 64 + temp) / 64);
            temp = (color & 0x001F) * 255 + 16;
            b = (byte)((temp / 32 + temp) / 32);
        }

        private static void ConvertBgra5551ToRgba8(ushort color, out byte r, out byte g, out byte b, out byte a)
        {
            //added by dexy
            int temp;
            temp = ((color >> 11) & 0x1F) * 255 + 16;
            r = (byte)((temp / 32 + temp) / 32);
            temp = ((color >> 6) & 0x1F) * 255 + 32;
            g = (byte)((temp / 32 + temp) / 32);
            temp = ((color >> 1) & 0x1F) * 255 + 16;
            b = (byte)((temp / 32 + temp) / 32);
            a = (byte)((color & 1) * 255);
        }




        //these ones added by dexy - based on DecompressDxt

        internal static byte[] DecompressBC4(byte[] imageData, int width, int height)
        {
            using (MemoryStream imageStream = new MemoryStream(imageData))
                return DecompressBC4(imageStream, width, height);
        }

        internal static byte[] DecompressBC4(Stream imageStream, int width, int height)
        {
            byte[] imageData = new byte[width * height * 4];

            using (BinaryReader imageReader = new BinaryReader(imageStream))
            {
                int blockCountX = (width + 3) / 4;
                int blockCountY = (height + 3) / 4;

                for (int y = 0; y < blockCountY; y++)
                {
                    for (int x = 0; x < blockCountX; x++)
                    {
                        DecompressBC4Block(imageReader, x, y, blockCountX, width, height, imageData);
                    }
                }
            }

            return imageData;
        }

        private static void DecompressBC4Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
        {
            byte r0 = imageReader.ReadByte();
            byte r1 = imageReader.ReadByte();

            ulong rMask = (ulong)imageReader.ReadByte();
            rMask += (ulong)imageReader.ReadByte() << 8;
            rMask += (ulong)imageReader.ReadByte() << 16;
            rMask += (ulong)imageReader.ReadByte() << 24;
            rMask += (ulong)imageReader.ReadByte() << 32;
            rMask += (ulong)imageReader.ReadByte() << 40;

            for (int blockY = 0; blockY < 4; blockY++)
            {
                for (int blockX = 0; blockX < 4; blockX++)
                {
                    byte r = 255;

                    uint rIndex = (uint)((rMask >> 3 * (4 * blockY + blockX)) & 0x07);
                    if (rIndex == 0)
                    {
                        r = r0;
                    }
                    else if (rIndex == 1)
                    {
                        r = r1;
                    }
                    else if (r0 > r1)
                    {
                        r = (byte)(((8 - rIndex) * r0 + (rIndex - 1) * r1) / 7);
                    }
                    else if (rIndex == 6)
                    {
                        r = 0;
                    }
                    else if (rIndex == 7)
                    {
                        r = 0xff;
                    }
                    else
                    {
                        r = (byte)(((6 - rIndex) * r0 + (rIndex - 1) * r1) / 5);
                    }

                    int px = (x << 2) + blockX;
                    int py = (y << 2) + blockY;
                    if ((px < width) && (py < height))
                    {
                        int offset = ((py * width) + px) << 2;
                        imageData[offset] = r;
                        imageData[offset + 1] = 0;
                        imageData[offset + 2] = 0;
                        imageData[offset + 3] = 255;
                    }
                }
            }
        }


        internal static byte[] DecompressBC5(byte[] imageData, int width, int height)
        {
            using (MemoryStream imageStream = new MemoryStream(imageData))
                return DecompressBC5(imageStream, width, height);
        }

        internal static byte[] DecompressBC5(Stream imageStream, int width, int height)
        {
            byte[] imageData = new byte[width * height * 4];

            using (BinaryReader imageReader = new BinaryReader(imageStream))
            {
                int blockCountX = (width + 3) / 4;
                int blockCountY = (height + 3) / 4;

                for (int y = 0; y < blockCountY; y++)
                {
                    for (int x = 0; x < blockCountX; x++)
                    {
                        DecompressBC5Block(imageReader, x, y, blockCountX, width, height, imageData);
                    }
                }
            }

            return imageData;
        }

        private static void DecompressBC5Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
        {
            byte r0 = imageReader.ReadByte();
            byte r1 = imageReader.ReadByte();

            ulong rMask = (ulong)imageReader.ReadByte();
            rMask += (ulong)imageReader.ReadByte() << 8;
            rMask += (ulong)imageReader.ReadByte() << 16;
            rMask += (ulong)imageReader.ReadByte() << 24;
            rMask += (ulong)imageReader.ReadByte() << 32;
            rMask += (ulong)imageReader.ReadByte() << 40;

            byte g0 = imageReader.ReadByte();
            byte g1 = imageReader.ReadByte();

            ulong gMask = (ulong)imageReader.ReadByte();
            gMask += (ulong)imageReader.ReadByte() << 8;
            gMask += (ulong)imageReader.ReadByte() << 16;
            gMask += (ulong)imageReader.ReadByte() << 24;
            gMask += (ulong)imageReader.ReadByte() << 32;
            gMask += (ulong)imageReader.ReadByte() << 40;

            for (int blockY = 0; blockY < 4; blockY++)
            {
                for (int blockX = 0; blockX < 4; blockX++)
                {
                    byte r = 255;
                    uint rIndex = (uint)((rMask >> 3 * (4 * blockY + blockX)) & 0x07);
                    if (rIndex == 0)
                    {
                        r = r0;
                    }
                    else if (rIndex == 1)
                    {
                        r = r1;
                    }
                    else if (r0 > r1)
                    {
                        r = (byte)(((8 - rIndex) * r0 + (rIndex - 1) * r1) / 7);
                    }
                    else if (rIndex == 6)
                    {
                        r = 0;
                    }
                    else if (rIndex == 7)
                    {
                        r = 0xff;
                    }
                    else
                    {
                        r = (byte)(((6 - rIndex) * r0 + (rIndex - 1) * r1) / 5);
                    }


                    byte g = 255;
                    uint gIndex = (uint)((gMask >> 3 * (4 * blockY + blockX)) & 0x07);
                    if (gIndex == 0)
                    {
                        r = r0;
                    }
                    else if (gIndex == 1)
                    {
                        r = r1;
                    }
                    else if (r0 > r1)
                    {
                        r = (byte)(((8 - gIndex) * r0 + (gIndex - 1) * r1) / 7);
                    }
                    else if (gIndex == 6)
                    {
                        r = 0;
                    }
                    else if (gIndex == 7)
                    {
                        r = 0xff;
                    }
                    else
                    {
                        r = (byte)(((6 - gIndex) * r0 + (gIndex - 1) * r1) / 5);
                    }


                    int px = (x << 2) + blockX;
                    int py = (y << 2) + blockY;
                    if ((px < width) && (py < height))
                    {
                        int offset = ((py * width) + px) << 2;
                        imageData[offset] = r;
                        imageData[offset + 1] = g;
                        imageData[offset + 2] = 0;
                        imageData[offset + 3] = 255;
                    }
                }
            }
        }



        private static byte[] ConvertA8ToRGBA8(byte[] imgdata, int w, int h)
        {
            var px = new byte[w * h * 4];
            var pxmax = px.Length - 3;
            for (int i = 0; i < imgdata.Length; i++)
            {
                byte av = imgdata[i];
                int pxi = i * 4;
                if (pxi < pxmax)
                {
                    px[pxi] = av;
                    px[pxi + 1] = av;
                    px[pxi + 2] = av;
                    px[pxi + 3] = 255;
                }
            }
            return px;
        }
        private static byte[] ConvertR8ToRGBA8(byte[] imgdata, int w, int h)
        {
            var px = new byte[w * h * 4];
            var pxmax = px.Length - 3;
            for (int i = 0; i < imgdata.Length; i++)
            {
                byte av = imgdata[i];
                int pxi = i * 4;
                if (pxi < pxmax)
                {
                    px[pxi] = av;
                    px[pxi + 1] = 0;
                    px[pxi + 2] = 0;
                    px[pxi + 3] = 255;
                }
            }
            return px;
        }
        private static byte[] ConvertBGR5A1ToRGBA8(byte[] imgdata, int w, int h)
        {
            var px = new byte[w * h * 4];
            var pxmax = px.Length - 3;
            for (int i = 0; i < imgdata.Length; i+=2)
            {
                byte v0 = imgdata[i];
                byte v1 = imgdata[i + 1];
                ushort vu = (ushort)(v1 * 255 + v0);
                int pxi = i * 4;
                if (pxi < pxmax)
                {
                    ConvertBgra5551ToRgba8(vu, out px[pxi], out px[pxi + 1], out px[pxi + 2], out px[pxi + 3]);
                }
            }
            return px;
        }

    }
}
