/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//  hsDXTDirectXCodec Class Functions                                        //
//  DirectX-based codec functions                                            //
//  Cyan, Inc.                                                               //
//                                                                           //
//// Version History //////////////////////////////////////////////////////////
//                                                                           //
//  6.8.2001 mcn - Got a much-needed Plasma 2.0/DX8 update.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "HeadSpin.h"

#if HS_BUILD_FOR_WIN32
#include <ddraw.h>
#include <d3d9.h>
#endif

#include "hsDXTDirectXCodec.h"
#include "plMipmap.h"
#include "hsCodecManager.h"
#include "plPipeline/hsGDDrawDllLoad.h"

#if HS_BUILD_FOR_WIN32
namespace {
    typedef HRESULT(WINAPI * DIRECTDRAWCREATEEX)( GUID*, VOID**, REFIID, IUnknown* );
}

enum
{
    D3DTEXTURE_FMT_ARGB32_8888                  = 0x00000002,       // No. 2: 8888 ARGB (32-bit) format.
    D3DTEXTURE_FMT_FOURCC_DXT1                  = 0x00002000,       // No.14: DXTn FourCC (DXT1), format.
    D3DTEXTURE_FMT_FOURCC_DXT5                  = 0x00020000,       // No.18: DXTn FourCC (DXT5), format.
};
#endif

hsBool hsDXTDirectXCodec::fRegistered = false;

//// Small Init Functions /////////////////////////////////////////////////////

hsDXTDirectXCodec& hsDXTDirectXCodec::Instance()
{
    static hsDXTDirectXCodec the_instance;

    return the_instance;
}

hsDXTDirectXCodec::hsDXTDirectXCodec() : fDirectDraw( nil ), fDDLibraryInstance( nil ), fFlags( 0 )
{
}

hsDXTDirectXCodec::~hsDXTDirectXCodec()
{
#if HS_BUILD_FOR_WIN32
    if ((fFlags & kExternalInit) == 0)
    {
        if (fDirectDraw)
        {
            fDirectDraw->Release();
        }
        
        if (fDDLibraryInstance)
        {
            FreeLibrary(fDDLibraryInstance);
        }
    }

    fDirectDraw = nil;
    fDDLibraryInstance = nil;
#endif
}

hsBool hsDXTDirectXCodec::Register()
{
    return hsCodecManager::Instance().Register( &(Instance()), plMipmap::kDirectXCompression, 500 );
}

#if HS_BUILD_FOR_WIN32
//// Initialize ///////////////////////////////////////////////////////////////

void hsDXTDirectXCodec::Initialize( IDirect3DDevice8 *directDraw )
{
/*  if (directDraw)
    {
        fFlags |= (kInitialized | kExternalInit);
    }
    else
    {
        fFlags &= ~(kInitialized | kExternalInit);
    }
    fDirectDraw = directDraw;
*/
    // Don't do anything--force IInitialize to run
}

//// IInitialize //////////////////////////////////////////////////////////////
//  Gotta initialize D3D ourself and create a device and everything. This is
//  a LOT messier than it was in DX7, since this time we got D3D to deal with

hsBool  hsDXTDirectXCodec::IInitialize()
{
    fFlags |= kInitialized;

//  if( hsGDDrawDllLoad::GetDDrawDll() == nil )
//      return false;   

    DIRECTDRAWCREATEEX DirectDrawCreateEx = 0;

    // Initialize DirectDraw
    HRESULT hr;
    DirectDrawCreateEx = (DIRECTDRAWCREATEEX)GetProcAddress( hsGDDrawDllLoad::GetD3DDll(), "DirectDrawCreateEx" );
    if( DirectDrawCreateEx == nil )
        return false;

    /// Using EMULATIONONLY here usually fails--using NULL forces the
    /// use of the standard display driver, which DOES work.
    if (FAILED(hr = DirectDrawCreateEx((GUID FAR *)NULL/*DDCREATE_EMULATIONONLY*/, (VOID**)&fDirectDraw, IID_IDirectDraw7, NULL)))
        return false;

    if (FAILED(hr = fDirectDraw->SetCooperativeLevel(NULL, DDSCL_NORMAL)))
        return false;

    return true;
}
#endif

//// CreateCompressedMipmap ///////////////////////////////////////////////////
//  Updated 8.15.2000 mcn to generate uncompressed mipmaps down to 1x1 (the
//  decompressor better know how to deal with this!) Also cleaned up so I can
//  read it :)

