#if defined(WIN32)
    #include <windows.h>
    #define GET_EXT_POINTER(name, type) \
     name = (type)wglGetProcAddress(#name)
#elif defined(LINUX)
    #include <GL/glx.h>
    extern "C" void (*glXGetProcAddressARB(const GLubyte *procName))( void );
    #define useGetProcAddress ::glXGetProcAddressARB
    #  define GET_EXT_POINTER(name, type) \
      name = (type)useGetProcAddress((const GLubyte*)#name)
#else // MACOS
    #define GET_EXT_POINTER(glname, gltype) 
    #include <OpenGL/gl.h>
    #include <GL/glext.h>
#endif
#if defined(WIN32) || defined(LINUX)
    #include <GL/gl.h>
    #include <GL/glext.h>
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "nv_dds.h"
using namespace std;
using namespace nv_dds;
#if defined(WIN32) || defined(LINUX)
PFNGLTEXIMAGE3DEXTPROC CDDSImage::glTexImage3D = NULL;
PFNGLCOMPRESSEDTEXIMAGE1DARBPROC CDDSImage::glCompressedTexImage1DARB = NULL;
PFNGLCOMPRESSEDTEXIMAGE2DARBPROC CDDSImage::glCompressedTexImage2DARB = NULL;
PFNGLCOMPRESSEDTEXIMAGE3DARBPROC CDDSImage::glCompressedTexImage3DARB = NULL;
#endif
CDDSImage::CDDSImage()
  : format(0),
    components(0),
    compressed(false),
    cubemap(false),
    volume(false),
    valid(false)
{
}
CDDSImage::~CDDSImage()
{
}
bool CDDSImage::load(string filename, bool flipImage)
{
    DDS_HEADER ddsh;
    char filecode[4];
    FILE *fp;
    int width, height, depth;
    int (CDDSImage::*sizefunc)(int, int);
    
    clear();
    
    
    fp = fopen(filename.data(), "rb");
    if (fp == NULL)
        return false;
    
    fread(filecode, 1, 4, fp);
    if (strncmp(filecode, "DDS ", 4) != 0)
    {
        fclose(fp);
        return false;
    }
    
    fread(&ddsh, sizeof(ddsh), 1, fp);
    swap_endian(&ddsh.dwSize);
    swap_endian(&ddsh.dwFlags);
    swap_endian(&ddsh.dwHeight);
    swap_endian(&ddsh.dwWidth);
    swap_endian(&ddsh.dwPitchOrLinearSize);
    swap_endian(&ddsh.dwMipMapCount);
    swap_endian(&ddsh.ddspf.dwSize);
    swap_endian(&ddsh.ddspf.dwFlags);
    swap_endian(&ddsh.ddspf.dwFourCC);
    swap_endian(&ddsh.ddspf.dwRGBBitCount);
    swap_endian(&ddsh.dwCaps1);
    swap_endian(&ddsh.dwCaps2);
    
    if (ddsh.dwCaps2 & DDS_CUBEMAP)
        cubemap = true;
    
    if ((ddsh.dwCaps2 & DDS_VOLUME) && (ddsh.dwDepth > 0))
        volume = true;
    
    if (ddsh.ddspf.dwFlags & DDS_FOURCC) 
    {
        switch(ddsh.ddspf.dwFourCC)
        {
            case FOURCC_DXT1:
                format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
                components = 3;
                compressed = true;
                break;
            case FOURCC_DXT3:
                format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
                components = 4;
                compressed = true;
                break;
            case FOURCC_DXT5:
                format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
                components = 4;
                compressed = true;
                break;
            default:
                fclose(fp);
                return false;
        }
    }
    else if (ddsh.ddspf.dwFlags == DDS_RGBA && ddsh.ddspf.dwRGBBitCount == 32)
    {
        format = GL_BGRA_EXT; 
        compressed = false;
        components = 4;
    }
    else if (ddsh.ddspf.dwFlags == DDS_RGB  && ddsh.ddspf.dwRGBBitCount == 32)
    {
        format = GL_BGRA_EXT; 
        compressed = false;
        components = 4;
    }
    else if (ddsh.ddspf.dwFlags == DDS_RGB  && ddsh.ddspf.dwRGBBitCount == 24)
    {
        format = GL_BGR_EXT; 
        compressed = false;
        components = 3;
    }
    else if (ddsh.ddspf.dwRGBBitCount == 8)
    {
        format = GL_LUMINANCE; 
        compressed = false;
        components = 1;
    }
    else 
    {
        fclose(fp);
        return false;
    }
    
    
    width = ddsh.dwWidth;
    height = ddsh.dwHeight;
    depth = clamp_size(ddsh.dwDepth);   
    
    
    
    sizefunc = (compressed ? &CDDSImage::size_dxtc : &CDDSImage::size_rgb);
    
    for (int n = 0; n < (cubemap ? 6 : 1); n++)
    {
        int size; 
        
        size = (this->*sizefunc)(width, height)*depth;
        
        CTexture img(width, height, depth, size);
        fread(img, 1, img.size, fp);
        align_memory(&img);
        
        if (!cubemap && flipImage)
            flip(img, img.width, img.height, img.depth, img.size);
        
        int w = clamp_size(width >> 1);
        int h = clamp_size(height >> 1);
        int d = clamp_size(depth >> 1); 
        
        int numMipmaps = ddsh.dwMipMapCount;
        
        
        if (numMipmaps != 0)
            numMipmaps--;
        
        for (int i = 0; i < numMipmaps && (w || h); i++)
        {
            
            size = (this->*sizefunc)(w, h)*d;
            CSurface mipmap(w, h, d, size);
            fread(mipmap, 1, mipmap.size, fp);
            
            if (!cubemap && flipImage)
            {
                flip(mipmap, mipmap.width, mipmap.height, mipmap.depth, 
                    mipmap.size);
            }
            img.mipmaps.push_back(mipmap);
            
            w = clamp_size(w >> 1);
            h = clamp_size(h >> 1);
            d = clamp_size(d >> 1); 
        }
        images.push_back(img);
    }
    
    if (cubemap && flipImage)
    {
        CTexture tmp;
        tmp = images[3];
        images[3] = images[2];
        images[2] = tmp;
    }
    
    fclose(fp);
    valid = true;
    return true;
}
void CDDSImage::clear()
{
    components = 0;
    format = 0;
    compressed = false;
    cubemap = false;
    volume = false;
    valid = false;
    images.clear();
}
CTexture &CDDSImage::operator[](int index)
{ 
    
    assert(valid);
    assert(index < (int)images.size());
    return images[index]; 
}
CDDSImage::operator char*()
{ 
    assert(valid);
    return images[0]; 
}
bool CDDSImage::upload_texture1D()
{
    assert(valid);
    assert(images[0].height == 1);
    assert(images[0].width > 0);
    assert(images[0]);
    if (compressed)
    {
#if defined(WIN32) || defined(LINUX)
        
        if (glCompressedTexImage1DARB == NULL)
        {
            GET_EXT_POINTER(glCompressedTexImage1DARB, 
                            PFNGLCOMPRESSEDTEXIMAGE1DARBPROC);
        }
        
        if (glCompressedTexImage1DARB == NULL)
            return false;
        
        glCompressedTexImage1DARB(GL_TEXTURE_1D, 0, format, 
            images[0].width, 0, images[0].size, 
            images[0]);
        
        
        unsigned int numMipMaps = (unsigned int)images[0].mipmaps.size();
        if( numMipMaps)
        {
            for (unsigned int i = 0; i < numMipMaps; i++)
            {
                glCompressedTexImage1DARB(GL_TEXTURE_1D, i+1, format, 
                    images[0].mipmaps[i].width, 0, 
                    images[0].mipmaps[i].size, 
                    images[0].mipmaps[i]);
            }
            glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_BASE_LEVEL, 0 );
            glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAX_LEVEL, numMipMaps);       
        }
#else
        glCompressedTexImage1D(GL_TEXTURE_1D, 0, format, 
            images[0].width, 0, images[0].size, 
            images[0]);
        
        
        unsigned int numMipMaps = (unsigned int)images[0].mipmaps.size();
        if( numMipMaps)
        {
            for (unsigned int i = 0; i < numMipMaps; i++)
            {
                glCompressedTexImage1D(GL_TEXTURE_1D, i+1, format, 
                    images[0].mipmaps[i].width, 0, 
                    images[0].mipmaps[i].size, 
                    images[0].mipmaps[i]);
            }
            glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_BASE_LEVEL, 0 );
            glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAX_LEVEL, numMipMaps);       
        }
