GLES2N64 (from mupen64plus-ae) plugin. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / gles2n64 / src / Textures.cpp
diff --git a/source/gles2n64/src/Textures.cpp b/source/gles2n64/src/Textures.cpp
new file mode 100644 (file)
index 0000000..d39f32d
--- /dev/null
@@ -0,0 +1,1334 @@
+#include <time.h>
+#include <stdlib.h>
+#include <memory.h>
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#include "Common.h"
+#include "Config.h"
+#include "OpenGL.h"
+#include "Textures.h"
+#include "GBI.h"
+#include "RSP.h"
+#include "gDP.h"
+#include "gSP.h"
+#include "N64.h"
+#include "CRC.h"
+#include "convert.h"
+#include "2xSAI.h"
+//#include "FrameBuffer.h"
+
+#define FORMAT_NONE     0
+#define FORMAT_I8       1
+#define FORMAT_IA88     2
+#define FORMAT_RGBA4444 3
+#define FORMAT_RGBA5551 4
+#define FORMAT_RGBA8888 5
+
+//#define PRINT_TEXTUREFORMAT
+
+TextureCache    cache;
+
+typedef u32 (*GetTexelFunc)( void *src, u16 x, u16 i, u8 palette );
+
+u32 GetNone( void *src, u16 x, u16 i, u8 palette )
+{
+    return 0x00000000;
+}
+
+u32 GetCI4IA_RGBA4444( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    if (x & 1)
+        return IA88_RGBA4444( *(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)] );
+    else
+        return IA88_RGBA4444( *(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)] );
+}
+
+u32 GetCI4IA_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    if (x & 1)
+        return IA88_RGBA8888( *(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)] );
+    else
+        return IA88_RGBA8888( *(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)] );
+}
+
+u32 GetCI4RGBA_RGBA5551( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    if (x & 1)
+        return RGBA5551_RGBA5551( *(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)] );
+    else
+        return RGBA5551_RGBA5551( *(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)] );
+}
+
+u32 GetCI4RGBA_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    if (x & 1)
+        return RGBA5551_RGBA8888( *(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)] );
+    else
+        return RGBA5551_RGBA8888( *(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)] );
+}
+
+u32 GetIA31_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    return IA31_RGBA8888( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
+}
+
+u32 GetIA31_RGBA4444( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    return IA31_RGBA4444( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
+}
+
+u32 GetIA31_IA88( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    return IA31_IA88( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
+}
+
+u32 GetI4_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    return I4_RGBA8888( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
+}
+
+u32 GetI4_RGBA4444( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    return I4_RGBA4444( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
+}
+
+u32 GetI4_I8( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    return I4_I8( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
+}
+
+
+u32 GetI4_IA88( void *src, u16 x, u16 i, u8 palette )
+{
+    u8 color4B = ((u8*)src)[(x>>1)^(i<<1)];
+    return I4_IA88( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
+}
+
+u32 GetCI8IA_RGBA4444( void *src, u16 x, u16 i, u8 palette )
+{
+    return IA88_RGBA4444( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] );
+}
+
+u32 GetCI8IA_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    return IA88_RGBA8888( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] );
+}
+
+u32 GetCI8RGBA_RGBA5551( void *src, u16 x, u16 i, u8 palette )
+{
+    return RGBA5551_RGBA5551( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] );
+}
+
+u32 GetCI8RGBA_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    return RGBA5551_RGBA8888( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] );
+}
+
+u32 GetIA44_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    return IA44_RGBA8888(((u8*)src)[x^(i<<1)]);
+}
+
+u32 GetIA44_RGBA4444( void *src, u16 x, u16 i, u8 palette )
+{
+    return IA44_RGBA4444(((u8*)src)[x^(i<<1)]);
+}
+
+u32 GetIA44_IA88( void *src, u16 x, u16 i, u8 palette )
+{
+    return IA44_IA88(((u8*)src)[x^(i<<1)]);
+}
+
+u32 GetI8_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    return I8_RGBA8888(((u8*)src)[x^(i<<1)]);
+}
+
+u32 GetI8_I8( void *src, u16 x, u16 i, u8 palette )
+{
+    return ((u8*)src)[x^(i<<1)];
+}
+
+u32 GetI8_IA88( void *src, u16 x, u16 i, u8 palette )
+{
+    return I8_IA88(((u8*)src)[x^(i<<1)]);
+}
+
+u32 GetI8_RGBA4444( void *src, u16 x, u16 i, u8 palette )
+{
+    return I8_RGBA4444(((u8*)src)[x^(i<<1)]);
+}
+
+u32 GetRGBA5551_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    return RGBA5551_RGBA8888( ((u16*)src)[x^i] );
+}
+
+u32 GetRGBA5551_RGBA5551( void *src, u16 x, u16 i, u8 palette )
+{
+    return RGBA5551_RGBA5551( ((u16*)src)[x^i] );
+}
+
+u32 GetIA88_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    return IA88_RGBA8888(((u16*)src)[x^i]);
+}
+
+u32 GetIA88_RGBA4444( void *src, u16 x, u16 i, u8 palette )
+{
+    return IA88_RGBA4444(((u16*)src)[x^i]);
+}
+
+u32 GetIA88_IA88( void *src, u16 x, u16 i, u8 palette )
+{
+    return IA88_IA88(((u16*)src)[x^i]);
+}
+
+u32 GetRGBA8888_RGBA8888( void *src, u16 x, u16 i, u8 palette )
+{
+    return ((u32*)src)[x^i];
+}
+
+u32 GetRGBA8888_RGBA4444( void *src, u16 x, u16 i, u8 palette )
+{
+    return RGBA8888_RGBA4444(((u32*)src)[x^i]);
+}
+
+
+struct TextureFormat
+{
+    int format;
+    GetTexelFunc getTexel;
+    int lineShift, maxTexels;
+};
+
+
+TextureFormat textureFormatIA[4*6] =
+{
+    // 4-bit
+    {   FORMAT_RGBA5551,    GetCI4RGBA_RGBA5551,    4,  4096 }, // RGBA (SELECT)
+    {   FORMAT_NONE,        GetNone,                4,  8192 }, // YUV
+    {   FORMAT_RGBA5551,    GetCI4RGBA_RGBA5551,    4,  4096 }, // CI
+    {   FORMAT_IA88,        GetIA31_IA88,           4,  8192 }, // IA
+    {   FORMAT_IA88,        GetI4_IA88,             4,  8192 }, // I
+    {   FORMAT_RGBA8888,    GetCI4IA_RGBA8888,      4,  4096 }, // IA Palette
+    // 8-bit
+    {   FORMAT_RGBA5551,    GetCI8RGBA_RGBA5551,    3,  2048 }, // RGBA (SELECT)
+    {   FORMAT_NONE,        GetNone,                3,  4096 }, // YUV
+    {   FORMAT_RGBA5551,    GetCI8RGBA_RGBA5551,    3,  2048 }, // CI
+    {   FORMAT_IA88,        GetIA44_IA88,           3,  4096 }, // IA
+    {   FORMAT_IA88,        GetI8_IA88,             3,  4096 }, // I
+    {   FORMAT_RGBA8888,    GetCI8IA_RGBA8888,      3,  2048 }, // IA Palette
+    // 16-bit
+    {   FORMAT_RGBA5551,    GetRGBA5551_RGBA5551,   2,  2048 }, // RGBA
+    {   FORMAT_NONE,        GetNone,                2,  2048 }, // YUV
+    {   FORMAT_NONE,        GetNone,                2,  2048 }, // CI
+    {   FORMAT_IA88,        GetIA88_IA88,           2,  2048 }, // IA
+    {   FORMAT_NONE,        GetNone,                2,  2048 }, // I
+    {   FORMAT_NONE,        GetNone,                2,  2048 }, // IA Palette
+    // 32-bit
+    {   FORMAT_RGBA8888,    GetRGBA8888_RGBA8888,   2,  1024 }, // RGBA
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // YUV
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // CI
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // IA
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // I
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // IA Palette
+};
+
+TextureFormat textureFormatRGBA[4*6] =
+{
+    // 4-bit
+    {   FORMAT_RGBA5551,    GetCI4RGBA_RGBA5551,    4,  4096 }, // RGBA (SELECT)
+    {   FORMAT_NONE,        GetNone,                4,  8192 }, // YUV
+    {   FORMAT_RGBA5551,    GetCI4RGBA_RGBA5551,    4,  4096 }, // CI
+    {   FORMAT_RGBA4444,    GetIA31_RGBA4444,       4,  8192 }, // IA
+    {   FORMAT_RGBA4444,    GetI4_RGBA4444,         4,  8192 }, // I
+    {   FORMAT_RGBA8888,    GetCI4IA_RGBA8888,      4,  4096 }, // IA Palette
+    // 8-bit
+    {   FORMAT_RGBA5551,    GetCI8RGBA_RGBA5551,    3,  2048 }, // RGBA (SELECT)
+    {   FORMAT_NONE,        GetNone,                3,  4096 }, // YUV
+    {   FORMAT_RGBA5551,    GetCI8RGBA_RGBA5551,    3,  2048 }, // CI
+    {   FORMAT_RGBA4444,    GetIA44_RGBA4444,       3,  4096 }, // IA
+    {   FORMAT_RGBA8888,    GetI8_RGBA8888,         3,  4096 }, // I
+    {   FORMAT_RGBA8888,    GetCI8IA_RGBA8888,      3,  2048 }, // IA Palette
+    // 16-bit
+    {   FORMAT_RGBA5551,    GetRGBA5551_RGBA5551,   2,  2048 }, // RGBA
+    {   FORMAT_NONE,        GetNone,                2,  2048 }, // YUV
+    {   FORMAT_NONE,        GetNone,                2,  2048 }, // CI
+    {   FORMAT_RGBA8888,    GetIA88_RGBA8888,       2,  2048 }, // IA
+    {   FORMAT_NONE,        GetNone,                2,  2048 }, // I
+    {   FORMAT_NONE,        GetNone,                2,  2048 }, // IA Palette
+    // 32-bit
+    {   FORMAT_RGBA8888,    GetRGBA8888_RGBA8888,   2,  1024 }, // RGBA
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // YUV
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // CI
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // IA
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // I
+    {   FORMAT_NONE,        GetNone,                2,  1024 }, // IA Palette
+};
+
+
+TextureFormat *textureFormat = textureFormatIA;
+
+void __texture_format_rgba(int size, int format, TextureFormat *texFormat)
+{
+    if (size < G_IM_SIZ_16b)
+    {
+        if (gDP.otherMode.textureLUT == G_TT_NONE)
+            *texFormat = textureFormat[size*6 + G_IM_FMT_I];
+        else if (gDP.otherMode.textureLUT == G_TT_RGBA16)
+            *texFormat = textureFormat[size*6 + G_IM_FMT_CI];
+        else
+            *texFormat = textureFormat[size*6 + G_IM_FMT_IA];
+    }
+    else
+    {
+        *texFormat = textureFormat[size*6 + G_IM_FMT_RGBA];
+    }
+}
+
+void __texture_format_ci(int size, int format, TextureFormat *texFormat)
+{
+    switch(size)
+    {
+        case G_IM_SIZ_4b:
+            if (gDP.otherMode.textureLUT == G_TT_IA16)
+                *texFormat = textureFormat[G_IM_SIZ_4b*6 + G_IM_FMT_CI_IA];
+            else
+                *texFormat = textureFormat[G_IM_SIZ_4b*6 + G_IM_FMT_CI];
+            break;
+
+        case G_IM_SIZ_8b:
+            if (gDP.otherMode.textureLUT == G_TT_NONE)
+                *texFormat = textureFormat[G_IM_SIZ_8b*6 + G_IM_FMT_I];
+            else if (gDP.otherMode.textureLUT == G_TT_IA16)
+                *texFormat = textureFormat[G_IM_SIZ_8b*6 + G_IM_FMT_CI_IA];
+            else
+                *texFormat = textureFormat[G_IM_SIZ_8b*6 + G_IM_FMT_CI];
+            break;
+
+        default:
+            *texFormat = textureFormat[size*6 + format];
+    }
+}
+
+void __texture_format(int size, int format, TextureFormat *texFormat)
+{
+    if (format == G_IM_FMT_RGBA)
+    {
+        __texture_format_rgba(size, format, texFormat);
+    }
+    else if (format == G_IM_FMT_YUV)
+    {
+        *texFormat = textureFormat[size*6 + G_IM_FMT_YUV];
+    }
+    else if (format == G_IM_FMT_CI)
+    {
+        __texture_format_ci(size, format, texFormat);
+    }
+    else if (format == G_IM_FMT_IA)
+    {
+        if (gDP.otherMode.textureLUT != G_TT_NONE)
+            __texture_format_ci(size, format, texFormat);
+        else
+            *texFormat = textureFormat[size*6 + G_IM_FMT_IA];
+    }
+    else if (format == G_IM_FMT_I)
+    {
+        if (gDP.otherMode.textureLUT == G_TT_NONE)
+            *texFormat = textureFormat[size*6 + G_IM_FMT_I];
+        else
+            __texture_format_ci(size, format, texFormat);
+    }
+}
+
+
+int isTexCacheInit = 0;
+
+void TextureCache_Init()
+{
+    u32 dummyTexture[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+    isTexCacheInit = 1;
+    cache.current[0] = NULL;
+    cache.current[1] = NULL;
+    cache.top = NULL;
+    cache.bottom = NULL;
+    cache.numCached = 0;
+    cache.cachedBytes = 0;
+
+#ifdef __HASHMAP_OPT
+    cache.hash.init(11);
+#endif
+
+    if (config.texture.useIA) textureFormat = textureFormatIA;
+    else textureFormat = textureFormatRGBA;
+
+    glPixelStorei(GL_PACK_ALIGNMENT, 1);
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    glGenTextures( 32, cache.glNoiseNames );
+
+    srand(time(NULL));
+    u8 noise[64*64*2];
+    for (u32 i = 0; i < 32; i++)
+    {
+        glBindTexture( GL_TEXTURE_2D, cache.glNoiseNames[i] );
+        for (u32 y = 0; y < 64; y++)
+        {
+            for (u32 x = 0; x < 64; x++)
+            {
+                u32 r = (rand()&0xFF);
+                noise[y*64*2+x*2] = r;
+                noise[y*64*2+x*2+1] = r;
+            }
+        }
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 64, 64, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, noise);
+    }
+
+    cache.dummy = TextureCache_AddTop();
+    cache.dummy->address = 0;
+    cache.dummy->clampS = 1;
+    cache.dummy->clampT = 1;
+    cache.dummy->clampWidth = 4;
+    cache.dummy->clampHeight = 4;
+    cache.dummy->crc = 0;
+    cache.dummy->format = 0;
+    cache.dummy->size = 0;
+    cache.dummy->width = 4;
+    cache.dummy->height = 4;
+    cache.dummy->realWidth = 0;
+    cache.dummy->realHeight = 0;
+    cache.dummy->maskS = 0;
+    cache.dummy->maskT = 0;
+    cache.dummy->scaleS = 0.5f;
+    cache.dummy->scaleT = 0.5f;
+    cache.dummy->shiftScaleS = 1.0f;
+    cache.dummy->shiftScaleT = 1.0f;
+    cache.dummy->textureBytes = 64;
+    cache.dummy->tMem = 0;
+
+    glBindTexture( GL_TEXTURE_2D, cache.dummy->glName );
+    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, dummyTexture);
+
+    cache.cachedBytes = cache.dummy->textureBytes;
+    TextureCache_ActivateDummy(0);
+    TextureCache_ActivateDummy(1);
+    CRC_BuildTable();
+}
+
+bool TextureCache_Verify()
+{
+    u16 i = 0;
+    CachedTexture *current;
+
+    current = cache.top;
+
+    while (current)
+    {
+        i++;
+        current = current->lower;
+    }
+    if (i != cache.numCached) return false;
+
+    i = 0;
+    current = cache.bottom;
+    while (current)
+    {
+        i++;
+        current = current->higher;
+    }
+    if (i != cache.numCached) return false;
+
+    return true;
+}
+
+void TextureCache_RemoveBottom()
+{
+    CachedTexture *newBottom = cache.bottom->higher;
+
+#ifdef __HASHMAP_OPT
+    CachedTexture* tex= cache.hash.find(cache.bottom->crc);
+    if (tex == cache.bottom)
+        cache.hash.insert(cache.bottom->crc, NULL);
+#endif
+
+    glDeleteTextures( 1, &cache.bottom->glName );
+    cache.cachedBytes -= cache.bottom->textureBytes;
+
+    if (cache.bottom == cache.top)
+        cache.top = NULL;
+
+    free( cache.bottom );
+
+    cache.bottom = newBottom;
+
+    if (cache.bottom)
+        cache.bottom->lower = NULL;
+
+    cache.numCached--;
+}
+
+void TextureCache_Remove( CachedTexture *texture )
+{
+    if ((texture == cache.bottom) && (texture == cache.top))
+    {
+        cache.top = NULL;
+        cache.bottom = NULL;
+    }
+    else if (texture == cache.bottom)
+    {
+        cache.bottom = texture->higher;
+
+        if (cache.bottom)
+            cache.bottom->lower = NULL;
+    }
+    else if (texture == cache.top)
+    {
+        cache.top = texture->lower;
+
+        if (cache.top)
+            cache.top->higher = NULL;
+    }
+    else
+    {
+        texture->higher->lower = texture->lower;
+        texture->lower->higher = texture->higher;
+    }
+
+#ifdef __HASHMAP_OPT
+    CachedTexture* tex= cache.hash.find(texture->crc);
+    if (tex == texture);
+        cache.hash.insert(texture->crc, NULL);
+#endif
+
+    glDeleteTextures( 1, &texture->glName );
+    cache.cachedBytes -= texture->textureBytes;
+    free( texture );
+
+    cache.numCached--;
+}
+
+CachedTexture *TextureCache_AddTop()
+{
+    while (cache.cachedBytes > TEXTURECACHE_MAX)
+    {
+        if (cache.bottom != cache.dummy)
+            TextureCache_RemoveBottom();
+        else if (cache.dummy->higher)
+            TextureCache_Remove( cache.dummy->higher );
+    }
+
+    CachedTexture *newtop = (CachedTexture*)malloc( sizeof( CachedTexture ) );
+
+    glGenTextures( 1, &newtop->glName );
+
+    newtop->lower = cache.top;
+    newtop->higher = NULL;
+
+    if (cache.top)
+        cache.top->higher = newtop;
+
+    if (!cache.bottom)
+        cache.bottom = newtop;
+
+    cache.top = newtop;
+
+    cache.numCached++;
+
+    return newtop;
+}
+
+void TextureCache_MoveToTop( CachedTexture *newtop )
+{
+    if (newtop == cache.top) return;
+
+    if (newtop == cache.bottom)
+    {
+        cache.bottom = newtop->higher;
+        cache.bottom->lower = NULL;
+    }
+    else
+    {
+        newtop->higher->lower = newtop->lower;
+        newtop->lower->higher = newtop->higher;
+    }
+
+    newtop->higher = NULL;
+    newtop->lower = cache.top;
+    cache.top->higher = newtop;
+    cache.top = newtop;
+}
+
+void TextureCache_Destroy()
+{
+    while (cache.bottom)
+        TextureCache_RemoveBottom();
+
+    glDeleteTextures( 32, cache.glNoiseNames );
+    glDeleteTextures( 1, &cache.dummy->glName  );
+
+#ifdef __HASHMAP_OPT
+    cache.hash.destroy();
+#endif
+
+    cache.top = NULL;
+    cache.bottom = NULL;
+}
+
+
+
+void TextureCache_LoadBackground( CachedTexture *texInfo )
+{
+    u32 *dest, *scaledDest;
+    u8 *swapped, *src;
+    u32 numBytes, bpl;
+    u32 x, y, j, tx, ty;
+    u16 clampSClamp,  clampTClamp;
+
+    int bytePerPixel=0;
+    TextureFormat   texFormat;
+    GetTexelFunc    getTexel;
+    GLint glWidth=0, glHeight=0;
+    GLenum glType=0;
+    GLenum glFormat=0;
+
+    __texture_format(texInfo->size, texInfo->format, &texFormat);
+
+#ifdef PRINT_TEXTUREFORMAT
+    printf("BG LUT=%i, TEXTURE SIZE=%i, FORMAT=%i -> GL FORMAT=%i\n", gDP.otherMode.textureLUT, texInfo->size, texInfo->format, texFormat.format); fflush(stdout);
+#endif
+
+    if (texFormat.format == FORMAT_NONE)
+    {
+        LOG(LOG_WARNING, "No Texture Conversion function available, size=%i format=%i\n", texInfo->size, texInfo->format);
+    }
+
+    switch(texFormat.format)
+    {
+        case FORMAT_I8:
+            glFormat = GL_LUMINANCE;
+            glType = GL_UNSIGNED_BYTE;
+            bytePerPixel = 1;
+            break;
+        case FORMAT_IA88:
+            glFormat = GL_LUMINANCE_ALPHA;
+            glType = GL_UNSIGNED_BYTE;
+            bytePerPixel = 2;
+            break;
+        case FORMAT_RGBA4444:
+            glFormat = GL_RGBA;
+            glType = GL_UNSIGNED_SHORT_4_4_4_4;
+            bytePerPixel = 2;
+            break;
+        case FORMAT_RGBA5551:
+            glFormat = GL_RGBA;
+            glType = GL_UNSIGNED_SHORT_5_5_5_1;
+            bytePerPixel = 2;
+            break;
+        case FORMAT_RGBA8888:
+            glFormat = GL_RGBA;
+            glType = GL_UNSIGNED_BYTE;
+            bytePerPixel = 4;
+            break;
+    }
+
+    glWidth = texInfo->realWidth;
+    glHeight = texInfo->realHeight;
+    texInfo->textureBytes = (glWidth * glHeight) * bytePerPixel;
+    getTexel = texFormat.getTexel;
+
+    bpl = gSP.bgImage.width << gSP.bgImage.size >> 1;
+    numBytes = bpl * gSP.bgImage.height;
+    swapped = (u8*) malloc(numBytes);
+    dest = (u32*) malloc(texInfo->textureBytes);
+
+    if (!dest || !swapped)
+    {
+        LOG(LOG_ERROR, "Malloc failed!\n");
+        return;
+    }
+
+    UnswapCopy(&RDRAM[gSP.bgImage.address], swapped, numBytes);
+
+    clampSClamp = texInfo->width - 1;
+    clampTClamp = texInfo->height - 1;
+
+    j = 0;
+    for (y = 0; y < texInfo->realHeight; y++)
+    {
+        ty = min(y, clampTClamp);
+        src = &swapped[bpl * ty];
+        for (x = 0; x < texInfo->realWidth; x++)
+        {
+            tx = min(x, clampSClamp);
+            if (bytePerPixel == 4)
+                ((u32*)dest)[j++] = getTexel(src, tx, 0, texInfo->palette);
+            else if (bytePerPixel == 2)
+                ((u16*)dest)[j++] = getTexel(src, tx, 0, texInfo->palette);
+            else if (bytePerPixel == 1)
+                ((u8*)dest)[j++] = getTexel(src, tx, 0, texInfo->palette);
+        }
+    }
+
+    if (!config.texture.sai2x || (texFormat.format == FORMAT_I8 || texFormat.format == FORMAT_IA88))
+    {
+        glTexImage2D( GL_TEXTURE_2D, 0, glFormat, glWidth, glHeight, 0, glFormat, glType, dest);
+    }
+    else
+    {
+        LOG(LOG_VERBOSE, "Using 2xSAI Filter on Texture\n");
+        texInfo->textureBytes <<= 2;
+
+        scaledDest = (u32*) malloc( texInfo->textureBytes );
+
+        if (glType == GL_UNSIGNED_BYTE)
+            _2xSaI8888( (u32*)dest, (u32*)scaledDest, texInfo->realWidth, texInfo->realHeight, texInfo->clampS, texInfo->clampT );
+        if (glType == GL_UNSIGNED_SHORT_4_4_4_4)
+            _2xSaI4444( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, texInfo->clampS, texInfo->clampT );
+        else
+            _2xSaI5551( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, texInfo->clampS, texInfo->clampT );
+
+        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texInfo->realWidth << 1, texInfo->realHeight << 1, 0, GL_RGBA, glType, scaledDest );
+
+        free( scaledDest );
+    }
+
+    free(dest);
+    free(swapped);
+
+
+    if (config.texture.enableMipmap)
+        glGenerateMipmap(GL_TEXTURE_2D);
+}
+
+void TextureCache_Load( CachedTexture *texInfo )
+{
+    u32 *dest, *scaledDest;
+
+    void *src;
+    u16 x, y, i, j, tx, ty, line;
+    u16 mirrorSBit, maskSMask, clampSClamp;
+    u16 mirrorTBit, maskTMask, clampTClamp;
+
+    int bytePerPixel=0;
+    TextureFormat   texFormat;
+    GetTexelFunc    getTexel;
+    GLint glWidth=0, glHeight=0;
+    GLenum glType=0;
+    GLenum glFormat=0;
+
+    __texture_format(texInfo->size, texInfo->format, &texFormat);
+
+#ifdef PRINT_TEXTUREFORMAT
+    printf("TEX LUT=%i, TEXTURE SIZE=%i, FORMAT=%i -> GL FORMAT=%i\n", gDP.otherMode.textureLUT, texInfo->size, texInfo->format, texFormat.format); fflush(stdout);
+#endif
+
+    if (texFormat.format == FORMAT_NONE)
+    {
+        LOG(LOG_WARNING, "No Texture Conversion function available, size=%i format=%i\n", texInfo->size, texInfo->format);
+    }
+
+    switch(texFormat.format)
+    {
+        case FORMAT_I8:
+            glFormat = GL_LUMINANCE;
+            glType = GL_UNSIGNED_BYTE;
+            bytePerPixel = 1;
+            break;
+        case FORMAT_IA88:
+            glFormat = GL_LUMINANCE_ALPHA;
+            glType = GL_UNSIGNED_BYTE;
+            bytePerPixel = 2;
+            break;
+        case FORMAT_RGBA4444:
+            glFormat = GL_RGBA;
+            glType = GL_UNSIGNED_SHORT_4_4_4_4;
+            bytePerPixel = 2;
+            break;
+        case FORMAT_RGBA5551:
+            glFormat = GL_RGBA;
+            glType = GL_UNSIGNED_SHORT_5_5_5_1;
+            bytePerPixel = 2;
+            break;
+        case FORMAT_RGBA8888:
+            glFormat = GL_RGBA;
+            glType = GL_UNSIGNED_BYTE;
+            bytePerPixel = 4;
+            break;
+    }
+
+    glWidth = texInfo->realWidth;
+    glHeight = texInfo->realHeight;
+    texInfo->textureBytes = (glWidth * glHeight) * bytePerPixel;
+    getTexel = texFormat.getTexel;
+
+    dest = (u32*)malloc(texInfo->textureBytes);
+
+    if (!dest)
+    {
+        LOG(LOG_ERROR, "Malloc failed!\n");
+        return;
+    }
+
+
+    line = texInfo->line;
+
+    if (texInfo->size == G_IM_SIZ_32b)
+        line <<= 1;
+
+    if (texInfo->maskS)
+    {
+        clampSClamp = texInfo->clampS ? texInfo->clampWidth - 1 : (texInfo->mirrorS ? (texInfo->width << 1) - 1 : texInfo->width - 1);
+        maskSMask = (1 << texInfo->maskS) - 1;
+        mirrorSBit = texInfo->mirrorS ? (1 << texInfo->maskS) : 0;
+    }
+    else
+    {
+        clampSClamp = min( texInfo->clampWidth, texInfo->width ) - 1;
+        maskSMask = 0xFFFF;
+        mirrorSBit = 0x0000;
+    }
+
+    if (texInfo->maskT)
+    {
+        clampTClamp = texInfo->clampT ? texInfo->clampHeight - 1 : (texInfo->mirrorT ? (texInfo->height << 1) - 1: texInfo->height - 1);
+        maskTMask = (1 << texInfo->maskT) - 1;
+        mirrorTBit = texInfo->mirrorT ? (1 << texInfo->maskT) : 0;
+    }
+    else
+    {
+        clampTClamp = min( texInfo->clampHeight, texInfo->height ) - 1;
+        maskTMask = 0xFFFF;
+        mirrorTBit = 0x0000;
+    }
+
+    // Hack for Zelda warp texture
+    if (((texInfo->tMem << 3) + (texInfo->width * texInfo->height << texInfo->size >> 1)) > 4096)
+    {
+        texInfo->tMem = 0;
+    }
+
+    // limit clamp values to min-0 (Perfect Dark has height=0 textures, making negative clamps)
+    if (clampTClamp & 0x8000) clampTClamp = 0;
+    if (clampSClamp & 0x8000) clampSClamp = 0;
+
+    j = 0;
+    for (y = 0; y < texInfo->realHeight; y++)
+    {
+        ty = min(y, clampTClamp) & maskTMask;
+        if (y & mirrorTBit) ty ^= maskTMask;
+        src = &TMEM[(texInfo->tMem + line * ty) & 511];
+        i = (ty & 1) << 1;
+        for (x = 0; x < texInfo->realWidth; x++)
+        {
+            tx = min(x, clampSClamp) & maskSMask;
+
+            if (x & mirrorSBit) tx ^= maskSMask;
+
+            if (bytePerPixel == 4)
+            {
+                ((u32*)dest)[j] = getTexel(src, tx, i, texInfo->palette);
+            }
+            else if (bytePerPixel == 2)
+            {
+                ((u16*)dest)[j] = getTexel(src, tx, i, texInfo->palette);
+            }
+            else if (bytePerPixel == 1)
+            {
+                ((u8*)dest)[j] = getTexel(src, tx, i, texInfo->palette);
+            }
+            j++;
+        }
+    }
+
+    if (!config.texture.sai2x || (texFormat.format == FORMAT_I8) || (texFormat.format == FORMAT_IA88))
+    {
+#ifdef PRINT_TEXTUREFORMAT
+        printf("j=%u DEST=0x%x SIZE=%i F=0x%x, W=%i, H=%i, T=0x%x\n", j, dest, texInfo->textureBytes,glFormat, glWidth, glHeight, glType); fflush(stdout);
+#endif
+        glTexImage2D( GL_TEXTURE_2D, 0, glFormat, glWidth, glHeight, 0, glFormat, glType, dest);
+    }
+    else
+    {
+        LOG(LOG_VERBOSE, "Using 2xSAI Filter on Texture\n");
+
+        texInfo->textureBytes <<= 2;
+
+        scaledDest = (u32*)malloc( texInfo->textureBytes );
+
+        if (glType == GL_UNSIGNED_BYTE)
+            _2xSaI8888( (u32*)dest, (u32*)scaledDest, texInfo->realWidth, texInfo->realHeight, 1, 1 );
+        else if (glType == GL_UNSIGNED_SHORT_4_4_4_4)
+            _2xSaI4444( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, 1, 1 );
+        else
+            _2xSaI5551( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, 1, 1 );
+
+        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texInfo->realWidth << 1, texInfo->realHeight << 1, 0, GL_RGBA, glType, scaledDest );
+
+        free( scaledDest );
+    }
+
+    free(dest);
+
+    if (config.texture.enableMipmap)
+        glGenerateMipmap(GL_TEXTURE_2D);
+
+}
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+u32 TextureCache_CalculateCRC( u32 t, u32 width, u32 height )
+{
+    u32 crc;
+    u32 y, /*i,*/ bpl, lineBytes, line;
+    void *src;
+
+    bpl = width << gSP.textureTile[t]->size >> 1;
+    lineBytes = gSP.textureTile[t]->line << 3;
+
+    line = gSP.textureTile[t]->line;
+    if (gSP.textureTile[t]->size == G_IM_SIZ_32b)
+        line <<= 1;
+
+    crc = 0xFFFFFFFF;
+
+#ifdef __CRC_OPT
+    unsigned n = (config.texture.fastCRC) ? max(1, height / 8) : 1;
+#else
+    unsigned n = 1;
+#endif
+
+    for (y = 0; y < height; y += n)
+    {
+        src = (void*) &TMEM[(gSP.textureTile[t]->tmem + (y * line)) & 511];
+        crc = CRC_Calculate( crc, src, bpl );
+    }
+
+    if (gSP.textureTile[t]->format == G_IM_FMT_CI)
+    {
+        if (gSP.textureTile[t]->size == G_IM_SIZ_4b)
+            crc = CRC_Calculate( crc, &gDP.paletteCRC16[gSP.textureTile[t]->palette], 4 );
+        else if (gSP.textureTile[t]->size == G_IM_SIZ_8b)
+            crc = CRC_Calculate( crc, &gDP.paletteCRC256, 4 );
+    }
+    return crc;
+}
+
+void TextureCache_ActivateTexture( u32 t, CachedTexture *texture )
+{
+
+#ifdef __HASHMAP_OPT
+    cache.hash.insert(texture->crc, texture);
+#endif
+
+    glActiveTexture( GL_TEXTURE0 + t );
+    glBindTexture( GL_TEXTURE_2D, texture->glName );
+
+    // Set filter mode. Almost always bilinear, but check anyways
+    if ((gDP.otherMode.textureFilter == G_TF_BILERP) || (gDP.otherMode.textureFilter == G_TF_AVERAGE) || (config.texture.forceBilinear))
+    {
+        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+    }
+    else
+    {
+        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+    }
+
+    // Set clamping modes
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (texture->clampS) ? GL_CLAMP_TO_EDGE : GL_REPEAT );
+    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (texture->clampT) ? GL_CLAMP_TO_EDGE : GL_REPEAT );
+
+    if (config.texture.maxAnisotropy > 0)
+    {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, config.texture.maxAnisotropy);
+    }
+
+    texture->lastDList = RSP.DList;
+    TextureCache_MoveToTop( texture );
+    cache.current[t] = texture;
+}
+
+void TextureCache_ActivateDummy( u32 t)
+{
+    glActiveTexture(GL_TEXTURE0 + t);
+    glBindTexture(GL_TEXTURE_2D, cache.dummy->glName );
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+}
+
+int _background_compare(CachedTexture *current, u32 crc)
+{
+    if ((current != NULL) &&
+        (current->crc == crc) &&
+        (current->width == gSP.bgImage.width) &&
+        (current->height == gSP.bgImage.height) &&
+        (current->format == gSP.bgImage.format) &&
+        (current->size == gSP.bgImage.size))
+        return 1;
+    else
+        return 0;
+}
+
+void TextureCache_UpdateBackground()
+{
+    u32 numBytes = gSP.bgImage.width * gSP.bgImage.height << gSP.bgImage.size >> 1;
+    u32 crc;
+
+    crc = CRC_Calculate( 0xFFFFFFFF, &RDRAM[gSP.bgImage.address], numBytes );
+
+    if (gSP.bgImage.format == G_IM_FMT_CI)
+    {
+        if (gSP.bgImage.size == G_IM_SIZ_4b)
+            crc = CRC_Calculate( crc, &gDP.paletteCRC16[gSP.bgImage.palette], 4 );
+        else if (gSP.bgImage.size == G_IM_SIZ_8b)
+            crc = CRC_Calculate( crc, &gDP.paletteCRC256, 4 );
+    }
+
+    //before we traverse cache, check to see if texture is already bound:
+    if (_background_compare(cache.current[0], crc))
+    {
+        return;
+    }
+
+#ifdef __HASHMAP_OPT
+    CachedTexture *tex = cache.hash.find(crc);
+    if (tex)
+    {
+        if (_background_compare(tex, crc))
+        {
+            TextureCache_ActivateTexture(0, tex);
+            cache.hits++;
+            return;
+        }
+    }
+#endif
+
+    CachedTexture *current = cache.top;
+    while (current)
+    {
+        if (_background_compare(current, crc))
+        {
+            TextureCache_ActivateTexture( 0, current );
+            cache.hits++;
+            return;
+        }
+        current = current->lower;
+    }
+    cache.misses++;
+
+    glActiveTexture(GL_TEXTURE0);
+    cache.current[0] = TextureCache_AddTop();
+
+    glBindTexture( GL_TEXTURE_2D, cache.current[0]->glName );
+    cache.current[0]->address = gSP.bgImage.address;
+    cache.current[0]->crc = crc;
+    cache.current[0]->format = gSP.bgImage.format;
+    cache.current[0]->size = gSP.bgImage.size;
+    cache.current[0]->width = gSP.bgImage.width;
+    cache.current[0]->height = gSP.bgImage.height;
+    cache.current[0]->clampWidth = gSP.bgImage.width;
+    cache.current[0]->clampHeight = gSP.bgImage.height;
+    cache.current[0]->palette = gSP.bgImage.palette;
+    cache.current[0]->maskS = 0;
+    cache.current[0]->maskT = 0;
+    cache.current[0]->mirrorS = 0;
+    cache.current[0]->mirrorT = 0;
+    cache.current[0]->clampS = 1;
+    cache.current[0]->clampT = 1;
+    cache.current[0]->line = 0;
+    cache.current[0]->tMem = 0;
+    cache.current[0]->lastDList = RSP.DList;
+
+    cache.current[0]->realWidth = (config.texture.pow2) ? pow2(gSP.bgImage.width ) : gSP.bgImage.width;
+    cache.current[0]->realHeight = (config.texture.pow2) ? pow2(gSP.bgImage.height) : gSP.bgImage.height;
+
+    cache.current[0]->scaleS = 1.0f / (f32)(cache.current[0]->realWidth);
+    cache.current[0]->scaleT = 1.0f / (f32)(cache.current[0]->realHeight);
+    cache.current[0]->shiftScaleS = 1.0f;
+    cache.current[0]->shiftScaleT = 1.0f;
+
+    TextureCache_LoadBackground( cache.current[0] );
+    TextureCache_ActivateTexture( 0, cache.current[0] );
+
+    cache.cachedBytes += cache.current[0]->textureBytes;
+}
+
+int _texture_compare(u32 t, CachedTexture *current, u32 crc,  u32 width, u32 height, u32 clampWidth, u32 clampHeight)
+{
+    if  ((current != NULL) &&
+        (current->crc == crc) &&
+        (current->width == width) &&
+        (current->height == height) &&
+        (current->clampWidth == clampWidth) &&
+        (current->clampHeight == clampHeight) &&
+        (current->maskS == gSP.textureTile[t]->masks) &&
+        (current->maskT == gSP.textureTile[t]->maskt) &&
+        (current->mirrorS == gSP.textureTile[t]->mirrors) &&
+        (current->mirrorT == gSP.textureTile[t]->mirrort) &&
+        (current->clampS == gSP.textureTile[t]->clamps) &&
+        (current->clampT == gSP.textureTile[t]->clampt) &&
+        (current->format == gSP.textureTile[t]->format) &&
+        (current->size == gSP.textureTile[t]->size))
+        return 1;
+    else
+        return 0;
+}
+
+
+void TextureCache_Update( u32 t )
+{
+    CachedTexture *current;
+
+    u32 crc, maxTexels;
+    u32 tileWidth, maskWidth, loadWidth, lineWidth, clampWidth, height;
+    u32 tileHeight, maskHeight, loadHeight, lineHeight, clampHeight, width;
+
+    if (gDP.textureMode == TEXTUREMODE_BGIMAGE)
+    {
+        TextureCache_UpdateBackground();
+        return;
+    }
+
+    TextureFormat texFormat;
+    __texture_format(gSP.textureTile[t]->size, gSP.textureTile[t]->format, &texFormat);
+
+    maxTexels = texFormat.maxTexels;
+
+    // Here comes a bunch of code that just calculates the texture size...I wish there was an easier way...
+    tileWidth = gSP.textureTile[t]->lrs - gSP.textureTile[t]->uls + 1;
+    tileHeight = gSP.textureTile[t]->lrt - gSP.textureTile[t]->ult + 1;
+
+    maskWidth = 1 << gSP.textureTile[t]->masks;
+    maskHeight = 1 << gSP.textureTile[t]->maskt;
+
+    loadWidth = gDP.loadTile->lrs - gDP.loadTile->uls + 1;
+    loadHeight = gDP.loadTile->lrt - gDP.loadTile->ult + 1;
+
+    lineWidth = gSP.textureTile[t]->line << texFormat.lineShift;
+
+    if (lineWidth) // Don't allow division by zero
+        lineHeight = min( maxTexels / lineWidth, tileHeight );
+    else
+        lineHeight = 0;
+
+    if (gDP.textureMode == TEXTUREMODE_TEXRECT)
+    {
+        u32 texRectWidth = gDP.texRect.width - gSP.textureTile[t]->uls;
+        u32 texRectHeight = gDP.texRect.height - gSP.textureTile[t]->ult;
+
+        if (gSP.textureTile[t]->masks && ((maskWidth * maskHeight) <= maxTexels))
+            width = maskWidth;
+        else if ((tileWidth * tileHeight) <= maxTexels)
+            width = tileWidth;
+        else if ((tileWidth * texRectHeight) <= maxTexels)
+            width = tileWidth;
+        else if ((texRectWidth * tileHeight) <= maxTexels)
+            width = gDP.texRect.width;
+        else if ((texRectWidth * texRectHeight) <= maxTexels)
+            width = gDP.texRect.width;
+        else if (gDP.loadType == LOADTYPE_TILE)
+            width = loadWidth;
+        else
+            width = lineWidth;
+
+        if (gSP.textureTile[t]->maskt && ((maskWidth * maskHeight) <= maxTexels))
+            height = maskHeight;
+        else if ((tileWidth * tileHeight) <= maxTexels)
+            height = tileHeight;
+        else if ((tileWidth * texRectHeight) <= maxTexels)
+            height = gDP.texRect.height;
+        else if ((texRectWidth * tileHeight) <= maxTexels)
+            height = tileHeight;
+        else if ((texRectWidth * texRectHeight) <= maxTexels)
+            height = gDP.texRect.height;
+        else if (gDP.loadType == LOADTYPE_TILE)
+            height = loadHeight;
+        else
+            height = lineHeight;
+    }
+    else
+    {
+        if (gSP.textureTile[t]->masks && ((maskWidth * maskHeight) <= maxTexels))
+            width = maskWidth;
+        else if ((tileWidth * tileHeight) <= maxTexels)
+            width = tileWidth;
+        else if (gDP.loadType == LOADTYPE_TILE)
+            width = loadWidth;
+        else
+            width = lineWidth;
+
+        if (gSP.textureTile[t]->maskt && ((maskWidth * maskHeight) <= maxTexels))
+            height = maskHeight;
+        else if ((tileWidth * tileHeight) <= maxTexels)
+            height = tileHeight;
+        else if (gDP.loadType == LOADTYPE_TILE)
+            height = loadHeight;
+        else
+            height = lineHeight;
+    }
+
+    clampWidth = gSP.textureTile[t]->clamps ? tileWidth : width;
+    clampHeight = gSP.textureTile[t]->clampt ? tileHeight : height;
+
+    if (clampWidth > 256)
+        gSP.textureTile[t]->clamps = 0;
+    if (clampHeight > 256)
+        gSP.textureTile[t]->clampt = 0;
+
+    // Make sure masking is valid
+    if (maskWidth > width)
+    {
+        gSP.textureTile[t]->masks = powof( width );
+        maskWidth = 1 << gSP.textureTile[t]->masks;
+    }
+
+    if (maskHeight > height)
+    {
+        gSP.textureTile[t]->maskt = powof( height );
+        maskHeight = 1 << gSP.textureTile[t]->maskt;
+    }
+
+    crc = TextureCache_CalculateCRC( t, width, height );
+
+    //before we traverse cache, check to see if texture is already bound:
+    if (_texture_compare(t, cache.current[t], crc, width, height, clampWidth, clampHeight))
+    {
+        cache.hits++;
+        return;
+    }
+
+#ifdef __HASHMAP_OPT
+    CachedTexture *tex = cache.hash.find(crc);
+    if (tex)
+    {
+        if (_texture_compare(t, tex, crc, width, height, clampWidth, clampHeight))
+        {
+            TextureCache_ActivateTexture( t, tex);
+            cache.hits++;
+            return;
+        }
+    }
+#endif
+
+    current = cache.top;
+    while (current)
+    {
+        if  (_texture_compare(t, current, crc, width, height, clampWidth, clampHeight))
+        {
+            TextureCache_ActivateTexture( t, current );
+            cache.hits++;
+            return;
+        }
+
+        current = current->lower;
+    }
+
+    cache.misses++;
+
+    glActiveTexture( GL_TEXTURE0 + t);
+
+    cache.current[t] = TextureCache_AddTop();
+
+    if (cache.current[t] == NULL)
+    {
+        LOG(LOG_ERROR, "Texture Cache Failure\n");
+    }
+
+    glBindTexture( GL_TEXTURE_2D, cache.current[t]->glName );
+
+    cache.current[t]->address = gDP.textureImage.address;
+    cache.current[t]->crc = crc;
+
+    cache.current[t]->format = gSP.textureTile[t]->format;
+    cache.current[t]->size = gSP.textureTile[t]->size;
+
+    cache.current[t]->width = width;
+    cache.current[t]->height = height;
+    cache.current[t]->clampWidth = clampWidth;
+    cache.current[t]->clampHeight = clampHeight;
+    cache.current[t]->palette = gSP.textureTile[t]->palette;
+    cache.current[t]->maskS = gSP.textureTile[t]->masks;
+    cache.current[t]->maskT = gSP.textureTile[t]->maskt;
+    cache.current[t]->mirrorS = gSP.textureTile[t]->mirrors;
+    cache.current[t]->mirrorT = gSP.textureTile[t]->mirrort;
+    cache.current[t]->clampS = gSP.textureTile[t]->clamps;
+    cache.current[t]->clampT = gSP.textureTile[t]->clampt;
+    cache.current[t]->line = gSP.textureTile[t]->line;
+    cache.current[t]->tMem = gSP.textureTile[t]->tmem;
+    cache.current[t]->lastDList = RSP.DList;
+
+
+    if (cache.current[t]->clampS)
+        cache.current[t]->realWidth = (config.texture.pow2) ? pow2(clampWidth) : clampWidth;
+    else if (cache.current[t]->mirrorS)
+        cache.current[t]->realWidth = maskWidth << 1;
+    else
+        cache.current[t]->realWidth = (config.texture.pow2) ? pow2(width) : width;
+
+    if (cache.current[t]->clampT)
+        cache.current[t]->realHeight = (config.texture.pow2) ? pow2(clampHeight) : clampHeight;
+    else if (cache.current[t]->mirrorT)
+        cache.current[t]->realHeight = maskHeight << 1;
+    else
+        cache.current[t]->realHeight = (config.texture.pow2) ? pow2(height) : height;
+
+
+    cache.current[t]->scaleS = 1.0f / (f32)(cache.current[t]->realWidth);
+    cache.current[t]->scaleT = 1.0f / (f32)(cache.current[t]->realHeight);
+
+    // Hack for Zelda Sun
+    if ((config.hackZelda) && (gDP.combine.mux == 0x00262a60150c937fLL))
+    {
+        if ((cache.current[t]->format = G_IM_FMT_I) && (cache.current[t]->size == G_IM_SIZ_8b) &&
+            (cache.current[t]->width == 64))
+        {
+            cache.current[t]->scaleS *= 0.5f;
+            cache.current[t]->scaleT *= 0.5f;
+        }
+    }
+
+    cache.current[t]->shiftScaleS = 1.0f;
+    cache.current[t]->shiftScaleT = 1.0f;
+
+    cache.current[t]->offsetS = config.texture.sai2x ? 0.25f : 0.5f;
+    cache.current[t]->offsetT = config.texture.sai2x ? 0.25f : 0.5f;
+
+    if (gSP.textureTile[t]->shifts > 10)
+        cache.current[t]->shiftScaleS = (f32)(1 << (16 - gSP.textureTile[t]->shifts));
+    else if (gSP.textureTile[t]->shifts > 0)
+        cache.current[t]->shiftScaleS /= (f32)(1 << gSP.textureTile[t]->shifts);
+
+    if (gSP.textureTile[t]->shiftt > 10)
+        cache.current[t]->shiftScaleT = (f32)(1 << (16 - gSP.textureTile[t]->shiftt));
+    else if (gSP.textureTile[t]->shiftt > 0)
+        cache.current[t]->shiftScaleT /= (f32)(1 << gSP.textureTile[t]->shiftt);
+
+    TextureCache_Load( cache.current[t] );
+    TextureCache_ActivateTexture( t, cache.current[t] );
+
+    cache.cachedBytes += cache.current[t]->textureBytes;
+}
+
+void TextureCache_ActivateNoise(u32 t)
+{
+    glActiveTexture(GL_TEXTURE0 + t);
+    glBindTexture(GL_TEXTURE_2D, cache.glNoiseNames[RSP.DList & 0x1F]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
+}
+