plMipmap *hsDXTDirectXCodec::CreateCompressedMipmap( plMipmap *uncompressed )
{
#if HS_BUILD_FOR_WIN32
    const plMipmap  *b = uncompressed;
    plMipmap        *compressed = nil;

    uint32_t  numLevels = 1, numCompLevels;
    uint32_t  compFormat, totalSize, compSize;
    int32_t   width, height, blockSize, i;


    /// Sanity checks, initialization, etc.
    if( !Initialized() )
    {
        if( !IInitialize() )
            return nil;
    }

    hsAssert( fRegistered, "Calling member of unregistered codec." );
    hsAssert( !uncompressed->IsCompressed(), "Trying to re-compress compressed bitmap." );

    if( !fDirectDraw )
        return nil;

    /// Check width and height
    if( ( uncompressed->GetWidth() | uncompressed->GetHeight() ) & 0x03 )
        return nil;     /// Width and height must be multiple of 4

    /// This used to be checked later--but WHY? We can check it now and
    /// potentially avoid a lot of headache
    compFormat = ICompressedFormat( b );
    if( !compFormat )
        return nil;

    /// Precalc this
    blockSize = ( compFormat == D3DTEXTURE_FMT_FOURCC_DXT1 ) ? 8 : 16;


    {
        compSize = 0;
        numCompLevels = 0;

        width = uncompressed->GetWidth();
        height = uncompressed->GetHeight();

        /// Count the levels we're going to compress
        for( i = 0; i < uncompressed->GetNumLevels(); i++ )
        {
            if( ( width | height ) & 0x03 )
                break;

            numCompLevels++;
            compSize += blockSize * width * height >> 4;
            width >>= 1;
            height >>= 1;
        }

        /// NEW 8.15.2000 mcn - Now count up remaining levels down to 1x1
        totalSize = compSize;
        numLevels = numCompLevels;
        for( ; i < uncompressed->GetNumLevels(); i++ )
        {
            totalSize += uncompressed->GetLevelSize( (uint8_t)i );

            width >>= 1;
            height >>= 1;
            numLevels++;
        }
    }


    /// Create source DirectDraw surface
    IDirectDrawSurface7 *srcSurface = IMakeDirect3DSurface( D3DTEXTURE_FMT_ARGB32_8888, 
                                                            numCompLevels, uncompressed->GetWidth(), 
                                                            uncompressed->GetHeight() );

    IFillSurface( (hsRGBAColor32 *)uncompressed->GetImage(), numCompLevels, srcSurface );

    /// Create destination DirectDraw surface
    IDirectDrawSurface7 *destSurface = IMakeDirect3DSurface( compFormat, numCompLevels, 
                                                            uncompressed->GetWidth(), 
                                                            uncompressed->GetHeight() );
    ICopySurface( destSurface, srcSurface, numCompLevels );


    /// Now set up the data structures
    compressed = new plMipmap( uncompressed->GetWidth(), uncompressed->GetHeight(), plMipmap::kARGB32Config,
                                uncompressed->GetNumLevels(), plMipmap::kDirectXCompression,
                                ( compFormat == D3DTEXTURE_FMT_FOURCC_DXT1 ) ? 
                                        plMipmap::DirectXInfo::kDXT1 : plMipmap::DirectXInfo::kDXT5 );
    
    /// Copy compressed data back from the surface
    IFillFromSurface( (hsRGBAColor32 *)compressed->GetImage(), numCompLevels, destSurface );

    /// Finally, copy back any remaining data
    if( numCompLevels < numLevels )
    {
        /// Now copy the rest straight over
        for( i = numCompLevels; i < numLevels; i++ )
        {
            memcpy( compressed->GetLevelPtr( (uint8_t)i ), uncompressed->GetLevelPtr( (uint8_t)i ), 
                    uncompressed->GetLevelSize( (uint8_t)i ) );
        }
    }

    /// All done!
    return compressed;
#else
    return nil;
#endif
}

//// CreateUncompressedMipmap /////////////////////////////////////////////////
//  Updated 8.15.2000 mcn to support mipmaps down to 1x1. See
//  CreateCompressedMipmap(). Also cleaned up the code a bit (too tired to 
//  clean it ALL up)

plMipmap *hsDXTDirectXCodec::CreateUncompressedMipmap( plMipmap *compressed, 
                                                            uint8_t bitDepth )
{
    /// Use software decompression ALWAYS
    return nil;
/*
    plMipmap    *uncompressed = nil;

    uint32_t  mmlvs, formatType, numCompLevels;
    int32_t   totalSize, width, height, i;


    /// Check bit depth--if it's 16 bit, we don't support it (for now)
    if( ( bitDepth & hsCodecManager::kBitDepthMask ) != hsCodecManager::k16BitDepth )
        return nil;

    if( !Initialized() )
    {
        if( !IInitialize() )
            return nil;
    }

    /// Sanity checks
    hsAssert( fRegistered, "Calling member of unregistered codec." );
    hsAssert( compressed->fFlags & hsGMipmap::kCompressed, "Trying to uncompress already uncompressed bitmap." );
    hsAssert( compressed->fCompressionFormat == hsGMipmap::kDirectXCompression, "Uncompressing wrong format." );
    hsAssert( ( compressed->fDirectXInfo.fCompressionType == hsGMipmap::DirectXInfo::kDXT1 ) ||
        ( compressed->fDirectXInfo.fCompressionType == hsGMipmap::DirectXInfo::kDXT5 ),
        "Unsupported directX compression format." );

    if( !fDirectDraw )
        return nil;

    if( compressed->fDirectXInfo.fCompressionType == hsGMipmap::DirectXInfo::kDXT5 )
    {
        // Fall out since directX software decompressor doesn't work...
        return nil;
    }

    if( compressed->fFlags & hsGMipmap::kMipMap )
    {
        mmCompressed->SetLevel( 0 );
        mmlvs = mmCompressed->GetNumLevels();

        for( i = 0, numCompLevels = 0; i < mmlvs; i++ )
        {
            mmCompressed->SetLevel( i );
            if( ( mmCompressed->fWidth | mmCompressed->fHeight ) & 0x03 )
                break;
            numCompLevels++;
        }
        mmCompressed->SetLevel( 0 );
    }
    else
    {
        mmlvs = numCompLevels = 1;
    }

    /// Get format type
    formatType = ( compressed->fDirectXInfo.fCompressionType == hsGMipmap::DirectXInfo::kDXT1 ) ? 
                            D3DTEXTURE_FMT_FOURCC_DXT1 : D3DTEXTURE_FMT_FOURCC_DXT5;

    /// Make the surfaces (decompress in the process)
    IDirectDrawSurface7 *srcSurface = IMakeDirect3DSurface( formatType, numCompLevels, 
                                                            compressed->fWidth, compressed->fHeight );
    IFillSurface( (hsRGBAColor32 *)compressed->fImage, numCompLevels, srcSurface );


    IDirectDrawSurface7 *destSurface = IMakeDirect3DSurface( D3DTEXTURE_FMT_ARGB32_8888, numCompLevels, 
                                                        compressed->fWidth, compressed->fHeight );
    ICopySurface( destSurface, srcSurface, numCompLevels );


    /// Set up the uncompressed data structure
    if( compressed->fFlags & hsGMipmap::kMipMap )
    {
        mmUncompressed = new hsGMipmapClass;
        uncompressed = mmUncompressed;
    }
    else
    {
        uncompressed = new plMipmap;
        mmUncompressed = nil;
    }

    uncompressed->fWidth = compressed->fWidth;
    uncompressed->fHeight = compressed->fHeight;
    uncompressed->fPixelSize = 32;
    uncompressed->fRowBytes = uncompressed->fWidth * uncompressed->fPixelSize >> 3;
    uncompressed->fFlags = compressed->fFlags & ~hsGMipmap::kCompressed;
    uncompressed->fCompressionFormat = 0;
    uncompressed->fDirectXInfo.fBlockSize = 0;
    uncompressed->fDirectXInfo.fCompressionType = hsGMipmap::DirectXInfo::kError;

    /// Handle mipmaps or single image?
    if( mmUncompressed )
    {
        totalSize = 0;
        width = compressed->fWidth;
        height = compressed->fHeight;
        for( i = 0; i < mmlvs; i++ )
        {
            totalSize += width * height * uncompressed->fPixelSize >> 3;
            width >>= 1;
            height >>= 1;
        }
        mmUncompressed->fImage = HSMemory::New( totalSize );
        mmUncompressed->SetData( mmUncompressed->fImage );
        mmUncompressed->SetNumLevels( mmlvs );
    }
    else
    {
        uncompressed->fImage = HSMemory::New( uncompressed->fWidth * uncompressed->fHeight * 
                                                uncompressed->fPixelSize >> 3 );
    }

    /// Copy over compressed levels
    IFillFromSurface( (hsRGBAColor32 *)uncompressed->fImage, numCompLevels, destSurface );

    /// Now take care of the remainder levels
    if( mmUncompressed )
    {
        for( i = numCompLevels; i < mmlvs; i++ )
        {
            mmUncompressed->SetLevel( i );
            mmCompressed->SetLevel( i );

            memcpy( mmUncompressed->fImage, mmCompressed->fImage, mmCompressed->ImageSize() );
        }
        mmUncompressed->SetLevel( 0 );
        mmCompressed->SetLevel( 0 );
    }

    /// All done!
    return uncompressed;
*/
}