#endif
    }
    else
    {
        glTexImage1D(GL_TEXTURE_1D, 0, format, images[0].width, 0,
            format, GL_UNSIGNED_BYTE, images[0]);
        
        unsigned int numMipMaps = (unsigned int)images[0].mipmaps.size();
        if( numMipMaps)
        {
            for (unsigned int i = 0; i < numMipMaps; i++)
            {
                glTexImage1D(GL_TEXTURE_1D, i+1, components, 
                    images[0].mipmaps[i].width, 0, format, 
                    GL_UNSIGNED_BYTE, images[0].mipmaps[i]);
            }
            glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_BASE_LEVEL, 0 );
            glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAX_LEVEL, numMipMaps);       
        }
    }
    return true;
}
bool CDDSImage::upload_texture2D(int imageIndex, GLenum target)
{
    assert(valid);
    assert(imageIndex >= 0);
    assert(imageIndex < (int)images.size());
    assert(images[imageIndex].height > 0);
    assert(images[imageIndex].width > 0);
    assert(images[imageIndex]);
    assert(target == GL_TEXTURE_2D || 
        (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB && 
         target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB));
    
    if (compressed)
    {
#if defined(WIN32) || defined(LINUX)
        
        if (glCompressedTexImage2DARB == NULL)
        {
            GET_EXT_POINTER(glCompressedTexImage2DARB, 
                            PFNGLCOMPRESSEDTEXIMAGE2DARBPROC);
        }
        
        if (glCompressedTexImage2DARB == NULL)
            return false;
        
        glCompressedTexImage2DARB(target, 0, format, 
            images[imageIndex].width, images[imageIndex].height, 0, 
            images[imageIndex].size, images[imageIndex]);
        
        
        unsigned int numMipMaps = (unsigned int)images[imageIndex].mipmaps.size();
        if( numMipMaps)
        {
            for (unsigned int i = 0; i < numMipMaps; i++)
            {
                glCompressedTexImage2DARB(target, i+1, format, 
                    images[imageIndex].mipmaps[i].width, 
                    images[imageIndex].mipmaps[i].height, 0, 
                    images[imageIndex].mipmaps[i].size, 
                    images[imageIndex].mipmaps[i]);
            }
            glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0 );
            glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, numMipMaps);      
        }