#if HS_BUILD_FOR_WIN32
uint32_t hsDXTDirectXCodec::ICompressedFormat(const plMipmap *uncompressed)
{
    if( uncompressed->GetFlags() & plMipmap::kAlphaChannelFlag )
        return D3DTEXTURE_FMT_FOURCC_DXT5;

    return D3DTEXTURE_FMT_FOURCC_DXT1;
}

//// IFindTextureFormat ///////////////////////////////////////////////////////
//  Changed to a local function 6.8.2001 to avoid certain annoying problems
//  with headers and the compiler.

DDPIXELFORMAT   IFindTextureFormat(uint32_t formatType)
{
    DDPIXELFORMAT ddPixelFormat;
    memset( &ddPixelFormat, 0x00, sizeof(DDPIXELFORMAT) );
    ddPixelFormat.dwSize = sizeof(DDPIXELFORMAT);

    switch (formatType)
    {
    case D3DTEXTURE_FMT_ARGB32_8888:
        ddPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS;
        ddPixelFormat.dwFourCC = 0;
        ddPixelFormat.dwRGBBitCount = 32;
        ddPixelFormat.dwRBitMask = 0x00ff0000;
        ddPixelFormat.dwGBitMask = 0x0000ff00;
        ddPixelFormat.dwBBitMask = 0x000000ff;
        ddPixelFormat.dwRGBAlphaBitMask = 0xff000000;
        return ddPixelFormat;
    case D3DTEXTURE_FMT_FOURCC_DXT1:
        ddPixelFormat.dwFlags = DDPF_FOURCC;
        ddPixelFormat.dwFourCC = FOURCC_DXT1;
        return ddPixelFormat;
    case D3DTEXTURE_FMT_FOURCC_DXT5:
        ddPixelFormat.dwFlags = DDPF_FOURCC;
        ddPixelFormat.dwFourCC = FOURCC_DXT5;
        return ddPixelFormat;
    default:
        hsAssert(false, "Unknown texture format selected");
        return ddPixelFormat;
    }
}

IDirectDrawSurface7 *hsDXTDirectXCodec::IMakeDirect3DSurface(uint32_t formatType, uint32_t mipMapLevels, 
                                                              uint32_t width, uint32_t height)
{
    DDSURFACEDESC2 ddsd2;
    memset( &ddsd2, 0x00, sizeof(DDSURFACEDESC2) );
    ddsd2.dwSize          = sizeof(DDSURFACEDESC2);
    ddsd2.dwFlags         = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT;
    ddsd2.ddsCaps.dwCaps  = DDSCAPS_TEXTURE;
    if(mipMapLevels > 1 )
    {
        ddsd2.dwFlags |= DDSD_MIPMAPCOUNT;
        ddsd2.ddsCaps.dwCaps |= DDSCAPS_MIPMAP|DDSCAPS_COMPLEX;
    }
    ddsd2.dwMipMapCount   = mipMapLevels;
    ddsd2.dwWidth         = width;
    ddsd2.dwHeight        = height;
    ddsd2.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;

    ddsd2.ddpfPixelFormat = IFindTextureFormat(formatType);

    IDirectDrawSurface7 *lpDDsTex;
    HRESULT res = fDirectDraw->CreateSurface( &ddsd2, &lpDDsTex, NULL );
    if( S_OK != res )
        CheckErrorCode(res);
    return lpDDsTex;
}