#else
        glCompressedTexImage2D(target, 0, format, 
            images[imageIndex].width, images[imageIndex].height, 0, 
            images[imageIndex].size, images[imageIndex]);
        
        
        unsigned int numMipMaps = (unsigned int)images[imageIndex].mipmaps.size();
        if( numMipMaps)
        {
            for (unsigned int i = 0; i < numMipMaps; i++)
            {
                glCompressedTexImage2D(target, i+1, format, 
                    images[imageIndex].mipmaps[i].width, 
                    images[imageIndex].mipmaps[i].height, 0, 
                    images[imageIndex].mipmaps[i].size, 
                    images[imageIndex].mipmaps[i]);
            }
            glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0 );
            glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, numMipMaps);      
        }
#endif
    }
    else
    {
        glTexImage2D(target, 0, components, images[imageIndex].width, 
            images[imageIndex].height, 0, format, GL_UNSIGNED_BYTE, 
            images[imageIndex]);
        
        unsigned int numMipMaps = (unsigned int)images[imageIndex].mipmaps.size();
        if( numMipMaps)
        {
            for (unsigned int i = 0; i < numMipMaps; i++)
            {
                glTexImage2D(target, i+1, components, 
                    images[imageIndex].mipmaps[i].width, 
                    images[imageIndex].mipmaps[i].height, 0, format, 
                    GL_UNSIGNED_BYTE, images[imageIndex].mipmaps[i]); 
            }
            glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0 );
            glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, numMipMaps);      
        }
    }
    
    return true;
}
#ifdef GL_NV_texture_rectangle
bool CDDSImage::upload_textureRectangle()
{
    assert(valid);
    assert(images.size() >= 1);
    if (!upload_texture2D(0, GL_TEXTURE_RECTANGLE_NV))
        return false;
    return true;
}
#endif
bool CDDSImage::upload_textureCubemap()
{
    assert(valid);
    assert(cubemap);
    assert(images.size() == 6);
    GLenum target;
    
    for (int n = 0; n < 6; n++)
    {
        
        target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB+n;
        if (!upload_texture2D(n, target))
            return false;
    }
    return true;
}
bool CDDSImage::upload_texture3D()
{
    assert(valid);
    assert(volume);
    assert(images[0].depth >= 1);
    if (compressed)
    {
#if defined(WIN32) || defined(LINUX)
        
        if (glCompressedTexImage3DARB == NULL)
        {
            GET_EXT_POINTER(glCompressedTexImage3DARB, 
                            PFNGLCOMPRESSEDTEXIMAGE3DARBPROC);
        }
        if (glCompressedTexImage3DARB == NULL)
            return false;
        glCompressedTexImage3DARB(GL_TEXTURE_3D, 0, format,  images[0].width, 
            images[0].height, images[0].depth, 0, images[0].size, images[0]);
        
        
        unsigned int numMipMaps = (unsigned int)images[0].mipmaps.size();
        if( numMipMaps)
        {
            for (unsigned int i = 0; i < numMipMaps; i++)
            {
                glCompressedTexImage3DARB(GL_TEXTURE_3D, i+1, format, 
                    images[0].mipmaps[i].width, images[0].mipmaps[i].height, 
                    images[0].depth, 0, images[0].mipmaps[i].size, 
                    images[0].mipmaps[i]);
            }
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 0 );
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, numMipMaps);       
        }