void hsDXTDirectXCodec::IFillSurface(hsRGBAColor32* src, uint32_t mmlvs, IDirectDrawSurface7 *pddsDest)
{
    uint8_t                   *pTexDat = (uint8_t*)src;
    uint32_t                  cap      = 0;

    HRESULT     hr;
    DDSCAPS2    ddsCaps2;

    for( WORD wNum=0; wNum < mmlvs; wNum++ )
    {
        DDSURFACEDESC2 ddsd2;
        memset( &ddsd2, 0x00, sizeof(DDSURFACEDESC2) );
        ddsd2.dwSize = sizeof( DDSURFACEDESC2 );

        hr  = pddsDest->Lock( NULL, &ddsd2, DDLOCK_WAIT | DDLOCK_WRITEONLY | DDLOCK_DISCARDCONTENTS, NULL );
        if (ddsd2.ddpfPixelFormat.dwFlags == DDPF_FOURCC)
        {
            int32_t blockSize = (ddsd2.ddpfPixelFormat.dwFourCC == FOURCC_DXT1) ? 8 : 16;
            cap = ddsd2.dwHeight * ddsd2.dwWidth * blockSize >> 4;
            memcpy( (char*)ddsd2.lpSurface, pTexDat, cap );
            pTexDat += cap;
        }
        else
        {
            hsAssert(ddsd2.ddpfPixelFormat.dwRGBBitCount == 32, "Format not supported.");

            int32_t* dest = (int32_t*)ddsd2.lpSurface;
            int32_t pixelCount = ddsd2.dwHeight * ddsd2.dwWidth;
            int32_t j;
            for (j = 0; j < pixelCount; ++j)
            {
                dest[j] =  ( ( src->a << 24 ) | ( src->r << 16 ) | ( src->g << 8 ) | src->b );
                src++;
            }
        }
        hr = pddsDest->Unlock( NULL );

        memset( &ddsCaps2, 0x00, sizeof(DDSCAPS2) );
        ddsCaps2.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;
        if( SUCCEEDED( pddsDest->GetAttachedSurface( &ddsCaps2, &pddsDest ) ) )
            pddsDest->Release();
    }
}

void hsDXTDirectXCodec::IFillFromSurface(hsRGBAColor32* dest, uint32_t mmlvs, IDirectDrawSurface7 *pddsSrc)
{
    uint8_t           *pTexDat = (uint8_t *)dest;
    uint32_t          cap      = 0;

    HRESULT         hr;
    DDSCAPS2        ddsCaps2;
    DDSURFACEDESC2  ddsd2;


    for( WORD wNum = 0; wNum < mmlvs; wNum++ )
    {
        memset( &ddsd2, 0, sizeof( DDSURFACEDESC2 ) );
        ddsd2.dwSize = sizeof( DDSURFACEDESC2 );

        hr = pddsSrc->Lock( NULL, &ddsd2, DDLOCK_WAIT | DDLOCK_READONLY | 
                                            DDLOCK_DISCARDCONTENTS, NULL );

        if( ddsd2.ddpfPixelFormat.dwFlags == DDPF_FOURCC )
        {
            int32_t blockSize = ( ddsd2.ddpfPixelFormat.dwFourCC == FOURCC_DXT1 ) ? 8 : 16;

            cap = ddsd2.dwHeight * ddsd2.dwWidth * blockSize >> 4;
            memcpy( pTexDat, (char*)ddsd2.lpSurface, cap );
            pTexDat += cap;
        }
        else
        {
            hsAssert( ddsd2.ddpfPixelFormat.dwRGBBitCount == 32, "Format not supported." );

            int32_t* src = (int32_t*)ddsd2.lpSurface;
            int32_t pixelCount = ddsd2.dwHeight * ddsd2.dwWidth;
            int32_t j;
            for (j = 0; j < pixelCount; ++j)
            {
                dest->a = (uint8_t)((src[j] >> 24) & 0xff);
                dest->r = (uint8_t)((src[j] >> 16) & 0xff);
                dest->g = (uint8_t)((src[j] >> 8) & 0xff);
                dest->b = (uint8_t)((src[j]) & 0xff);
                dest++;
            }
        }
        hr = pddsSrc->Unlock( NULL );

        memset( &ddsCaps2, 0, sizeof( DDSCAPS2 ) );
        ddsCaps2.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;

        if( SUCCEEDED( pddsSrc->GetAttachedSurface( &ddsCaps2, &pddsSrc ) ) )
            pddsSrc->Release();
    }
}

void hsDXTDirectXCodec::ICopySurface(IDirectDrawSurface7 *dest, IDirectDrawSurface7 *src, int32_t mipMapLevels)
{
    DDSURFACEDESC2 ddsd2;
    memset( &ddsd2, 0x00, sizeof(DDSURFACEDESC2) );
    ddsd2.dwSize = sizeof( DDSURFACEDESC2 );

    DDSCAPS2 ddsCaps;
    
    ZeroMemory(&ddsCaps, sizeof(ddsCaps));
    ddsCaps.dwCaps = DDSCAPS_TEXTURE;
    if (mipMapLevels > 1)
        ddsCaps.dwCaps2 = DDSCAPS2_MIPMAPSUBLEVEL;

    IDirectDrawSurface7 *lpSrc = src;
    IDirectDrawSurface7 *lpDst = dest;

    int mmlvs = mipMapLevels;

    HRESULT hr;
    int i;
    for( i = 0; i < mmlvs; i++ )
    {
        if( FAILED(hr = lpDst->Blt(NULL, lpSrc, NULL, DDBLT_WAIT, NULL)) )
        {
            hsAssert(false, "Uh oh!");
        }
        
        if (FAILED(hr = lpSrc->GetAttachedSurface(&ddsCaps, &lpSrc))
          ||FAILED(hr = lpDst->GetAttachedSurface(&ddsCaps, &lpDst)))
        {
            break;
        }
        lpSrc->Release();
        lpDst->Release();
    }
}