#else
        glCompressedTexImage3D(GL_TEXTURE_3D, 0, format,  images[0].width, 
            images[0].height, images[0].depth, 0, images[0].size, images[0]);
        
        
        unsigned int numMipMaps = (unsigned int)images[0].mipmaps.size();
        if( numMipMaps)
        {
            for (unsigned int i = 0; i < numMipMaps; i++)
            {
                glCompressedTexImage3D(GL_TEXTURE_3D, i+1, format, 
                    images[0].mipmaps[i].width, images[0].mipmaps[i].height, 
                    images[0].depth, 0, images[0].mipmaps[i].size, 
                    images[0].mipmaps[i]);
            }
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 0 );
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, numMipMaps);       
        }
#endif
    }
    else
    {
#if defined(WIN32) || defined(LINUX)
        
        if (glTexImage3D == NULL)
        {
            GET_EXT_POINTER(glTexImage3D, PFNGLTEXIMAGE3DEXTPROC);
        }
    
        if (glTexImage3D == NULL)
            return false;
#endif
    
        glTexImage3D(GL_TEXTURE_3D, 0, components, images[0].width, 
            images[0].height, images[0].depth, 0, format, GL_UNSIGNED_BYTE, 
            images[0]);
        
        
        unsigned int numMipMaps = (unsigned int)images[0].mipmaps.size();
        if( numMipMaps)
        {
            for (unsigned int i = 0; i < numMipMaps; i++)
            {
                glTexImage3D(GL_TEXTURE_3D, i+1, components, 
                    images[0].mipmaps[i].width, images[0].mipmaps[i].height, 
                    images[0].mipmaps[i].depth, 0, format, GL_UNSIGNED_BYTE, 
                    images[0].mipmaps[i]);
            }
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 0 );
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, numMipMaps);       
        }
    }
    return true;
}
inline int CDDSImage::clamp_size(int size)
{
    if (size <= 0)
        size = 1;
    return size;
}
inline int CDDSImage::get_line_width(int width, int bpp)
{
    return ((width * bpp + 31) & -32) >> 3;
}
inline int CDDSImage::size_dxtc(int width, int height)
{
    return ((width+3)/4)*((height+3)/4)*
        (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);   
}
inline int CDDSImage::size_rgb(int width, int height)
{
    return width*height*components;
}
inline void CDDSImage::swap_endian(void *val)
{
#ifdef MACOS
    unsigned int *ival = (unsigned int *)val;
    *ival = ((*ival >> 24) & 0x000000ff) |
            ((*ival >>  8) & 0x0000ff00) |
            ((*ival <<  8) & 0x00ff0000) |
            ((*ival << 24) & 0xff000000);
#endif
}
void CDDSImage::align_memory(CTexture *surface)
{
    
    if (compressed || volume || cubemap)
        return;
    
    int linesize = get_line_width(surface->width, components*8);
    int imagesize = linesize*surface->height;
    
    if (surface->size == imagesize)
        return;
    
    CTexture newSurface(surface->width, surface->height, surface->depth, 
        imagesize);
    
    char *srcimage = (char*)*surface;
    char *dstimage = (char*)newSurface;
    for (int n = 0; n < surface->depth; n++)
    {
        char *curline = srcimage;
        char *newline = dstimage;
        int imsize = surface->size / surface->depth;
        int lnsize = imsize / surface->height;
        
        for (int i = 0; i < surface->height; i++)
        {
            memcpy(newline, curline, lnsize);
            newline += linesize;
            curline += lnsize;
        }
    }
    
    *surface = newSurface;
}
void CDDSImage::flip(char *image, int width, int height, int depth, int size)
{
    int linesize;
    int offset;
    if (!compressed)
    {
        assert(depth > 0);
        int imagesize = size/depth;
        linesize = imagesize / height;
        for (int n = 0; n < depth; n++)
        {
            offset = imagesize*n;
            char *top = image + offset;
            char *bottom = top + (imagesize-linesize);
    
            for (int i = 0; i < (height >> 1); i++)
            {
                swap(bottom, top, linesize);
                top += linesize;
                bottom -= linesize;
            }
        }
    }
    else
    {
        void (CDDSImage::*flipblocks)(DXTColBlock*, int);
        int xblocks = width / 4;
        int yblocks = height / 4;
        int blocksize;
        switch (format)
        {
            case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: 
                blocksize = 8;
                flipblocks = &CDDSImage::flip_blocks_dxtc1; 
                break;
            case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: 
                blocksize = 16;
                flipblocks = &CDDSImage::flip_blocks_dxtc3; 
                break;
            case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: 
                blocksize = 16;
                flipblocks = &CDDSImage::flip_blocks_dxtc5; 
                break;
            default:
                return;
        }
        linesize = xblocks * blocksize;
        DXTColBlock *top;
        DXTColBlock *bottom;
    
        for (int j = 0; j < (yblocks >> 1); j++)
        {
            top = (DXTColBlock*)(image + j * linesize);
            bottom = (DXTColBlock*)(image + (((yblocks-j)-1) * linesize));
            (this->*flipblocks)(top, xblocks);
            (this->*flipblocks)(bottom, xblocks);
            swap(bottom, top, linesize);
        }
    }
}    
void CDDSImage::swap(void *byte1, void *byte2, int size)
{
    unsigned char *tmp = new unsigned char[size];
    memcpy(tmp, byte1, size);
    memcpy(byte1, byte2, size);
    memcpy(byte2, tmp, size);
    delete [] tmp;
}
void CDDSImage::flip_blocks_dxtc1(DXTColBlock *line, int numBlocks)
{
    DXTColBlock *curblock = line;
    for (int i = 0; i < numBlocks; i++)
    {
        swap(&curblock->row[0], &curblock->row[3], sizeof(unsigned char));
        swap(&curblock->row[1], &curblock->row[2], sizeof(unsigned char));
        curblock++;
    }
}
void CDDSImage::flip_blocks_dxtc3(DXTColBlock *line, int numBlocks)
{
    DXTColBlock *curblock = line;
    DXT3AlphaBlock *alphablock;
    for (int i = 0; i < numBlocks; i++)
    {
        alphablock = (DXT3AlphaBlock*)curblock;
        swap(&alphablock->row[0], &alphablock->row[3], sizeof(unsigned short));
        swap(&alphablock->row[1], &alphablock->row[2], sizeof(unsigned short));
        curblock++;
        swap(&curblock->row[0], &curblock->row[3], sizeof(unsigned char));
        swap(&curblock->row[1], &curblock->row[2], sizeof(unsigned char));
        curblock++;
    }
}
void CDDSImage::flip_dxt5_alpha(DXT5AlphaBlock *block)
{
    unsigned char gBits[4][4];
    
    const unsigned int mask = 0x00000007;          
    unsigned int bits = 0;
    memcpy(&bits, &block->row[0], sizeof(unsigned char) * 3);
    gBits[0][0] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[0][1] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[0][2] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[0][3] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[1][0] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[1][1] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[1][2] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[1][3] = (unsigned char)(bits & mask);
    bits = 0;
    memcpy(&bits, &block->row[3], sizeof(unsigned char) * 3);
    gBits[2][0] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[2][1] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[2][2] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[2][3] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[3][0] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[3][1] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[3][2] = (unsigned char)(bits & mask);
    bits >>= 3;
    gBits[3][3] = (unsigned char)(bits & mask);
    
    memset(block->row, 0, sizeof(unsigned char) * 6);
    unsigned int *pBits = ((unsigned int*) &(block->row[0]));
    *pBits = *pBits | (gBits[3][0] << 0);
    *pBits = *pBits | (gBits[3][1] << 3);
    *pBits = *pBits | (gBits[3][2] << 6);
    *pBits = *pBits | (gBits[3][3] << 9);
    *pBits = *pBits | (gBits[2][0] << 12);
    *pBits = *pBits | (gBits[2][1] << 15);
    *pBits = *pBits | (gBits[2][2] << 18);
    *pBits = *pBits | (gBits[2][3] << 21);
    pBits = ((unsigned int*) &(block->row[3]));
    *pBits = *pBits | (gBits[1][0] << 0);
    *pBits = *pBits | (gBits[1][1] << 3);
    *pBits = *pBits | (gBits[1][2] << 6);
    *pBits = *pBits | (gBits[1][3] << 9);
    *pBits = *pBits | (gBits[0][0] << 12);
    *pBits = *pBits | (gBits[0][1] << 15);
    *pBits = *pBits | (gBits[0][2] << 18);
    *pBits = *pBits | (gBits[0][3] << 21);
}
void CDDSImage::flip_blocks_dxtc5(DXTColBlock *line, int numBlocks)
{
    DXTColBlock *curblock = line;
    DXT5AlphaBlock *alphablock;
    
    for (int i = 0; i < numBlocks; i++)
    {
        alphablock = (DXT5AlphaBlock*)curblock;
        
        flip_dxt5_alpha(alphablock);
        curblock++;
        swap(&curblock->row[0], &curblock->row[3], sizeof(unsigned char));
        swap(&curblock->row[1], &curblock->row[2], sizeof(unsigned char));
        curblock++;
    }
}
CTexture::CTexture()
  : CSurface()  
{
}
CTexture::CTexture(int w, int h, int d, int imgSize)
  : CSurface(w, h, d, imgSize)  
{
}
CTexture::CTexture(const CTexture ©)
  : CSurface(copy)
{
    for (unsigned int i = 0; i < copy.mipmaps.size(); i++)
        mipmaps.push_back(copy.mipmaps[i]);
}
CTexture &CTexture::operator= (const CTexture &rhs)
{
    if (this != &rhs)
    {
        CSurface::operator = (rhs);
        mipmaps.clear();
        for (unsigned int i = 0; i < rhs.mipmaps.size(); i++)
        {
            mipmaps.push_back(rhs.mipmaps[i]);
        }
    }
    return *this;
}
CTexture::~CTexture()
{
    mipmaps.clear();
}
CSurface::CSurface()
  : width(0),
    height(0),
    depth(0),
    size(0),
    pixels(NULL)
{
}
CSurface::CSurface(int w, int h, int d, int imgsize)
{
    pixels = NULL;
    create(w, h, d, imgsize);
}
CSurface::CSurface(const CSurface ©)
  : width(0),
    height(0),
    depth(0),
    size(0),
    pixels(NULL)
{
    if (copy.pixels)
    {
        size = copy.size;
        width = copy.width;
        height = copy.height;
        depth = copy.depth;
        pixels = new char[size];
        memcpy(pixels, copy.pixels, copy.size);
    }
}
CSurface &CSurface::operator= (const CSurface &rhs)
{
    if (this != &rhs)
    {
        clear();
        if (rhs.pixels)
        {
            size = rhs.size;
            width = rhs.width;
            height = rhs.height;
            depth = rhs.depth;
            pixels = new char[size];
            memcpy(pixels, rhs.pixels, size);
        }
    }
    return *this;
}
CSurface::~CSurface()
{
    clear();
}
CSurface::operator char*()
{ 
    return pixels; 
}
void CSurface::create(int w, int h, int d, int imgsize)
{
    clear();
    width = w;
    height = h;
    depth = d;
    size = imgsize;
    pixels = new char[imgsize];
}
void CSurface::clear()
{
    delete [] pixels;
    pixels = NULL;
}