void hsDXTDirectXCodec::CheckErrorCode(HRESULT res)
{
    switch( res )
    {
        // This object is already initialized
        case DDERR_ALREADYINITIALIZED:
            hsAssert(false, "DDERR_ALREADYINITIALIZED.");
            break;

        // This surface can not be attached to the requested surface.
        case DDERR_CANNOTATTACHSURFACE:
            hsAssert(false, "DDERR_CANNOTATTACHSURFACE.");
            break;

        // This surface can not be detached from the requested surface.
        case DDERR_CANNOTDETACHSURFACE:
            hsAssert(false, "DDERR_CANNOTDETACHSURFACE.");
            break;

        // Support is currently not available.
        case DDERR_CURRENTLYNOTAVAIL:
            hsAssert(false, "DDERR_CURRENTLYNOTAVAIL.");
            break;

        // An exception was encountered while performing the requested operation
        case DDERR_EXCEPTION:
            hsAssert(false, "DDERR_EXCEPTION.");
            break;

        // Generic failure.
        case DDERR_GENERIC:
            hsAssert(false, "DDERR_GENERIC.");
            break;

        // Height of rectangle provided is not a multiple of reqd alignment
        case DDERR_HEIGHTALIGN:
            hsAssert(false, "DDERR_HEIGHTALIGN.");
            break;

        // Unable to match primary surface creation request with existing
        // primary surface.
        case DDERR_INCOMPATIBLEPRIMARY:
            hsAssert(false, "DDERR_INCOMPATIBLEPRIMARY.");
            break;

        // One or more of the caps bits passed to the callback are incorrect.
        case DDERR_INVALIDCAPS:
            hsAssert(false, "DDERR_INVALIDCAPS.");
            break;

        // DirectDraw does not support provided Cliplist.
        case DDERR_INVALIDCLIPLIST:
            hsAssert(false, "DDERR_INVALIDCLIPLIST.");
            break;

        // DirectDraw does not support the requested mode
        case DDERR_INVALIDMODE:
            hsAssert(false, "DDERR_INVALIDMODE.");
            break;

        // DirectDraw received a pointer that was an invalid DIRECTDRAW object.
        case DDERR_INVALIDOBJECT:
            hsAssert(false, "DDERR_INVALIDOBJECT.");
            break;

        // One or more of the parameters passed to the callback function are
        // incorrect.
        case DDERR_INVALIDPARAMS:
            hsAssert(false, "DDERR_INVALIDPARAMS.");
            break;

        // pixel format was invalid as specified
        case DDERR_INVALIDPIXELFORMAT:
            hsAssert(false, "DDERR_INVALIDPIXELFORMAT.");
            break;

        // Rectangle provided was invalid.
        case DDERR_INVALIDRECT:
            hsAssert(false, "DDERR_INVALIDRECT.");
            break;

        // Operation could not be carried out because one or more surfaces are locked
        case DDERR_LOCKEDSURFACES:
            hsAssert(false, "DDERR_LOCKEDSURFACES.");
            break;

        // There is no 3D present.
        case DDERR_NO3D:
            hsAssert(false, "DDERR_NO3D.");
            break;

        // Operation could not be carried out because there is no alpha accleration
        // hardware present or available.
        case DDERR_NOALPHAHW:
            hsAssert(false, "DDERR_NOALPHAHW.");
            break;

        // no clip list available
        case DDERR_NOCLIPLIST:
            hsAssert(false, "DDERR_NOCLIPLIST.");
            break;

        // Operation could not be carried out because there is no color conversion
        // hardware present or available.
        case DDERR_NOCOLORCONVHW:
            hsAssert(false, "DDERR_NOCOLORCONVHW.");
            break;

        // Create function called without DirectDraw object method SetCooperativeLevel
        // being called.
        case DDERR_NOCOOPERATIVELEVELSET:
            hsAssert(false, "DDERR_NOCOOPERATIVELEVELSET.");
            break;

        // Surface doesn't currently have a color key
        case DDERR_NOCOLORKEY:
            hsAssert(false, "DDERR_NOCOLORKEY.");
            break;

        // Operation could not be carried out because there is no hardware support
        // of the dest color key.
        case DDERR_NOCOLORKEYHW:
            hsAssert(false, "DDERR_NOCOLORKEYHW.");
            break;

        // No DirectDraw support possible with current display driver
        case DDERR_NODIRECTDRAWSUPPORT:
            hsAssert(false, "DDERR_NODIRECTDRAWSUPPORT.");
            break;

        // Operation requires the application to have exclusive mode but the
        // application does not have exclusive mode.
        case DDERR_NOEXCLUSIVEMODE:
            hsAssert(false, "DDERR_NOEXCLUSIVEMODE.");
            break;

        // Flipping visible surfaces is not supported.
        case DDERR_NOFLIPHW:
            hsAssert(false, "DDERR_NOFLIPHW.");
            break;

        // There is no GDI present.
        case DDERR_NOGDI:
            hsAssert(false, "DDERR_NOGDI.");
            break;

        // Operation could not be carried out because there is no hardware present
        // or available.
        case DDERR_NOMIRRORHW:
            hsAssert(false, "DDERR_NOMIRRORHW.");
            break;

        // Requested item was not found
        case DDERR_NOTFOUND:
            hsAssert(false, "DDERR_NOTFOUND.");
            break;

        // Operation could not be carried out because there is no overlay hardware
        // present or available.
        case DDERR_NOOVERLAYHW:
            hsAssert(false, "DDERR_NOOVERLAYHW.");
            break;

        // Operation could not be carried out because the source and destination
        // rectangles are on the same surface and overlap each other.
        case DDERR_OVERLAPPINGRECTS:
            hsAssert(false, "DDERR_OVERLAPPINGRECTS.");
            break;

        // Operation could not be carried out because there is no appropriate raster
        // op hardware present or available.
        case DDERR_NORASTEROPHW:
            hsAssert(false, "DDERR_NORASTEROPHW.");
            break;

        // Operation could not be carried out because there is no rotation hardware
        // present or available.
        case DDERR_NOROTATIONHW:
            hsAssert(false, "DDERR_NOROTATIONHW.");
            break;

        // Operation could not be carried out because there is no hardware support
        // for stretching
        case DDERR_NOSTRETCHHW:
            hsAssert(false, "DDERR_NOSTRETCHHW.");
            break;

        // DirectDrawSurface is not in 4 bit color palette and the requested operation
        // requires 4 bit color palette.
        case DDERR_NOT4BITCOLOR:
            hsAssert(false, "DDERR_NOT4BITCOLOR.");
            break;

        // DirectDrawSurface is not in 4 bit color index palette and the requested
        // operation requires 4 bit color index palette.
        case DDERR_NOT4BITCOLORINDEX:
            hsAssert(false, "DDERR_NOT4BITCOLORINDEX.");
            break;

        // DirectDraw Surface is not in 8 bit color mode and the requested operation
        // requires 8 bit color.
        case DDERR_NOT8BITCOLOR:
            hsAssert(false, "DDERR_NOT8BITCOLOR.");
            break;

        // Operation could not be carried out because there is no texture mapping
        // hardware present or available.
        case DDERR_NOTEXTUREHW:
            hsAssert(false, "DDERR_NOTEXTUREHW.");
            break;

        // Operation could not be carried out because there is no hardware support
        // for vertical blank synchronized operations.
        case DDERR_NOVSYNCHW:
            hsAssert(false, "DDERR_NOVSYNCHW.");
            break;

        // Operation could not be carried out because there is no hardware support
        // for zbuffer blting.
        case DDERR_NOZBUFFERHW:
            hsAssert(false, "DDERR_NOZBUFFERHW.");
            break;

        // Overlay surfaces could not be z layered based on their BltOrder because
        // the hardware does not support z layering of overlays.
        case DDERR_NOZOVERLAYHW:
            hsAssert(false, "DDERR_NOZOVERLAYHW.");
            break;

        // The hardware needed for the requested operation has already been
        // allocated.
        case DDERR_OUTOFCAPS:
            hsAssert(false, "DDERR_OUTOFCAPS.");
            break;

        // DirectDraw does not have enough memory to perform the operation.
        case DDERR_OUTOFMEMORY:
            hsAssert(false, "DDERR_OUTOFMEMORY.");
            break;

        // DirectDraw does not have enough memory to perform the operation.
        case DDERR_OUTOFVIDEOMEMORY:
            hsAssert(false, "DDERR_OUTOFVIDEOMEMORY.");
            break;

        // hardware does not support clipped overlays
        case DDERR_OVERLAYCANTCLIP:
            hsAssert(false, "DDERR_OVERLAYCANTCLIP.");
            break;

        // Can only have ony color key active at one time for overlays
        case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
            hsAssert(false, "DDERR_OVERLAYCOLORKEYONLYONEACTIVE.");
            break;

        // Access to this palette is being refused because the palette is already
        // locked by another thread.
        case DDERR_PALETTEBUSY:
            hsAssert(false, "DDERR_PALETTEBUSY.");
            break;

        // No src color key specified for this operation.
        case DDERR_COLORKEYNOTSET:
            hsAssert(false, "DDERR_COLORKEYNOTSET.");
            break;

        // This surface is already attached to the surface it is being attached to.
        case DDERR_SURFACEALREADYATTACHED:
            hsAssert(false, "DDERR_SURFACEALREADYATTACHED.");
            break;

        // This surface is already a dependency of the surface it is being made a
        // dependency of.
        case DDERR_SURFACEALREADYDEPENDENT:
            hsAssert(false, "DDERR_SURFACEALREADYDEPENDENT.");
            break;

        // Access to this surface is being refused because the surface is already
        // locked by another thread.
        case DDERR_SURFACEBUSY:
            hsAssert(false, "DDERR_SURFACEBUSY.");
            break;

        // Access to this surface is being refused because no driver exists
        // which can supply a pointer to the surface.
        // This is most likely to happen when attempting to lock the primary
        // surface when no DCI provider is present.
        // Will also happen on attempts to lock an optimized surface.
        case DDERR_CANTLOCKSURFACE:
            hsAssert(false, "DDERR_CANTLOCKSURFACE.");
            break;

        // Access to Surface refused because Surface is obscured.
        case DDERR_SURFACEISOBSCURED:
            hsAssert(false, "DDERR_SURFACEISOBSCURED.");
            break;

        // Access to this surface is being refused because the surface is gone.
        // The DIRECTDRAWSURFACE object representing this surface should
        // have Restore called on it.
        case DDERR_SURFACELOST:
            hsAssert(false, "DDERR_SURFACELOST.");
            break;

        // The requested surface is not attached.
        case DDERR_SURFACENOTATTACHED:
            hsAssert(false, "DDERR_SURFACENOTATTACHED.");
            break;

        // Height requested by DirectDraw is too large.
        case DDERR_TOOBIGHEIGHT:
            hsAssert(false, "DDERR_TOOBIGHEIGHT.");
            break;

        // Size requested by DirectDraw is too large --  The individual height and
        // width are OK.
        case DDERR_TOOBIGSIZE:
            hsAssert(false, "DDERR_TOOBIGSIZE.");
            break;

        // Width requested by DirectDraw is too large.
        case DDERR_TOOBIGWIDTH:
            hsAssert(false, "DDERR_TOOBIGWIDTH.");
            break;

        // Action not supported.
        case DDERR_UNSUPPORTED:
            hsAssert(false, "DDERR_UNSUPPORTED.");
            break;

        // FOURCC format requested is unsupported by DirectDraw
        case DDERR_UNSUPPORTEDFORMAT:
            hsAssert(false, "DDERR_UNSUPPORTEDFORMAT.");
            break;

        // Bitmask in the pixel format requested is unsupported by DirectDraw
        case DDERR_UNSUPPORTEDMASK:
            hsAssert(false, "DDERR_UNSUPPORTEDMASK.");
            break;

        // The specified stream contains invalid data
        case DDERR_INVALIDSTREAM:
            hsAssert(false, "DDERR_INVALIDSTREAM.");
            break;

        // vertical blank is in progress
        case DDERR_VERTICALBLANKINPROGRESS:
            hsAssert(false, "DDERR_VERTICALBLANKINPROGRESS.");
            break;

        // Informs DirectDraw that the previous Blt which is transfering information
        // to or from this Surface is incomplete.
        case DDERR_WASSTILLDRAWING:
            hsAssert(false, "DDERR_WASSTILLDRAWING.");
            break;

        // Rectangle provided was not horizontally aligned on reqd. boundary
        case DDERR_XALIGN:
            hsAssert(false, "DDERR_XALIGN.");
            break;

        // The GUID passed to DirectDrawCreate is not a valid DirectDraw driver
        // identifier.
        case DDERR_INVALIDDIRECTDRAWGUID:
            hsAssert(false, "DDERR_INVALIDDIRECTDRAWGUID.");
            break;

        // A DirectDraw object representing this driver has already been created
        // for this process.
        case DDERR_DIRECTDRAWALREADYCREATED:
            hsAssert(false, "DDERR_DIRECTDRAWALREADYCREATED.");
            break;

        // A hardware only DirectDraw object creation was attempted but the driver
        // did not support any hardware.
        case DDERR_NODIRECTDRAWHW:
            hsAssert(false, "DDERR_NODIRECTDRAWHW.");
            break;

        // this process already has created a primary surface
        case DDERR_PRIMARYSURFACEALREADYEXISTS:
            hsAssert(false, "DDERR_PRIMARYSURFACEALREADYEXISTS.");
            break;

        // software emulation not available.
        case DDERR_NOEMULATION:
            hsAssert(false, "DDERR_NOEMULATION.");
            break;

        // region passed to Clipper::GetClipList is too small.
        case DDERR_REGIONTOOSMALL:
            hsAssert(false, "DDERR_REGIONTOOSMALL.");
            break;

        // an attempt was made to set a clip list for a clipper objec that
        // is already monitoring an hwnd.
        case DDERR_CLIPPERISUSINGHWND:
            hsAssert(false, "DDERR_CLIPPERISUSINGHWND.");
            break;

        // No clipper object attached to surface object
        case DDERR_NOCLIPPERATTACHED:
            hsAssert(false, "DDERR_NOCLIPPERATTACHED.");
            break;

        // Clipper notification requires an HWND or
        // no HWND has previously been set as the CooperativeLevel HWND.
        case DDERR_NOHWND:
            hsAssert(false, "DDERR_NOHWND.");
            break;

        // HWND used by DirectDraw CooperativeLevel has been subclassed,
        // this prevents DirectDraw from restoring state.
        case DDERR_HWNDSUBCLASSED:
            hsAssert(false, "DDERR_HWNDSUBCLASSED.");
            break;

        // The CooperativeLevel HWND has already been set.
        // It can not be reset while the process has surfaces or palettes created.
        case DDERR_HWNDALREADYSET:
            hsAssert(false, "DDERR_HWNDALREADYSET.");
            break;

        // No palette object attached to this surface.
        case DDERR_NOPALETTEATTACHED:
            hsAssert(false, "DDERR_NOPALETTEATTACHED.");
            break;

        // No hardware support for 16 or 256 color palettes.
        case DDERR_NOPALETTEHW:
            hsAssert(false, "DDERR_NOPALETTEHW.");
            break;

        // If a clipper object is attached to the source surface passed into a
        // BltFast call.
        case DDERR_BLTFASTCANTCLIP:
            hsAssert(false, "DDERR_BLTFASTCANTCLIP.");
            break;

        // No blter.
        case DDERR_NOBLTHW:
            hsAssert(false, "DDERR_NOBLTHW.");
            break;

        // No DirectDraw ROP hardware.
        case DDERR_NODDROPSHW:
            hsAssert(false, "DDERR_NODDROPSHW.");
            break;

        // returned when GetOverlayPosition is called on a hidden overlay
        case DDERR_OVERLAYNOTVISIBLE:
            hsAssert(false, "DDERR_OVERLAYNOTVISIBLE.");
            break;

        // returned when GetOverlayPosition is called on a overlay that UpdateOverlay
        // has never been called on to establish a destionation.
        case DDERR_NOOVERLAYDEST:
            hsAssert(false, "DDERR_NOOVERLAYDEST.");
            break;

        // returned when the position of the overlay on the destionation is no longer
        // legal for that destionation.
        case DDERR_INVALIDPOSITION:
            hsAssert(false, "DDERR_INVALIDPOSITION.");
            break;

        // returned when an overlay member is called for a non-overlay surface
        case DDERR_NOTAOVERLAYSURFACE:
            hsAssert(false, "DDERR_NOTAOVERLAYSURFACE.");
            break;

        // An attempt was made to set the cooperative level when it was already
        // set to exclusive.
        case DDERR_EXCLUSIVEMODEALREADYSET:
            hsAssert(false, "DDERR_EXCLUSIVEMODEALREADYSET.");
            break;

        // An attempt has been made to flip a surface that is not flippable.
        case DDERR_NOTFLIPPABLE:
            hsAssert(false, "DDERR_NOTFLIPPABLE.");
            break;

        // Can't duplicate primary & 3D surfaces, or surfaces that are implicitly
        // created.
        case DDERR_CANTDUPLICATE:
            hsAssert(false, "DDERR_CANTDUPLICATE.");
            break;

        // Surface was not locked.  An attempt to unlock a surface that was not
        // locked at all, or by this process, has been attempted.
        case DDERR_NOTLOCKED:
            hsAssert(false, "DDERR_NOTLOCKED.");
            break;

        // Windows can not create any more DCs, or a DC was requested for a paltte-indexed
        // surface when the surface had no palette AND the display mode was not palette-indexed
        // (in this case DirectDraw cannot select a proper palette into the DC)
        case DDERR_CANTCREATEDC:
            hsAssert(false, "DDERR_CANTCREATEDC.");
            break;

        // No DC was ever created for this surface.
        case DDERR_NODC:
            hsAssert(false, "DDERR_NODC.");
            break;

        // This surface can not be restored because it was created in a different
        // mode.
        case DDERR_WRONGMODE:
            hsAssert(false, "DDERR_WRONGMODE.");
            break;

        // This surface can not be restored because it is an implicitly created
        // surface.
        case DDERR_IMPLICITLYCREATED:
            hsAssert(false, "DDERR_IMPLICITLYCREATED.");
            break;

        // The surface being used is not a palette-based surface
        case DDERR_NOTPALETTIZED:
            hsAssert(false, "DDERR_NOTPALETTIZED.");
            break;

        // The display is currently in an unsupported mode
        case DDERR_UNSUPPORTEDMODE:
            hsAssert(false, "DDERR_UNSUPPORTEDMODE.");
            break;

        // Operation could not be carried out because there is no mip-map
        // texture mapping hardware present or available.
        case DDERR_NOMIPMAPHW:
            hsAssert(false, "DDERR_NOMIPMAPHW.");
            break;

        // The requested action could not be performed because the surface was of
        // the wrong type.
        case DDERR_INVALIDSURFACETYPE:
            hsAssert(false, "DDERR_INVALIDSURFACETYPE.");
            break;

        // Device does not support optimized surfaces, therefore no video memory optimized surfaces
        case DDERR_NOOPTIMIZEHW:
            hsAssert(false, "DDERR_NOOPTIMIZEHW.");
            break;

        // Surface is an optimized surface, but has not yet been allocated any memory
        case DDERR_NOTLOADED:
            hsAssert(false, "DDERR_NOTLOADED.");
            break;

        // Attempt was made to create or set a device window without first setting
        // the focus window
        case DDERR_NOFOCUSWINDOW:
            hsAssert(false, "DDERR_NOFOCUSWINDOW.");
            break;

        // A DC has already been returned for this surface. Only one DC can be
        // retrieved per surface.
        case DDERR_DCALREADYCREATED:
            hsAssert(false, "DDERR_DCALREADYCREATED.");
            break;

        // An attempt was made to allocate non-local video memory from a device
        // that does not support non-local video memory.
        case DDERR_NONONLOCALVIDMEM:
            hsAssert(false, "DDERR_NONONLOCALVIDMEM.");
            break;

        // The attempt to page lock a surface failed.
        case DDERR_CANTPAGELOCK:
            hsAssert(false, "DDERR_CANTPAGELOCK.");
            break;

        // The attempt to page unlock a surface failed.
        case DDERR_CANTPAGEUNLOCK:
            hsAssert(false, "DDERR_CANTPAGEUNLOCK.");
            break;

        // An attempt was made to page unlock a surface with no outstanding page locks.
        case DDERR_NOTPAGELOCKED:
            hsAssert(false, "DDERR_NOTPAGELOCKED.");
            break;

        // There is more data available than the specified buffer size could hold
        case DDERR_MOREDATA:
            hsAssert(false, "DDERR_MOREDATA.");
            break;

        // The data has expired and is therefore no longer valid.
        case DDERR_EXPIRED:
            hsAssert(false, "DDERR_EXPIRED.");
            break;

        // The video port is not active
        case DDERR_VIDEONOTACTIVE:
            hsAssert(false, "DDERR_VIDEONOTACTIVE.");
            break;

        // Surfaces created by one direct draw device cannot be used directly by
        // another direct draw device.
        case DDERR_DEVICEDOESNTOWNSURFACE:
            hsAssert(false, "DDERR_DEVICEDOESNTOWNSURFACE.");
            break;

        // An attempt was made to invoke an interface member of a DirectDraw object
        // created by CoCreateInstance() before it was initialized.
        case DDERR_NOTINITIALIZED:
            hsAssert(false, "DDERR_NOTINITIALIZED.");
            break;

        default:
            hsAssert(false, "Unknown error.");
            break;
    }
}
#endif

hsBool hsDXTDirectXCodec::ColorizeCompMipmap( plMipmap *bMap, const uint8_t *colorMask )
{
    return false;
}