/*
Copyright (C) 2002 Rice1964

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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// Texture related ucode

#include <stdlib.h>
#include <algorithm>

#include "Render.h"

#undef min
#undef max

uint32 g_TmemFlag[16];
void SetTmemFlag(uint32 tmemAddr, uint32 size);
bool IsTmemFlagValid(uint32 tmemAddr);
uint32 GetValidTmemInfoIndex(uint32 tmemAddr);

void EnhanceTexture(TxtrCacheEntry *pEntry);
void LoadHiresTexture( TxtrCacheEntry &entry );


extern TMEMLoadMapInfo g_tmemInfo0;             // Info for Tmem=0
extern TMEMLoadMapInfo g_tmemInfo1;             // Info for Tmem=0x100

TmemType g_Tmem;

/************************************************************************/
/*                                                                      */
/************************************************************************/
uint32 sizeShift[4] = {2,1,0,0};
uint32 sizeIncr[4] = {3,1,0,0};
uint32 sizeBytes[4] = {0,1,2,4};

inline uint32 Txl2Words(uint32 width, uint32 size)
{
    if( size == TXT_SIZE_4b )
        return std::max(1u, width/16);
    else
        return std::max(1u, width*sizeBytes[size]/8);
}

inline uint32 CalculateImgSize(uint32 width, uint32 height, uint32 size)
{
    //(((width)*(height) + siz##_INCR) >> siz##_SHIFT) -1
    return (((width)*(height) + sizeIncr[size]) >> sizeShift[size]) -1;
}


inline uint32 CalculateDXT(uint32 txl2words)
{
    //#define CALC_DXT(width, b_txl)    ((2048 + TXL2WORDS(width, b_txl) - 1) / TXL2WORDS(width, b_txl))
    if( txl2words == 0 ) return 1;
    else return (2048+txl2words-1)/txl2words;
}

inline uint32 ReverseDXT(uint32 val, uint32 lrs, uint32 width, uint32 size)
{
    //#define TXL2WORDS(txls, b_txl)    MAX(1, ((txls)*(b_txl)/8))
    if( val == 0x800 ) return 1;
    
    unsigned int low = 2047/val;
    if( CalculateDXT(low) > val )   low++;
    unsigned int high = 2047/(val-1);

    if( low == high )   return low;

    for( unsigned int i=low; i<=high; i++ )
    {
        if( Txl2Words(width, size) == i )
            return i;
    }

    return  (low+high)/2;   //dxt = 2047 / (dxt-1);
}

// The following inline assemble routines are borrowed from glN64, I am too tired to
// rewrite these routine by myself.
// Rice, 02/24/2004

inline void UnswapCopy( void *src, void *dest, uint32 numBytes )
{
#if !defined(__GNUC__) && !defined(NO_ASM)
    __asm
    {
        mov     ecx, 0
        mov     esi, dword ptr [src]
        mov     edi, dword ptr [dest]

        mov     ebx, esi
        and     ebx, 3          // ebx = number of leading bytes

        cmp     ebx, 0
        jz      StartDWordLoop
        neg     ebx
        add     ebx, 4

        cmp     ebx, [numBytes]
        jle     NotGreater
        mov     ebx, [numBytes]
NotGreater:
        mov     ecx, ebx
            xor     esi, 3
LeadingLoop:                // Copies leading bytes, in reverse order (un-swaps)
        mov     al, byte ptr [esi]
        mov     byte ptr [edi], al
        sub     esi, 1
        add     edi, 1
        loop    LeadingLoop
        add     esi, 5

StartDWordLoop:
        mov     ecx, dword ptr [numBytes]
        sub     ecx, ebx        // Don't copy what's already been copied

        mov     ebx, ecx
        and     ebx, 3
        //      add     ecx, 3          // Round up to nearest dword
        shr     ecx, 2

        cmp     ecx, 0          // If there's nothing to do, don't do it
        jle     StartTrailingLoop

        // Copies from source to destination, bswap-ing first
DWordLoop:
        mov     eax, dword ptr [esi]
        bswap   eax
        mov     dword ptr [edi], eax
        add     esi, 4
        add     edi, 4
        loop    DWordLoop
StartTrailingLoop:
        cmp     ebx, 0
        jz      Done
        mov     ecx, ebx
        xor     esi, 3

TrailingLoop:
        mov     al, byte ptr [esi]
        mov     byte ptr [edi], al
        sub     esi, 1
        add     edi, 1
        loop    TrailingLoop
Done:
    }
#elif defined(__GNUC__) && defined(__x86_64__) && !defined(NO_ASM)
  asm volatile(" movl       %k1,   %%ebx      \n"
               " andl        $3,   %%ebx      \n"
               " cmpl        $0,   %%ebx      \n"
               " jz          2f               \n"
               " negl     %%ebx               \n"
               " addl        $4,   %%ebx      \n"
               " cmpl       %k2,   %%ebx      \n"
               " jle         0f               \n"
               " movl       %k2,   %%ebx      \n"
               "0:                            \n"
               " movl     %%ebx,   %%ecx      \n"
               " xor         $3,      %1      \n"
               "1:                            \n"
               " movb      (%1),    %%al      \n"
               " movb      %%al,    (%0)      \n"
               " sub         $1,      %1      \n"
               " add         $1,      %0      \n"
               " decl     %%ecx               \n"
               " jne         1b               \n"
               " add         $5,      %1      \n"
               "2:                            \n"
               " movl       %k2,   %%ecx      \n"
               " subl     %%ebx,   %%ecx      \n"
               " movl     %%ecx,   %%ebx      \n"
               " andl       $3,    %%ebx      \n"
               " shrl       $2,    %%ecx      \n"
               " cmpl       $0,    %%ecx      \n"
               " jle        4f                \n"
               "3:                            \n"
               " movl     (%1),    %%eax      \n"
               " bswapl  %%eax                \n"
               " movl    %%eax,     (%0)      \n"
               " add        $4,       %1      \n"
               " add        $4,       %0      \n"
               " decl    %%ecx                \n"
               " jne        3b                \n"
               "4:                            \n"
               " cmpl       $0,    %%ebx      \n"
               " jz         6f                \n"
               " xor        $3,       %1      \n"
               "5:                            \n"
               " movb     (%1),     %%al      \n"
               " movb     %%al,     (%0)      \n"
               " sub        $1,       %1      \n"
               " add        $1,       %0      \n"
               " decl    %%ebx                \n"
               " jne        5b                \n"
               "6:                            \n"
               :"+r"(dest), "+r"(src)
               :"r"(numBytes)
               : "memory", "cc", "%rax", "%rbx", "%rcx"
               );

#elif !defined(NO_ASM)
   unsigned int saveEBX;
   asm volatile ("mov           %%ebx, %2         \n"
         "mov       $0, %%ecx         \n"
         "mov       %0, %%esi         \n"
         "mov       %1, %%edi         \n"
         
         "mov       %%esi, %%ebx      \n"
         "and       $3, %%ebx         \n"           // ebx = number of leading bytes
         
         "cmp       $0, %%ebx         \n"
         "jz            2f                \n" //jz      StartDWordLoop
         "neg       %%ebx             \n"
         "add       $4, %%ebx         \n"
         
         "cmp       %3, %%ebx         \n"
         "jle           0f                \n" //jle     NotGreater
         "mov       %3, %%ebx         \n"
         "0:                              \n" //NotGreater:
         "mov       %%ebx, %%ecx      \n"
         "xor       $3, %%esi         \n"
         "1:                              \n" //LeadingLoop:                // Copies leading bytes, in reverse order (un-swaps)
         "mov       (%%esi), %%al     \n"
         "mov       %%al, (%%edi)     \n"
         "sub       $1, %%esi         \n"
         "add       $1, %%edi         \n"
         "loop          1b                \n" //loop     LeadingLoop
         "add       $5, %%esi         \n"
         
         "2:                              \n" //StartDWordLoop:
         "mov       %3, %%ecx         \n"
         "sub       %%ebx, %%ecx      \n"       // Don't copy what's already been copied
         
         "mov       %%ecx, %%ebx      \n"
         "and       $3, %%ebx         \n"
         //     add     ecx, 3          // Round up to nearest dword
         "shr       $2, %%ecx         \n"
         
         "cmp       $0, %%ecx         \n"           // If there's nothing to do, don't do it
         "jle           4f                \n" //jle     StartTrailingLoop
         
         // Copies from source to destination, bswap-ing first
         "3:                              \n" //DWordLoop:
         "mov       (%%esi), %%eax    \n"
         "bswap %%eax                     \n"
         "mov       %%eax, (%%edi)    \n"
         "add       $4, %%esi         \n"
         "add       $4, %%edi         \n"
         "loop          3b                \n" //loop    DWordLoop
         "4:                              \n" //StartTrailingLoop:
         "cmp       $0, %%ebx         \n"
         "jz            6f                \n" //jz      Done
         "mov       %%ebx, %%ecx      \n"
         "xor       $3, %%esi         \n"
         
         "5:                              \n" //TrailingLoop:
         "mov       (%%esi), %%al     \n"
         "mov       %%al, (%%edi)     \n"
         "sub       $1, %%esi         \n"
         "add       $1, %%edi         \n"
         "loop          5b                \n" //loop    TrailingLoop
         "6:                              \n" //Done:
         "mov           %2, %%ebx         \n"
         :
         : "m"(src), "m"(dest), "m"(saveEBX), "m"(numBytes)
         : "memory", "cc", "%ecx", "%esi", "%edi", "%eax"
         );
#endif
}

inline void DWordInterleave( void *mem, uint32 numDWords )
{
#if !defined(__GNUC__) && !defined(NO_ASM)
    __asm {
        mov     esi, dword ptr [mem]
        mov     edi, dword ptr [mem]
        add     edi, 4
        mov     ecx, dword ptr [numDWords]
DWordInterleaveLoop:
        mov     eax, dword ptr [esi]
        mov     ebx, dword ptr [edi]
        mov     dword ptr [esi], ebx
        mov     dword ptr [edi], eax
        add     esi, 8
        add     edi, 8
        loop    DWordInterleaveLoop
    }
#elif defined(__GNUC__) && defined(__x86_64__) && !defined(NO_ASM)
  asm volatile("0:                                 \n"
               " movl     (%0),     %%eax          \n"
               " movl    8(%0),     %%ebx          \n"
               " movl    %%eax,     8(%0)          \n"
               " movl    %%ebx,      (%0)          \n"
               " add        $8,        %0          \n"
               " decl      %k1                     \n"
               " jne        0b                     \n"
           : "+r"(mem), "+r"(numDWords)
           :
           : "memory", "cc", "%rax", "%rbx"
           );
#elif !defined(NO_ASM)
   unsigned int saveEBX;
   asm volatile ("mov           %%ebx, %2          \n"
         "mov       %0, %%esi          \n"
         "mov       %0, %%edi          \n"
         "add       $4, %%edi          \n"
         "mov       %1, %%ecx          \n"
         "0:                               \n" //DWordInterleaveLoop:
         "mov       (%%esi), %%eax     \n"
         "mov       (%%edi), %%ebx     \n"
         "mov       %%ebx, (%%esi)     \n"
         "mov       %%eax, (%%edi)     \n"
         "add       $8, %%esi          \n"
         "add       $8, %%edi          \n"
         "loop          0b                 \n" //loop   DWordInterleaveLoop
         "mov           %2, %%ebx          \n"
         :
         : "m"(mem), "m"(numDWords), "m"(saveEBX)
         : "memory", "cc", "%esi", "%edi", "%ecx", "%eax"
         );
#endif
}

inline void QWordInterleave( void *mem, uint32 numDWords )
{
#if !defined(__GNUC__) && !defined(NO_ASM)
    __asm
    {
        // Interleave the line on the qword
        mov     esi, dword ptr [mem]
        mov     edi, dword ptr [mem]
        add     edi, 8
        mov     ecx, dword ptr [numDWords]
        shr     ecx, 1
QWordInterleaveLoop:
        mov     eax, dword ptr [esi]
        mov     ebx, dword ptr [edi]
        mov     dword ptr [esi], ebx
        mov     dword ptr [edi], eax
        add     esi, 4
        add     edi, 4
        mov     eax, dword ptr [esi]
        mov     ebx, dword ptr [edi]
        mov     dword ptr [esi], ebx
        mov     dword ptr [edi], eax
        add     esi, 12
        add     edi, 12
        loop    QWordInterleaveLoop
    }
#elif defined(__GNUC__) && defined(__x86_64__) && !defined(NO_ASM)
  asm volatile(" shr        $1,       %k1          \n"
               "0:                                 \n"
               " mov      (%0),     %%rax          \n"
               " mov     8(%0),     %%rbx          \n"
               " mov     %%rax,     8(%0)          \n"
               " mov     %%rbx,      (%0)          \n"
               " add       $16,        %0          \n"
               " decl      %k1                     \n"
               " jne        0b                     \n"
           : "+r"(mem), "+r"(numDWords)
           :
           : "memory", "cc", "%rax", "%rbx"
           );
#elif !defined(NO_ASM) // GCC assumed

   unsigned int saveEBX;
   asm volatile("mov            %%ebx, %2          \n"
        // Interleave the line on the qword
        "mov        %0, %%esi          \n"
        "mov        %0, %%edi          \n"
        "add        $8, %%edi          \n"
        "mov        %1, %%ecx          \n"
        "shr        $1, %%ecx          \n"
        "0:                                \n" //QWordInterleaveLoop:
        "mov        (%%esi), %%eax     \n"
        "mov        (%%edi), %%ebx     \n"
        "mov        %%ebx, (%%esi)     \n"
        "mov        %%eax, (%%edi)     \n"
        "add        $4, %%esi          \n"
        "add        $4, %%edi          \n"
        "mov        (%%esi), %%eax     \n"
        "mov        (%%edi), %%ebx     \n"
        "mov        %%ebx, (%%esi)     \n"
        "mov        %%eax, (%%edi)     \n"
        "add        $12, %%esi         \n"
        "add        $12, %%edi         \n"
        "loop           0b                 \n" //loop   QWordInterleaveLoop
        "mov            %2, %%ebx          \n"
        :
        : "m"(mem), "m"(numDWords), "m"(saveEBX)
        : "memory", "cc", "%esi", "%edi", "%ecx", "%eax"
        );
#endif
}

inline uint32 swapdword( uint32 value )
{
#if defined(__INTEL_COMPILER) && !defined(NO_ASM)
    __asm
    {
        mov     eax, dword ptr [value]
        bswap   eax
    }
#elif defined(__GNUC__) && defined(__x86_64__) && !defined(NO_ASM)
  asm volatile(" bswapl %k0                    \n"
               : "+r"(value)
               :
               :
               );
  return value;
#elif defined(__GNUC__) && defined(__i386__) && !defined(NO_ASM)
  asm volatile("bswapl %0 \n"
               : "+r"(value)
               :
               :
               );
   return value;
#else
  return ((value & 0xff000000) >> 24) |
         ((value & 0x00ff0000) >>  8) |
         ((value & 0x0000ff00) <<  8) |
         ((value & 0x000000ff) << 24);
#endif
}

inline uint16 swapword( uint16 value )
{
#if defined(__INTEL_COMPILER) && !defined(NO_ASM)
    __asm
    {
        mov     ax, word ptr [value]
        xchg    ah, al
    }
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) && !defined(NO_ASM)
  asm volatile("xchg  %%al, %%ah    \n"
               : "+a"(value)
               :
               :
               );
  return value;
#else
  return ((value & 0xff00) >> 8) |
         ((value & 0x00ff) << 8);
#endif
}


void ComputeTileDimension(int mask, int clamp, int mirror, int width, uint32 &widthToCreate, uint32 &widthToLoad)
{
    int maskwidth = mask > 0 ? (1<<mask) : 0;
    widthToCreate = widthToLoad = width;

    if( mask > 0 )
    {
        if( width > maskwidth )
        {
            if( clamp == 0 )
            {
                // clamp is not used, so just use the dwTileMaskWidth as the real width
                widthToCreate = widthToLoad = maskwidth;
            }
            else
            {
                widthToLoad = maskwidth;
                //gti.WidthToCreate = dwTileWidth;
                // keep the current WidthToCreate, we will do mirror/wrap
                // during texture loading, not during rendering
            }
        }
        else if( width < maskwidth )
        {
            // dwTileWidth < dwTileMaskWidth

            if( clamp == 0 )
            {
                if( maskwidth%width == 0 )
                {
                    if( (maskwidth/width)%2 == 0 || mirror == 0 )
                    {
                        // Do nothing
                        // gti.WidthToLoad = gti.WidthToCreate = gRDP.tiles[tileno].dwWidth = dwTileWidth
                    }
                    else
                    {
                        widthToCreate = maskwidth;
                    }
                }
                else
                {
                    widthToCreate = maskwidth;
                    //widthToLoad = maskwidth;
                }
            }
            else
            {
                widthToCreate = maskwidth;
                //widthToLoad = maskwidth;
            }
        }
        else // dwTileWidth == dwTileMaskWidth
        {
        }

        // Some hacks, to limit the image size
        if( mask >= 8 )
        {
            if( maskwidth / width >= 2 )
            {
                widthToCreate = width;
            }
        }
    }
}

bool conkerSwapHack=false;

bool CalculateTileSizes_method_2(int tileno, TMEMLoadMapInfo *info, TxtrInfo &gti)
{
    Tile &tile = gRDP.tiles[tileno];
    Tile &loadtile = gRDP.tiles[RDP_TXT_LOADTILE];

    uint32 dwPitch;

    // Now Initialize the texture dimension
    int dwTileWidth;
    int dwTileHeight;
    if( info->bSetBy == CMD_LOADTILE )
    {
        if( tile.sl >= tile.sh )
        {
            dwTileWidth = info->dwWidth;    // From SetTImage
            dwTileWidth = dwTileWidth << info->dwSize >> tile.dwSize;
        }
        else
        {
            dwTileWidth= tile.sh - tile.sl + 1;
        }

        if( tile.tl >= tile.th )
        {
            dwTileHeight= info->th - info->tl + 1;
        }
        else
        {
            dwTileHeight= tile.th - tile.tl + 1;
        }
    }
    else
    {
        if( tile.dwMaskS == 0 || tile.bClampS )
        {
            dwTileWidth = tile.hilite_sh - tile.hilite_sl +1;
            if( dwTileWidth < tile.sh - tile.sl +1 )
                dwTileWidth = tile.sh - tile.sl +1;
            if( dwTileWidth <= 0 )
            {
                DebuggerAppendMsg("Error");
            }
        }
        else
        {
            if( tile.dwMaskS < 8 )
                dwTileWidth = (1 << tile.dwMaskS );
            else if( tile.dwLine )
            {
                dwTileWidth = (tile.dwLine<<5)>>tile.dwSize;
            }
            else
            {
                if( tile.sl <= tile.sh )
                {
                    dwTileWidth = tile.sh - tile.sl +1;
                }
                else if( loadtile.sl <= loadtile.sh )
                {
                    dwTileWidth = loadtile.sh - loadtile.sl +1;
                }
                else
                {
                    dwTileWidth = tile.sh - tile.sl +1;
                }
            }
        }

        if( tile.dwMaskT == 0 || tile.bClampT )
        {
            dwTileHeight= tile.hilite_th - tile.hilite_tl +1;
            if( dwTileHeight < tile.th - tile.tl +1 )
                dwTileHeight = tile.th - tile.tl +1;

            if( dwTileHeight <= 0 )
            {
                DebuggerAppendMsg("Error");
            }
        }
        else
        {
            if( tile.dwMaskT < 8 )
                dwTileHeight = (1 << tile.dwMaskT );
            else if( tile.tl <= tile.th )
            {
                dwTileHeight = tile.th - tile.tl +1;
            }
            else if( loadtile.tl <= loadtile.th )
            {
                dwTileHeight = loadtile.th - loadtile.tl +1;
            }
            else
            {
                dwTileHeight = tile.th - tile.tl +1;
            }
        }
    }

    int dwTileMaskWidth = tile.dwMaskS > 0 ? (1 << tile.dwMaskS ) : 0;
    int dwTileMaskHeight = tile.dwMaskT > 0 ? (1 << tile.dwMaskT ) : 0;

    if( dwTileWidth < 0 || dwTileHeight < 0)
    {
        if( dwTileMaskWidth > 0 )
            dwTileWidth = dwTileMaskWidth;
        else if( dwTileWidth < 0 )
            dwTileWidth = -dwTileWidth;

        if( dwTileMaskHeight > 0 )
            dwTileHeight = dwTileMaskHeight;
        else if( dwTileHeight < 0 )
            dwTileHeight = -dwTileHeight;
    }



    if( dwTileWidth-dwTileMaskWidth == 1 && dwTileMaskWidth && dwTileHeight-dwTileMaskHeight == 1 && dwTileMaskHeight )
    {
        // Hack for Mario Kart
        dwTileWidth--;
        dwTileHeight--;
    }

    ComputeTileDimension(tile.dwMaskS, tile.bClampS,
        tile.bMirrorS, dwTileWidth, gti.WidthToCreate, gti.WidthToLoad);
    tile.dwWidth = gti.WidthToCreate;

    ComputeTileDimension(tile.dwMaskT, tile.bClampT,
        tile.bMirrorT, dwTileHeight, gti.HeightToCreate, gti.HeightToLoad);
    tile.dwHeight = gti.HeightToCreate;

#ifdef DEBUGGER
    if( gti.WidthToCreate < gti.WidthToLoad )
        TRACE2("Check me, width to create = %d, width to load = %d", gti.WidthToCreate, gti.WidthToLoad);
    if( gti.HeightToCreate < gti.HeightToLoad )
        TRACE2("Check me, height to create = %d, height to load = %d", gti.HeightToCreate, gti.HeightToLoad);
#endif


    gti.bSwapped = info->bSwapped;

    if( info->bSetBy == CMD_LOADTILE )
    {
        // It was a tile - the pitch is set by LoadTile
        dwPitch = info->dwWidth<<(info->dwSize-1);

        if( dwPitch == 0 )
        {
            dwPitch = 1024;     // Hack for Bust-A-Move
        }
    }
    else    //Set by LoadBlock
    {
        // It was a block load - the pitch is determined by the tile size
        if (info->dxt == 0 || info->dwTmem != tile.dwTMem )
        {
            dwPitch = tile.dwLine << 3;
            gti.bSwapped = TRUE;
            if( info->dwTmem != tile.dwTMem && info->dxt != 0 && info->dwSize == TXT_SIZE_16b && tile.dwSize == TXT_SIZE_4b )
                conkerSwapHack = true;
        }
        else
        {
            uint32 DXT = info->dxt;
            if( info->dxt > 1 )
            {
                DXT = ReverseDXT(info->dxt, info->sh, dwTileWidth, tile.dwSize);
            }
            dwPitch = DXT << 3;
        }

        if (tile.dwSize == TXT_SIZE_32b)
            dwPitch = tile.dwLine << 4;
    }

    gti.Pitch = tile.dwPitch = dwPitch;

    if( (gti.WidthToLoad < gti.WidthToCreate || tile.bSizeIsValid == false) && tile.dwMaskS > 0 && gti.WidthToLoad != (unsigned int)dwTileMaskWidth &&
        info->bSetBy == CMD_LOADBLOCK )
        //if( (gti.WidthToLoad < gti.WidthToCreate ) && tile.dwMaskS > 0 && gti.WidthToLoad != dwTileMaskWidth &&
        //  info->bSetBy == CMD_LOADBLOCK )
    {
        // We have got the pitch now, recheck the width_to_load
        uint32 pitchwidth = dwPitch<<1>>tile.dwSize;
        if( pitchwidth == (unsigned int)dwTileMaskWidth )
        {
            gti.WidthToLoad = pitchwidth;
        }
    }
    if( (gti.HeightToLoad < gti.HeightToCreate  || tile.bSizeIsValid == false) && tile.dwMaskT > 0 && gti.HeightToLoad != (unsigned int)dwTileMaskHeight &&
        info->bSetBy == CMD_LOADBLOCK )
        //if( (gti.HeightToLoad < gti.HeightToCreate  ) && tile.dwMaskT > 0 && gti.HeightToLoad != dwTileMaskHeight &&
        //  info->bSetBy == CMD_LOADBLOCK )
    {
        //uint32 pitchwidth = dwPitch<<1>>tile.dwSize;
        uint32 pitchHeight = (info->dwTotalWords<<1)/dwPitch;
        if( pitchHeight == (unsigned int)dwTileMaskHeight || gti.HeightToLoad == 1 )
        {
            gti.HeightToLoad = pitchHeight;
        }
    }
    if( gti.WidthToCreate < gti.WidthToLoad )   gti.WidthToCreate = gti.WidthToLoad;
    if( gti.HeightToCreate < gti.HeightToLoad )     gti.HeightToCreate = gti.HeightToLoad;

    if( info->bSetBy == CMD_LOADTILE )
    {
        gti.LeftToLoad = (info->sl<<info->dwSize)>>tile.dwSize;
        gti.TopToLoad = info->tl;
    }
    else
    {
        gti.LeftToLoad = (info->sl<<info->dwSize)>>tile.dwSize;
        gti.TopToLoad = (info->tl<<info->dwSize)>>tile.dwSize;
    }

    uint32 total64BitWordsToLoad = (gti.HeightToLoad*gti.WidthToLoad)>>(4-tile.dwSize);
    if( total64BitWordsToLoad + tile.dwTMem > 0x200 )
    {
        //TRACE0("Warning: texture loading tmem is over range");
        if( gti.WidthToLoad > gti.HeightToLoad )
        {
            uint32 newheight = (dwPitch << 1 )>> tile.dwSize;
            tile.dwWidth = gti.WidthToLoad = gti.WidthToCreate = std::min(newheight, (gti.WidthToLoad&0xFFFFFFFE));
            tile.dwHeight = gti.HeightToCreate = gti.HeightToLoad = ((0x200 - tile.dwTMem) << (4-tile.dwSize)) / gti.WidthToLoad;
        }
        else
        {
            tile.dwHeight = gti.HeightToCreate = gti.HeightToLoad = info->dwTotalWords / ((gti.WidthToLoad << tile.dwSize) >> 1);
        }
    }

    // Check the info
    if( (info->dwTotalWords>>2) < total64BitWordsToLoad+tile.dwTMem-info->dwTmem - 4 )
    {
        // Hack here
        if( (options.enableHackForGames == HACK_FOR_ZELDA||options.enableHackForGames == HACK_FOR_ZELDA_MM) && (unsigned int)tileno != gRSP.curTile )
        {
            return false;
        }

        if( total64BitWordsToLoad+tile.dwTMem-info->dwTmem <= 0x200 )
        {
            LOG_TEXTURE(TRACE4("Fix me, info is not covering this Tmem address,Info start: 0x%x, total=0x%x, Tmem start: 0x%x, total=0x%x", 
                info->dwTmem,info->dwTotalWords>>2, tile.dwTMem, total64BitWordsToLoad));
        }
    }

    //Check memory boundary
    if( gti.Address + gti.HeightToLoad*gti.Pitch >= g_dwRamSize )
    {
        WARNING(TRACE0("Warning: texture loading tmem is over range 3"));
        gti.HeightToCreate = gti.HeightToLoad = tile.dwHeight = (g_dwRamSize-gti.Address)/gti.Pitch;
    }

    return true;
}

bool CalculateTileSizes_method_1(int tileno, TMEMLoadMapInfo *info, TxtrInfo &gti)
{
    Tile &tile = gRDP.tiles[tileno];
    //Tile &loadtile = gRDP.tiles[RDP_TXT_LOADTILE];

    // Now Initialize the texture dimension
    int loadwidth, loadheight;

    int maskwidth = tile.dwMaskS ? (1 << tile.dwMaskS ) : 0;
    int maskheight = tile.dwMaskT ? (1 << tile.dwMaskT ) : 0;
    int clampwidth = abs(tile.hilite_sh - tile.hilite_sl) +1;
    int clampheight = abs(tile.hilite_th - tile.hilite_tl) +1;
    int linewidth = tile.dwLine << (5 - tile.dwSize);

    gti.bSwapped = info->bSwapped;

    if( info->bSetBy == CMD_LOADTILE )
    {
        loadwidth = (abs(info->sh - info->sl) + 1) << info->dwSize >> tile.dwSize;
        loadheight = (abs(info->th - info->tl) + 1) << info->dwSize >> tile.dwSize;

        tile.dwPitch = info->dwWidth << info->dwSize >> 1;
        if( tile.dwPitch == 0 ) tile.dwPitch = 1024;        // Hack for Bust-A-Move

        gti.LeftToLoad = (info->sl<<info->dwSize)>>tile.dwSize;
        gti.TopToLoad = info->tl;
    }
    else
    {
        loadwidth = abs(tile.sh - tile.sl) +1;
        if( tile.dwMaskS )  
        {
            loadwidth = maskwidth;
        }

        loadheight = abs(tile.th - tile.tl) +1;
        if( tile.dwMaskT )  
        {
            loadheight = maskheight;
        }


        // It was a block load - the pitch is determined by the tile size
        if (tile.dwSize == TXT_SIZE_32b)
            tile.dwPitch = tile.dwLine << 4;
        else if (info->dxt == 0 )
        {
            tile.dwPitch = tile.dwLine << 3;
            gti.bSwapped = TRUE;
            if( info->dwTmem != tile.dwTMem && info->dxt != 0 && info->dwSize == TXT_SIZE_16b && tile.dwSize == TXT_SIZE_4b )
                conkerSwapHack = true;
        }
        else
        {
            uint32 DXT = info->dxt;
            if( info->dxt > 1 )
            {
                DXT = ReverseDXT(info->dxt, info->sh, loadwidth, tile.dwSize);
            }
            tile.dwPitch = DXT << 3;
        }

        gti.LeftToLoad = (info->sl<<info->dwSize)>>tile.dwSize;
        gti.TopToLoad = (info->tl<<info->dwSize)>>tile.dwSize;
    }

    if( options.enableHackForGames == HACK_FOR_MARIO_KART )
    {
        if( loadwidth-maskwidth == 1 && tile.dwMaskS )
        {
            loadwidth--;
            if( loadheight%2 )  loadheight--;
        }

        if( loadheight-maskheight == 1 && tile.dwMaskT )
        {
            loadheight--;
            if(loadwidth%2) loadwidth--;
        }

        if( loadwidth - ((g_TI.dwWidth<<g_TI.dwSize)>>tile.dwSize) == 1 )
        {
            loadwidth--;
            if( loadheight%2 )  loadheight--;
        }
    }


    // Limit the texture size
    if( g_curRomInfo.bUseSmallerTexture )
    {
        if( tile.dwMaskS && tile.bClampS )
        {
            if( !tile.bMirrorS )
            {
                if( clampwidth/maskwidth >= 2 )
                {
                    clampwidth = maskwidth;
                    tile.bForceWrapS = true;
                }
                else if( clampwidth && maskwidth/clampwidth >= 2 )
                {
                    maskwidth = clampwidth;
                    tile.bForceClampS = true;
                }
            }
            else
            {
                if( clampwidth/maskwidth == 2 )
                {
                    clampwidth = maskwidth*2;
                    tile.bForceWrapS = false;
                }
                else if( clampwidth/maskwidth > 2 )
                {
                    clampwidth = maskwidth*2;
                    tile.bForceWrapS = true;
                }
            }
        }

        if( tile.dwMaskT && tile.bClampT )
        {
            if( !tile.bMirrorT )
            {
                if( clampheight/maskheight >= 2 )
                {
                    clampheight = maskheight;
                    tile.bForceWrapT = true;
                }
                else if( clampheight && maskheight/clampheight >= 2 )
                {
                    maskwidth = clampwidth;
                    tile.bForceClampT = true;
                }
            }
            else
            {
                if( clampheight/maskheight == 2 )
                {
                    clampheight = maskheight*2;
                    tile.bForceWrapT = false;
                }
                else if( clampheight/maskheight >= 2 )
                {
                    clampheight = maskheight*2;
                    tile.bForceWrapT = true;
                }
            }
        }
    }
    else
    {
        //if( clampwidth > linewidth )  clampwidth = linewidth;
        if( clampwidth > 512 && clampheight > 512 )
        {
            if( clampwidth > maskwidth && maskwidth && clampheight > 256 )  clampwidth = maskwidth;
            if( clampheight > maskheight && maskheight && clampheight > 256 )   clampheight = maskheight;
        }

        if( tile.dwMaskS > 8 && tile.dwMaskT > 8 )  
        {
            maskwidth = loadwidth;
            maskheight = loadheight;
        }
        else 
        {
            if( tile.dwMaskS > 10 )
                maskwidth = loadwidth;
            if( tile.dwMaskT > 10 )
                maskheight = loadheight;
        }
    }

    gti.Pitch = tile.dwPitch;

    if( tile.dwMaskS == 0 || tile.bClampS )
    {
        gti.WidthToLoad = linewidth ? std::min( linewidth, maskwidth ? std::min(clampwidth,maskwidth) : clampwidth ) : clampwidth;
        if( tile.dwMaskS && clampwidth < maskwidth )
            tile.dwWidth = gti.WidthToCreate = clampwidth;
        else
            tile.dwWidth = gti.WidthToCreate = std::max(clampwidth,maskwidth);
    }
    else
    {
        gti.WidthToLoad = loadwidth > 2 ? std::min(loadwidth,maskwidth) : maskwidth;
        if( linewidth ) gti.WidthToLoad = std::min( linewidth, (int)gti.WidthToLoad );
        tile.dwWidth = gti.WidthToCreate = maskwidth;
    }

    if( tile.dwMaskT == 0 || tile.bClampT )
    {
        gti.HeightToLoad = maskheight ? std::min(clampheight,maskheight) : clampheight;
        if( tile.dwMaskT && clampheight < maskheight )
            tile.dwHeight = gti.HeightToCreate = clampheight;
        else
            tile.dwHeight = gti.HeightToCreate = std::max(clampheight,maskheight);
    }
    else
    {
        gti.HeightToLoad = loadheight > 2 ? std::min(loadheight,maskheight) : maskheight;
        tile.dwHeight = gti.HeightToCreate = maskheight;
    }

    if( options.enableHackForGames == HACK_FOR_MARIO_KART )
    {
        if( gti.WidthToLoad - ((g_TI.dwWidth<<g_TI.dwSize)>>tile.dwSize) == 1 )
        {
            gti.WidthToLoad--;
            if( gti.HeightToLoad%2 )    gti.HeightToLoad--;
        }
    }

    // Double check
    uint32 total64BitWordsToLoad = (gti.HeightToLoad*gti.WidthToLoad)>>(4-tile.dwSize);
    if( total64BitWordsToLoad + tile.dwTMem > 0x200 )
    {
        //TRACE0("Warning: texture loading tmem is over range");
        if( gti.WidthToLoad > gti.HeightToLoad )
        {
            uint32 newheight = (tile.dwPitch << 1 )>> tile.dwSize;
            tile.dwWidth = gti.WidthToLoad = gti.WidthToCreate = std::min(newheight, (gti.WidthToLoad&0xFFFFFFFE));
            tile.dwHeight = gti.HeightToCreate = gti.HeightToLoad = ((0x200 - tile.dwTMem) << (4-tile.dwSize)) / gti.WidthToLoad;
        }
        else
        {
            tile.dwHeight = gti.HeightToCreate = gti.HeightToLoad = info->dwTotalWords / ((gti.WidthToLoad << tile.dwSize) >> 1);
        }
    }

    // Check the info
    if( (info->dwTotalWords>>2) < total64BitWordsToLoad+tile.dwTMem-info->dwTmem - 4 )
    {
        // Hack here
        if( (options.enableHackForGames == HACK_FOR_ZELDA||options.enableHackForGames == HACK_FOR_ZELDA_MM) && (unsigned int)tileno != gRSP.curTile )
        {
            return false;
        }

        if( total64BitWordsToLoad+tile.dwTMem-info->dwTmem <= 0x200 )
        {
            LOG_TEXTURE(TRACE4("Fix me, info is not covering this Tmem address,Info start: 0x%x, total=0x%x, Tmem start: 0x%x, total=0x%x", 
                info->dwTmem,info->dwTotalWords>>2, tile.dwTMem, total64BitWordsToLoad));
        }
    }

    //Check memory boundary
    if( gti.Address + gti.HeightToLoad*gti.Pitch >= g_dwRamSize )
    {
        WARNING(TRACE0("Warning: texture loading tmem is over range 3"));
        gti.HeightToCreate = gti.HeightToLoad = tile.dwHeight = (g_dwRamSize-gti.Address)/gti.Pitch;
    }

    return true;
}

TxtrCacheEntry* LoadTexture(uint32 tileno)
{
    //TxtrCacheEntry *pEntry = NULL;
    TxtrInfo gti;

    Tile &tile = gRDP.tiles[tileno];

    // Retrieve the tile loading info
    uint32 infoTmemAddr = tile.dwTMem;
    TMEMLoadMapInfo *info = &g_tmemLoadAddrMap[infoTmemAddr];
    if( !IsTmemFlagValid(infoTmemAddr) )
    {
        infoTmemAddr =  GetValidTmemInfoIndex(infoTmemAddr);
        info = &g_tmemLoadAddrMap[infoTmemAddr];
    }

    if( info->dwFormat != tile.dwFormat )
    {
        // Check the tile format, hack for Zelda's road
        if( tileno != gRSP.curTile && tile.dwTMem == gRDP.tiles[gRSP.curTile].dwTMem &&
            tile.dwFormat != gRDP.tiles[gRSP.curTile].dwFormat )
        {
            //TRACE1("Tile %d format is not matching the loaded texture format", tileno);
            return NULL;
        }
    }

    gti = tile; // Copy tile info to textureInfo entry

    gti.TLutFmt = gRDP.otherMode.text_tlut <<RSP_SETOTHERMODE_SHIFT_TEXTLUT;
    if (gti.Format == TXT_FMT_CI && gti.TLutFmt == TLUT_FMT_NONE )
        gti.TLutFmt = TLUT_FMT_RGBA16;      // Force RGBA

    gti.PalAddress = (uchar *) (&g_wRDPTlut[0]);
    if( !options.bUseFullTMEM && tile.dwSize == TXT_SIZE_4b )
        gti.PalAddress += 16  * 2 * tile.dwPalette; 

    gti.Address = (info->dwLoadAddress+(tile.dwTMem-infoTmemAddr)*8) & (g_dwRamSize-1) ;
    gti.pPhysicalAddress = ((uint8*)g_pRDRAMu32)+gti.Address;
    gti.tileNo = tileno;

    if( g_curRomInfo.bTxtSizeMethod2 )
    {
        if( !CalculateTileSizes_method_2(tileno, info, gti) )
            return NULL;
    }
    else
    {
        if( !CalculateTileSizes_method_1(tileno, info, gti) )
            return NULL;
    }

    LOG_TEXTURE(
    {
        TRACE0("Loading texture:\n");
        DebuggerAppendMsg("Left: %d, Top: %d, Width: %d, Height: %d, Size to Load (%d, %d)", 
            gti.LeftToLoad, gti.TopToLoad, gti.WidthToCreate, gti.HeightToCreate, gti.WidthToLoad, gti.HeightToLoad);
        DebuggerAppendMsg("Pitch: %d, Addr: 0x%08x", gti.Pitch, gti.Address);
    });

    // Option for faster loading tiles
    if( g_curRomInfo.bFastLoadTile && info->bSetBy == CMD_LOADTILE && ((gti.Pitch<<1)>>gti.Size) <= 0x400
        //&& ((gti.Pitch<<1)>>gti.Size) > 128 && status.primitiveType == PRIM_TEXTRECT
        )
    {
        uint32 idx = tileno-gRSP.curTile;
        status.LargerTileRealLeft[idx] = gti.LeftToLoad;
        gti.LeftToLoad=0;
        gti.WidthToLoad = gti.WidthToCreate = ((gti.Pitch<<1)>>gti.Size);
        status.UseLargerTile[idx]=true;
    }

    // Loading the textures by using texture cache manager
    return gTextureManager.GetTexture(&gti, true, true, true);  // Load the texture by using texture cache
}

void PrepareTextures()
{
    if( gRDP.textureIsChanged || !currentRomOptions.bFastTexCRC )
    {
        status.UseLargerTile[0]=false;
        status.UseLargerTile[1]=false;

        int tilenos[2];
        if( CRender::g_pRender->IsTexel0Enable() || gRDP.otherMode.cycle_type  == CYCLE_TYPE_COPY )
            tilenos[0] = gRSP.curTile;
        else
            tilenos[0] = -1;

        if( gRSP.curTile<7 && CRender::g_pRender->IsTexel1Enable() )
            tilenos[1] = gRSP.curTile+1;
        else
            tilenos[1] = -1;


        for( int i=0; i<2; i++ )
        {
            if( tilenos[i] < 0 )    continue;

            TxtrCacheEntry *pEntry = LoadTexture(tilenos[i]);
            if (pEntry && pEntry->pTexture )
            {
                if( pEntry->txtrBufIdx <= 0 )
                {
                    if( pEntry->pEnhancedTexture && pEntry->dwEnhancementFlag == TEXTURE_EXTERNAL && !options.bLoadHiResTextures )
                    {
                        SAFE_DELETE(pEntry->pEnhancedTexture);
                    }

                    if( options.bLoadHiResTextures && (pEntry->pEnhancedTexture == NULL || pEntry->dwEnhancementFlag < TEXTURE_EXTERNAL ) )
                    {
                        LoadHiresTexture(*pEntry);
                    }

                    if( pEntry->pEnhancedTexture == NULL || (pEntry->dwEnhancementFlag != options.textureEnhancement && pEntry->dwEnhancementFlag < TEXTURE_EXTERNAL ) )
                    {
                        EnhanceTexture(pEntry);
                    }
                }

                CRender::g_pRender->SetCurrentTexture( tilenos[i], 
                    (pEntry->pEnhancedTexture)?pEntry->pEnhancedTexture:pEntry->pTexture,
                    pEntry->ti.WidthToLoad, pEntry->ti.HeightToLoad, pEntry);
            }
            else
            {
                pEntry = gTextureManager.GetBlackTexture();
                CRender::g_pRender->SetCurrentTexture( tilenos[i], pEntry->pTexture, 4, 4, pEntry);
                _VIDEO_DisplayTemporaryMessage("Fail to load texture, use black to replace");
            }
        }

        gRDP.textureIsChanged = false;
    }
}

extern uint32 g_TxtLoadBy;;

void DLParser_LoadTLut(Gfx *gfx)
{
gRDP.textureIsChanged = true;

uint32 tileno = gfx->loadtile.tile;
uint32 uls = gfx->loadtile.sl/4;
uint32 ult = gfx->loadtile.tl/4;
uint32 lrs = gfx->loadtile.sh/4;
uint32 lrt = gfx->loadtile.th/4;

#ifdef DEBUGGER
uint32 dwTLutFmt = (gRDP.otherModeH >> RSP_SETOTHERMODE_SHIFT_TEXTLUT)&0x3;
#endif

uint32 dwCount;
// starting location in the palettes
uint32 dwTMEMOffset = gRDP.tiles[tileno].dwTMem - 256;  
// number to copy
dwCount = ((uint16)((gfx->words.w1) >> 14) & 0x03FF) + 1;
uint32 dwRDRAMOffset = 0;

Tile &tile = gRDP.tiles[tileno];
tile.bForceWrapS = tile.bForceWrapT = tile.bForceClampS = tile.bForceClampT = false;

tile.hilite_sl = tile.sl = uls;
tile.hilite_tl = tile.tl = ult;
tile.sh = lrs;
tile.th = lrt;
tile.bSizeIsValid = true;

tile.lastTileCmd = CMD_LOADTLUT;

#ifdef DEBUGGER
/*
if((((gfx->words.w0)>>12)&0x3) != 0 || (((gfx->words.w0))&0x3) != 0 || (((gfx->words.w1)>>12)&0x3) != 0 || (((gfx->words.w1))&0x3) != 0)
    TRACE0("Load tlut, sl,tl,sh,th are not integers");
*/
#endif

dwCount = (lrs - uls)+1;
dwRDRAMOffset = (uls + ult*g_TI.dwWidth )*2;
uint32 dwPalAddress = g_TI.dwAddr + dwRDRAMOffset;

//Copy PAL to the PAL memory
uint16 *srcPal = (uint16*)(g_pRDRAMu8 + (dwPalAddress& (g_dwRamSize-1)) );
for (uint32 i=0; i<dwCount && i<0x100; i++)
    g_wRDPTlut[(i+dwTMEMOffset)^1] = srcPal[i^1];

if( options.bUseFullTMEM )
    {
    for (uint32 i=0; i<dwCount && i+tile.dwTMem<0x200; i++)
        *(uint16*)(&g_Tmem.g_Tmem64bit[tile.dwTMem+i]) = srcPal[i^1];
    }

LOG_TEXTURE(
{
DebuggerAppendMsg("LoadTLut Tile: %d Start: 0x%X+0x%X, Count: 0x%X\nFmt is %s, TMEM=0x%X\n", 
                 tileno, g_TI.dwAddr, dwRDRAMOffset, dwCount,textluttype[dwTLutFmt],
                 dwTMEMOffset);

DebuggerAppendMsg("    :ULS: 0x%X, ULT:0x%X, LRS: 0x%X, LRT:0x%X\n", uls, ult, lrs,lrt);

if( pauseAtNext && eventToPause == NEXT_LOADTLUT && dwCount == 16 ) 
    {
    char buf[2000];
    strcpy(buf, "Data:\n");
    for(uint32 i=0; i<16; i++ )
        {
        sprintf(buf+strlen(buf), "%04X ", g_wRDPTlut[dwTMEMOffset+i]);
        if(i%4 == 3)
            sprintf(buf+strlen(buf), "\n");
        }
    sprintf(buf+strlen(buf), "\n");
    TRACE0(buf);
    }
});

DEBUGGER_PAUSE_COUNT_N(NEXT_LOADTLUT);

extern bool RevTlutTableNeedUpdate;
RevTlutTableNeedUpdate = true;
g_TxtLoadBy = CMD_LOADTLUT;
}


void DLParser_LoadBlock(Gfx *gfx)
{
    gRDP.textureIsChanged = true;

    uint32 tileno   = gfx->loadtile.tile;
    uint32 uls      = gfx->loadtile.sl;
    uint32 ult      = gfx->loadtile.tl;
    uint32 lrs      = gfx->loadtile.sh;
    uint32 dxt      = gfx->loadtile.th;                 // 1.11 fixed point

    Tile &tile = gRDP.tiles[tileno];
    tile.bForceWrapS = tile.bForceWrapT = tile.bForceClampS = tile.bForceClampT = false;

    uint32 size     = lrs+1;
    if( tile.dwSize == TXT_SIZE_32b )   size<<=1;

    SetTmemFlag(tile.dwTMem, size>>2);

    TMEMLoadMapInfo &info = g_tmemLoadAddrMap[tile.dwTMem];

    info.bSwapped = (dxt == 0? TRUE : FALSE);

    info.sl = tile.hilite_sl = tile.sl = uls;
    info.sh = tile.hilite_sh = tile.sh = lrs;
    info.tl = tile.tl = ult;
    info.th = tile.th = dxt;
    tile.bSizeIsValid = false;
    tile.lastTileCmd = CMD_LOADBLOCK;

    info.dwLoadAddress = g_TI.dwAddr;
    info.bSetBy = CMD_LOADBLOCK;
    info.dxt = dxt;
    info.dwLine = tile.dwLine;

    info.dwFormat = g_TI.dwFormat;
    info.dwSize = g_TI.dwSize;
    info.dwWidth = g_TI.dwWidth;
    info.dwTotalWords = size;
    info.dwTmem = tile.dwTMem;

    if( gRDP.tiles[tileno].dwTMem == 0 )
    {
        if( size >= 1024 )
        {
            memcpy(&g_tmemInfo0, &info, sizeof(TMEMLoadMapInfo) );
            g_tmemInfo0.dwTotalWords = size>>2;
        }
        
        if( size == 2048 )
        {
            memcpy(&g_tmemInfo1, &info, sizeof(TMEMLoadMapInfo) );
            g_tmemInfo1.dwTotalWords = size>>2;
        }
    }
    else if( tile.dwTMem == 0x100 )
    {
        if( size == 1024 )
        {
            memcpy(&g_tmemInfo1, &info, sizeof(TMEMLoadMapInfo) );
            g_tmemInfo1.dwTotalWords = size>>2;
        }
    }

    g_TxtLoadBy = CMD_LOADBLOCK;


    if( options.bUseFullTMEM )
    {
        uint32 bytes = (lrs + 1) << tile.dwSize >> 1;
        uint32 address = g_TI.dwAddr + ult * g_TI.bpl + (uls << g_TI.dwSize >> 1);
        if ((bytes == 0) || ((address + bytes) > g_dwRamSize) || (((tile.dwTMem << 3) + bytes) > 4096))
        {
            return;
        }
        uint64* src = (uint64*)(g_pRDRAMu8+address);
        uint64* dest = &g_Tmem.g_Tmem64bit[tile.dwTMem];

        if( dxt > 0)
        {
            void (*Interleave)( void *mem, uint32 numDWords );

            uint32 line = (2047 + dxt) / dxt;
            uint32 bpl = line << 3;
            uint32 height = bytes / bpl;

            if (tile.dwSize == TXT_SIZE_32b)
                Interleave = QWordInterleave;
            else
                Interleave = DWordInterleave;

            for (uint32 y = 0; y < height; y++)
            {
                UnswapCopy( src, dest, bpl );
                if (y & 1) Interleave( dest, line );

                src += line;
                dest += line;
            }
        }
        else
            UnswapCopy( src, dest, bytes );
    }


    LOG_UCODE("    Tile:%d (%d,%d - %d) DXT:0x%04x\n", tileno, uls, ult, lrs, dxt);

    LOG_TEXTURE(
    {
        DebuggerAppendMsg("LoadBlock:%d (%d,%d,%d) DXT:0x%04x(%X)\n",
            tileno, uls, ult, (((gfx->words.w1)>>12)&0x0FFF), dxt, ((gfx->words.w1)&0x0FFF));
    });

    DEBUGGER_PAUSE_COUNT_N(NEXT_TEXTURE_CMD);
}

void swap(uint32 &a, uint32 &b)
{
    uint32 temp = a;
    a = b;
    b = temp;
}
void DLParser_LoadTile(Gfx *gfx)
{
    gRDP.textureIsChanged = true;

    uint32 tileno   = gfx->loadtile.tile;
    uint32 uls      = gfx->loadtile.sl/4;
    uint32 ult      = gfx->loadtile.tl/4;
    uint32 lrs      = gfx->loadtile.sh/4;
    uint32 lrt      = gfx->loadtile.th/4;

    Tile &tile = gRDP.tiles[tileno];
    tile.bForceWrapS = tile.bForceWrapT = tile.bForceClampS = tile.bForceClampT = false;

    if (lrt < ult) swap(lrt, ult);
    if (lrs < uls) swap(lrs, uls);

    tile.hilite_sl = tile.sl = uls;
    tile.hilite_tl = tile.tl = ult;
    tile.hilite_sh = tile.sh = lrs;
    tile.hilite_th = tile.th = lrt;
    tile.bSizeIsValid = true;

    // compute block height, and bpl of source and destination
    uint32 bpl = (lrs - uls + 1) << tile.dwSize >> 1;
    uint32 height = lrt - ult + 1;
    uint32 line = tile.dwLine;
    if (tile.dwSize == TXT_SIZE_32b) line <<= 1;

    if (((tile.dwTMem << 3) + line * height) > 4096)  // check destination ending point (TMEM is 4k bytes)
        return;

    if( options.bUseFullTMEM )
    {
        void (*Interleave)( void *mem, uint32 numDWords );
        uint32 address, y;
        uint64 *dest;
        uint8 *src;

        if( g_TI.bpl == 0 )
        {
            if( options.enableHackForGames == HACK_FOR_BUST_A_MOVE )
            {
                g_TI.bpl = 1024;        // Hack for Bust-A-Move
            }
            else
            {
                TRACE0("Warning: g_TI.bpl = 0" );
            }
        }

        address = g_TI.dwAddr + tile.tl * g_TI.bpl + (tile.sl << g_TI.dwSize >> 1);
        src = &g_pRDRAMu8[address];
        dest = &g_Tmem.g_Tmem64bit[tile.dwTMem];

        if ((address + height * bpl) > g_dwRamSize) // check source ending point
        {
            return;
        }

        // Line given for 32-bit is half what it seems it should since they split the
        // high and low words. I'm cheating by putting them together.
        if (tile.dwSize == TXT_SIZE_32b)
        {
            Interleave = QWordInterleave;
        }
        else
        {
            Interleave = DWordInterleave;
        }

        if( tile.dwLine == 0 )
        {
            //tile.dwLine = 1;
            return;
        }

        for (y = 0; y < height; y++)
        {
            UnswapCopy( src, dest, bpl );
            if (y & 1) Interleave( dest, line );

            src += g_TI.bpl;
            dest += line;
        }
    }


    for( int i=0; i<8; i++ )
    {
        if( gRDP.tiles[i].dwTMem == tile.dwTMem )
            gRDP.tiles[i].lastTileCmd = CMD_LOADTILE;
    }

    uint32 size = line * height;
    SetTmemFlag(tile.dwTMem,size );

    LOG_TEXTURE(
    {
        DebuggerAppendMsg("LoadTile:%d (%d,%d) -> (%d,%d) [%d x %d]\n",
            tileno, uls, ult, lrs, lrt,
            (lrs - uls)+1, (lrt - ult)+1);
    });

    
    DEBUGGER_PAUSE_COUNT_N(NEXT_TEXTURE_CMD);

    LOG_UCODE("    Tile:%d (%d,%d) -> (%d,%d) [%d x %d]",
        tileno, uls, ult, lrs, lrt,
        (lrs - uls)+1, (lrt - ult)+1);

    TMEMLoadMapInfo &info = g_tmemLoadAddrMap[tile.dwTMem];

    info.dwLoadAddress = g_TI.dwAddr;
    info.dwFormat = g_TI.dwFormat;
    info.dwSize = g_TI.dwSize;
    info.dwWidth = g_TI.dwWidth;

    info.sl = uls;
    info.sh = lrs;
    info.tl = ult;
    info.th = lrt;
    
    info.dxt = 0;
    info.dwLine = tile.dwLine;
    info.dwTmem = tile.dwTMem;
    info.dwTotalWords = size<<2;

    info.bSetBy = CMD_LOADTILE;
    info.bSwapped =FALSE;

    g_TxtLoadBy = CMD_LOADTILE;

    if( tile.dwTMem == 0 )
    {
        if( size >= 256 )
        {
            memcpy(&g_tmemInfo0, &info, sizeof(TMEMLoadMapInfo) );
            g_tmemInfo0.dwTotalWords = size;
        }

        if( size == 512 )
        {
            memcpy(&g_tmemInfo1, &info, sizeof(TMEMLoadMapInfo) );
            g_tmemInfo1.dwTotalWords = size;
        }
    }
    else if( tile.dwTMem == 0x100 )
    {
        if( size == 256 )
        {
            memcpy(&g_tmemInfo1, &info, sizeof(TMEMLoadMapInfo) );
            g_tmemInfo1.dwTotalWords = size;
        }
    }
}


const char *pszOnOff[2] = {"Off", "On"};
uint32 lastSetTile;
void DLParser_SetTile(Gfx *gfx)
{
    gRDP.textureIsChanged = true;

    uint32 tileno       = gfx->settile.tile;
    Tile &tile = gRDP.tiles[tileno];
    tile.bForceWrapS = tile.bForceWrapT = tile.bForceClampS = tile.bForceClampT = false;

    lastSetTile = tileno;

    tile.dwFormat   = gfx->settile.fmt;
    tile.dwSize     = gfx->settile.siz;
    tile.dwLine     = gfx->settile.line;
    tile.dwTMem     = gfx->settile.tmem;

    tile.dwPalette  = gfx->settile.palette;
    tile.bClampT    = gfx->settile.ct;
    tile.bMirrorT   = gfx->settile.mt;
    tile.dwMaskT    = gfx->settile.maskt;
    tile.dwShiftT   = gfx->settile.shiftt;
    tile.bClampS    = gfx->settile.cs;
    tile.bMirrorS   = gfx->settile.ms;
    tile.dwMaskS    = gfx->settile.masks;
    tile.dwShiftS   = gfx->settile.shifts;


    tile.fShiftScaleS = 1.0f;
    if( tile.dwShiftS )
    {
        if( tile.dwShiftS > 10 )
        {
            tile.fShiftScaleS = (float)(1 << (16 - tile.dwShiftS));
        }
        else
        {
            tile.fShiftScaleS = (float)1.0f/(1 << tile.dwShiftS);
        }
    }

    tile.fShiftScaleT = 1.0f;
    if( tile.dwShiftT )
    {
        if( tile.dwShiftT > 10 )
        {
            tile.fShiftScaleT = (float)(1 << (16 - tile.dwShiftT));
        }
        else
        {
            tile.fShiftScaleT = (float)1.0f/(1 << tile.dwShiftT);
        }
    }

    // Hack for DK64
    /*
    if( tile.dwMaskS > 0 && tile.dwMaskT > 0 && tile.dwMaskS < 8 && tile.dwMaskT < 8 )
    {
        tile.sh = tile.sl + (1<<tile.dwMaskS);
        tile.th = tile.tl + (1<<tile.dwMaskT);
        tile.hilite_sl = tile.sl;
        tile.hilite_tl = tile.tl;
    }
    */

    tile.lastTileCmd = CMD_SETTILE;

    LOG_TEXTURE(
    {
    DebuggerAppendMsg("SetTile:%d  Fmt: %s/%s Line:%d TMem:0x%04x Palette:%d\n",
        tileno, pszImgFormat[tile.dwFormat], pszImgSize[tile.dwSize],
        tile.dwLine,  tile.dwTMem, tile.dwPalette);
    DebuggerAppendMsg("         S: Clamp: %s Mirror:%s Mask:0x%x Shift:0x%x\n",
        pszOnOff[tile.bClampS],pszOnOff[tile.bMirrorS],
        tile.dwMaskS, tile.dwShiftS);
    DebuggerAppendMsg("         T: Clamp: %s Mirror:%s Mask:0x%x Shift:0x%x\n",
        pszOnOff[tile.bClampT],pszOnOff[tile.bMirrorT],
        tile.dwMaskT, tile.dwShiftT);
    });

    DEBUGGER_PAUSE_COUNT_N(NEXT_TEXTURE_CMD);

    LOG_UCODE("    Tile:%d  Fmt: %s/%s Line:%d TMem:0x%04x Palette:%d",
        tileno, pszImgFormat[tile.dwFormat], pszImgSize[tile.dwSize],
        tile.dwLine, tile.dwTMem, tile.dwPalette);
    LOG_UCODE("         S: Clamp: %s Mirror:%s Mask:0x%x Shift:0x%x",
        pszOnOff[tile.bClampS],pszOnOff[tile.bMirrorS],
        tile.dwMaskS, tile.dwShiftS);
    LOG_UCODE("         T: Clamp: %s Mirror:%s Mask:0x%x Shift:0x%x",
        pszOnOff[tile.bClampT],pszOnOff[tile.bMirrorT],
        tile.dwMaskT, tile.dwShiftT);
}

void DLParser_SetTileSize(Gfx *gfx)
{
    gRDP.textureIsChanged = true;

    uint32 tileno   = gfx->loadtile.tile;
    int sl      = gfx->loadtile.sl;
    int tl      = gfx->loadtile.tl;
    int sh      = gfx->loadtile.sh;
    int th      = gfx->loadtile.th;

    Tile &tile = gRDP.tiles[tileno];
    tile.bForceWrapS = tile.bForceWrapT = tile.bForceClampS = tile.bForceClampT = false;

    if( options.bUseFullTMEM )
    {
        tile.bSizeIsValid = true;
        tile.hilite_sl = tile.sl = sl / 4;
        tile.hilite_tl = tile.tl = tl / 4;
        tile.hilite_sh = tile.sh = sh / 4;
        tile.hilite_th = tile.th = th / 4;

        tile.fhilite_sl = tile.fsl = sl / 4.0f;
        tile.fhilite_tl = tile.ftl = tl / 4.0f;
        tile.fhilite_sh = tile.fsh = sh / 4.0f;
        tile.fhilite_th = tile.fth = th / 4.0f;

        tile.lastTileCmd = CMD_SETTILE_SIZE;
    }
    else
    {
        if( tile.lastTileCmd != CMD_SETTILE_SIZE )
        {
            tile.bSizeIsValid = true;
            if( sl/4 > sh/4 || tl/4 > th/4 || (sh == 0 && tile.dwShiftS==0 && th == 0 && tile.dwShiftT ==0 ) )
            {
#ifdef DEBUGGER
                if( sl != 0 || tl != 0 || sh != 0 || th != 0 )
                {
                    if( tile.dwMaskS==0 || tile.dwMaskT==0 )
                        TRACE0("Check me, setTileSize is not correct");
                }
#endif
                tile.bSizeIsValid = false;
            }
            tile.hilite_sl = tile.sl = sl / 4;
            tile.hilite_tl = tile.tl = tl / 4;
            tile.hilite_sh = tile.sh = sh / 4;
            tile.hilite_th = tile.th = th / 4;

            tile.fhilite_sl = tile.fsl = sl / 4.0f;
            tile.fhilite_tl = tile.ftl = tl / 4.0f;
            tile.fhilite_sh = tile.fsh = sh / 4.0f;
            tile.fhilite_th = tile.fth = th / 4.0f;

            tile.lastTileCmd = CMD_SETTILE_SIZE;
        }
        else
        {
            tile.fhilite_sh = tile.fsh;
            tile.fhilite_th = tile.fth;
            tile.fhilite_sl = tile.fsl = (sl>0x7ff ? (sl-0xfff) : sl)/4.0f;
            tile.fhilite_tl = tile.ftl = (tl>0x7ff ? (tl-0xfff) : tl)/4.0f;

            tile.hilite_sl = sl>0x7ff ? (sl-0xfff) : sl;
            tile.hilite_tl = tl>0x7ff ? (tl-0xfff) : tl;
            tile.hilite_sl /= 4;
            tile.hilite_tl /= 4;
            tile.hilite_sh = sh/4;
            tile.hilite_th = th/4;

            tile.lastTileCmd = CMD_SETTILE_SIZE;
        }
    }

    LOG_TEXTURE(
    {
    DebuggerAppendMsg("SetTileSize:%d (%d/4,%d/4) -> (%d/4,%d/4) [%d x %d]\n",
        tileno, sl, tl, sh, th, 
        ((sh/4) - (sl/4)) + 1, ((th/4) - (tl/4)) + 1);
    });
    DEBUGGER_PAUSE_COUNT_N(NEXT_TEXTURE_CMD);

    LOG_UCODE("    Tile:%d (%d,%d) -> (%d,%d) [%d x %d]",
        tileno, sl/4, tl/4, sh/4, th/4,
        ((sh/4) - (sl/4)) + 1, ((th/4) - (tl/4)) + 1);
}

extern const char *pszImgFormat[8];// = {"RGBA", "YUV", "CI", "IA", "I", "?1", "?2", "?3"};
extern const char *pszImgSize[4];// = {"4", "8", "16", "32"};
void DLParser_SetTImg(Gfx *gfx)
{
    gRDP.textureIsChanged = true;

    g_TI.dwFormat   = gfx->setimg.fmt;
    g_TI.dwSize     = gfx->setimg.siz;
    g_TI.dwWidth    = gfx->setimg.width + 1;
    g_TI.dwAddr     = RSPSegmentAddr((gfx->setimg.addr));
    g_TI.bpl        = g_TI.dwWidth << g_TI.dwSize >> 1;

#ifdef DEBUGGER
    if( g_TI.dwAddr == 0x00ffffff)
    {
        TRACE0("Check me here in setTimg");
    }

    LOG_TEXTURE(TRACE4("SetTImage: 0x%08x Fmt: %s/%s Width in Pixel: %d\n", g_TI.dwAddr,
            pszImgFormat[g_TI.dwFormat], pszImgSize[g_TI.dwSize], g_TI.dwWidth));

    DEBUGGER_PAUSE_COUNT_N(NEXT_TEXTURE_CMD);

    LOG_UCODE("Image: 0x%08x Fmt: %s/%s Width in Pixel: %d", g_TI.dwAddr,
        pszImgFormat[g_TI.dwFormat], pszImgSize[g_TI.dwSize], g_TI.dwWidth);
#endif
}

void DLParser_TexRect(Gfx *gfx)
{
    //Gtexrect *gtextrect = (Gtexrect *)gfx;

    if( !status.bCIBufferIsRendered ) g_pFrameBufferManager->ActiveTextureBuffer();

    status.primitiveType = PRIM_TEXTRECT;

    // This command used 128bits, and not 64 bits. This means that we have to look one 
    // Command ahead in the buffer, and update the PC.
    uint32 dwPC = gDlistStack[gDlistStackPointer].pc;       // This points to the next instruction
    uint32 dwCmd2 = *(uint32 *)(g_pRDRAMu8 + dwPC+4);
    uint32 dwCmd3 = *(uint32 *)(g_pRDRAMu8 + dwPC+4+8);
    uint32 dwHalf1 = *(uint32 *)(g_pRDRAMu8 + dwPC);
    uint32 dwHalf2 = *(uint32 *)(g_pRDRAMu8 + dwPC+8);

    if( options.enableHackForGames == HACK_FOR_ALL_STAR_BASEBALL || options.enableHackForGames == HACK_FOR_MLB )
    {
        if( ((dwHalf1>>24) == 0xb4 || (dwHalf1>>24) == 0xb3 || (dwHalf1>>24) == 0xb2 || (dwHalf1>>24) == 0xe1) && 
            ((dwHalf2>>24) == 0xb4 || (dwHalf2>>24) == 0xb3 || (dwHalf2>>24) == 0xb2 || (dwHalf2>>24) == 0xf1) )
        {
            // Increment PC so that it points to the right place
            gDlistStack[gDlistStackPointer].pc += 16;
        }
        else
        {
            // Hack for some games, All_Star_Baseball_2000
            gDlistStack[gDlistStackPointer].pc += 8;
            dwCmd3 = dwCmd2;
            //dwCmd2 = dwHalf1;
            //dwCmd2 = 0;

            // fix me here
            dwCmd2 = (((dwHalf1>>12)&0x03FF)<<17) | (((dwHalf1)&0x03FF)<<1);
        }   
    }
    else
    {
        gDlistStack[gDlistStackPointer].pc += 16;
    }


    // Hack for Mario Tennis
    if( !status.bHandleN64RenderTexture && g_CI.dwAddr == g_ZI.dwAddr )
    {
        return;
    }


    LOG_UCODE("0x%08x: %08x %08x", dwPC, *(uint32 *)(g_pRDRAMu8 + dwPC+0), *(uint32 *)(g_pRDRAMu8 + dwPC+4));
    LOG_UCODE("0x%08x: %08x %08x", dwPC+8, *(uint32 *)(g_pRDRAMu8 + dwPC+8), *(uint32 *)(g_pRDRAMu8 + dwPC+8+4));

    uint32 dwXH     = (((gfx->words.w0)>>12)&0x0FFF)/4;
    uint32 dwYH     = (((gfx->words.w0)    )&0x0FFF)/4;
    uint32 tileno   = ((gfx->words.w1)>>24)&0x07;
    uint32 dwXL     = (((gfx->words.w1)>>12)&0x0FFF)/4;
    uint32 dwYL     = (((gfx->words.w1)    )&0x0FFF)/4;
    uint16 uS       = (uint16)(  dwCmd2>>16)&0xFFFF;
    uint16 uT       = (uint16)(  dwCmd2    )&0xFFFF;
    uint16  uDSDX   = (uint16)((  dwCmd3>>16)&0xFFFF);
    uint16  uDTDY       = (uint16)((  dwCmd3    )&0xFFFF);
    

    if( (int)dwXL >= gRDP.scissor.right || (int)dwYL >= gRDP.scissor.bottom || (int)dwXH < gRDP.scissor.left || (int)dwYH < gRDP.scissor.top )
    {
        // Clipping
        return;
    }

    short s16S = *(short*)(&uS);
    short s16T = *(short*)(&uT);

    short    s16DSDX  = *(short*)(&uDSDX);
    short  s16DTDY  = *(short*)(&uDTDY);

    uint32 curTile = gRSP.curTile;
    ForceMainTextureIndex(tileno);

    float fS0 = s16S / 32.0f;
    float fT0 = s16T / 32.0f;

    float fDSDX = s16DSDX / 1024.0f;
    float fDTDY = s16DTDY / 1024.0f;

    uint32 cycletype = gRDP.otherMode.cycle_type;

    if (cycletype == CYCLE_TYPE_COPY)
    {
        fDSDX /= 4.0f;  // In copy mode 4 pixels are copied at once.
        dwXH++;
        dwYH++;
    }
    else if (cycletype == CYCLE_TYPE_FILL)
    {
        dwXH++;
        dwYH++;
    }

    if( fDSDX == 0 )    fDSDX = 1;
    if( fDTDY == 0 )    fDTDY = 1;

    float fS1 = fS0 + (fDSDX * (dwXH - dwXL));
    float fT1 = fT0 + (fDTDY * (dwYH - dwYL));

    LOG_UCODE("    Tile:%d Screen(%d,%d) -> (%d,%d)", tileno, dwXL, dwYL, dwXH, dwYH);
    LOG_UCODE("           Tex:(%#5f,%#5f) -> (%#5f,%#5f) (DSDX:%#5f DTDY:%#5f)",
                                            fS0, fT0, fS1, fT1, fDSDX, fDTDY);
    LOG_UCODE("");

    float t0u0 = (fS0-gRDP.tiles[tileno].hilite_sl) * gRDP.tiles[tileno].fShiftScaleS;
    float t0v0 = (fT0-gRDP.tiles[tileno].hilite_tl) * gRDP.tiles[tileno].fShiftScaleT;
    float t0u1 = t0u0 + (fDSDX * (dwXH - dwXL))*gRDP.tiles[tileno].fShiftScaleS;
    float t0v1 = t0v0 + (fDTDY * (dwYH - dwYL))*gRDP.tiles[tileno].fShiftScaleT;

    if( dwXL==0 && dwYL==0 && dwXH==windowSetting.fViWidth-1 && dwYH==windowSetting.fViHeight-1 &&
        t0u0 == 0 && t0v0==0 && t0u1==0 && t0v1==0 )
    {
        //Using TextRect to clear the screen
    }
    else
    {
        if( status.bHandleN64RenderTexture && //status.bDirectWriteIntoRDRAM && 
            g_pRenderTextureInfo->CI_Info.dwFormat == gRDP.tiles[tileno].dwFormat && 
            g_pRenderTextureInfo->CI_Info.dwSize == gRDP.tiles[tileno].dwSize && 
            gRDP.tiles[tileno].dwFormat == TXT_FMT_CI && gRDP.tiles[tileno].dwSize == TXT_SIZE_8b )
        {
            if( options.enableHackForGames == HACK_FOR_YOSHI )
            {
                // Hack for Yoshi background image
                PrepareTextures();
                TexRectToFrameBuffer_8b(dwXL, dwYL, dwXH, dwYH, t0u0, t0v0, t0u1, t0v1, tileno);
                DEBUGGER_PAUSE_AT_COND_AND_DUMP_COUNT_N((eventToPause == NEXT_FLUSH_TRI || eventToPause == NEXT_TEXTRECT), {
                    DebuggerAppendMsg("TexRect: tile=%d, X0=%d, Y0=%d, X1=%d, Y1=%d,\nfS0=%f, fT0=%f, ScaleS=%f, ScaleT=%f\n",
                        gRSP.curTile, dwXL, dwYL, dwXH, dwYH, fS0, fT0, fDSDX, fDTDY);
                    DebuggerAppendMsg("Pause after TexRect for Yoshi\n");
                });

            }
            else
            {
                if( frameBufferOptions.bUpdateCIInfo )
                {
                    PrepareTextures();
                    TexRectToFrameBuffer_8b(dwXL, dwYL, dwXH, dwYH, t0u0, t0v0, t0u1, t0v1, tileno);
                }

                if( !status.bDirectWriteIntoRDRAM )
                {
                    CRender::g_pRender->TexRect(dwXL, dwYL, dwXH, dwYH, fS0, fT0, fDSDX, fDTDY);

                    status.dwNumTrisRendered += 2;
                }
            }
        }
        else
        {
            CRender::g_pRender->TexRect(dwXL, dwYL, dwXH, dwYH, fS0, fT0, fDSDX, fDTDY);
            status.bFrameBufferDrawnByTriangles = true;

            status.dwNumTrisRendered += 2;
        }
    }

    if( status.bHandleN64RenderTexture )    g_pRenderTextureInfo->maxUsedHeight = std::max(g_pRenderTextureInfo->maxUsedHeight,(int)dwYH);

    ForceMainTextureIndex(curTile);
}


void DLParser_TexRectFlip(Gfx *gfx)
{ 
    status.bCIBufferIsRendered = true;
    status.primitiveType = PRIM_TEXTRECTFLIP;

    // This command used 128bits, and not 64 bits. This means that we have to look one 
    // Command ahead in the buffer, and update the PC.
    uint32 dwPC = gDlistStack[gDlistStackPointer].pc;       // This points to the next instruction
    uint32 dwCmd2 = *(uint32 *)(g_pRDRAMu8 + dwPC+4);
    uint32 dwCmd3 = *(uint32 *)(g_pRDRAMu8 + dwPC+4+8);

    // Increment PC so that it points to the right place
    gDlistStack[gDlistStackPointer].pc += 16;

    uint32 dwXH     = (((gfx->words.w0)>>12)&0x0FFF)/4;
    uint32 dwYH     = (((gfx->words.w0)    )&0x0FFF)/4;
    uint32 tileno   = ((gfx->words.w1)>>24)&0x07;
    uint32 dwXL     = (((gfx->words.w1)>>12)&0x0FFF)/4;
    uint32 dwYL     = (((gfx->words.w1)    )&0x0FFF)/4;
    uint32 dwS      = (  dwCmd2>>16)&0xFFFF;
    uint32 dwT      = (  dwCmd2    )&0xFFFF;
    int  nDSDX     = (int)(short)((  dwCmd3>>16)&0xFFFF);
    int  nDTDY     = (int)(short)((  dwCmd3    )&0xFFFF);

    uint32 curTile = gRSP.curTile;
    ForceMainTextureIndex(tileno);
    
    float fS0 = (float)dwS / 32.0f;
    float fT0 = (float)dwT / 32.0f;

    float fDSDX = (float)nDSDX / 1024.0f;
    float fDTDY = (float)nDTDY / 1024.0f;

    uint32 cycletype = gRDP.otherMode.cycle_type;

    if (cycletype == CYCLE_TYPE_COPY)
    {
        fDSDX /= 4.0f;  // In copy mode 4 pixels are copied at once.
        dwXH++;
        dwYH++;
    }
    else if (cycletype == CYCLE_TYPE_FILL)
    {
        dwXH++;
        dwYH++;
    }

    float fS1 = fS0 + (fDSDX * (dwYH - dwYL));
    float fT1 = fT0 + (fDTDY * (dwXH - dwXL));
    
    LOG_UCODE("    Tile:%d (%d,%d) -> (%d,%d)",
        tileno, dwXL, dwYL, dwXH, dwYH);
    LOG_UCODE("    Tex:(%#5f,%#5f) -> (%#5f,%#5f) (DSDX:%#5f DTDY:%#5f)",
        fS0, fT0, fS1, fT1, fDSDX, fDTDY);
    LOG_UCODE("");

    float t0u0 = (fS0) * gRDP.tiles[tileno].fShiftScaleS-gRDP.tiles[tileno].sl;
    float t0v0 = (fT0) * gRDP.tiles[tileno].fShiftScaleT-gRDP.tiles[tileno].tl;
    float t0u1 = t0u0 + (fDSDX * (dwYH - dwYL))*gRDP.tiles[tileno].fShiftScaleS;
    float t0v1 = t0v0 + (fDTDY * (dwXH - dwXL))*gRDP.tiles[tileno].fShiftScaleT;

    CRender::g_pRender->TexRectFlip(dwXL, dwYL, dwXH, dwYH, t0u0, t0v0, t0u1, t0v1);
    status.dwNumTrisRendered += 2;

    if( status.bHandleN64RenderTexture )    g_pRenderTextureInfo->maxUsedHeight = std::max(g_pRenderTextureInfo->maxUsedHeight,int(dwYL+(dwXH-dwXL)));

    ForceMainTextureIndex(curTile);
}

/************************************************************************/
/*                                                                      */
/************************************************************************/
/*
 *  TMEM emulation
 *  There are 0x200's 64bits entry in TMEM
 *  Usually, textures are loaded into TMEM at 0x0, and TLUT is loaded at 0x100
 *  of course, the whole TMEM can be used by textures if TLUT is not used, and TLUT
 *  can be at other address of TMEM.
 *
 *  We don't want to emulate TMEM by creating a block of memory for TMEM and load
 *  everything into the block of memory, this will be slow.
 */
typedef struct TmemInfoEntry{
    uint32 start;
    uint32 length;
    uint32 rdramAddr;
    TmemInfoEntry* next;
} TmemInfoEntry;

const int tmenMaxEntry=20;
TmemInfoEntry tmenEntryBuffer[20]={{0}};
TmemInfoEntry *g_pTMEMInfo=NULL;
TmemInfoEntry *g_pTMEMFreeList=tmenEntryBuffer;

void TMEM_Init()
{
    g_pTMEMInfo=NULL;
    g_pTMEMFreeList=tmenEntryBuffer;
        int i;
    for( i=0; i<tmenMaxEntry; i++ )
    {
        tmenEntryBuffer[i].start=0;
        tmenEntryBuffer[i].length=0;
        tmenEntryBuffer[i].rdramAddr=0;
        tmenEntryBuffer[i].next = &(tmenEntryBuffer[i+1]);
    }
    tmenEntryBuffer[i-1].next = NULL;
}

void TMEM_SetBlock(uint32 tmemstart, uint32 length, uint32 rdramaddr)
{
    TmemInfoEntry *p=g_pTMEMInfo;

    if( p == NULL )
    {
        // Move an entry from freelist and link it to the header
        p = g_pTMEMFreeList;
        g_pTMEMFreeList = g_pTMEMFreeList->next;

        p->start = tmemstart;
        p->length = length;
        p->rdramAddr = rdramaddr;
        p->next = NULL;
    }
    else
    {
        while ( tmemstart > (p->start+p->length) )
        {
            if( p->next != NULL ) {
                p = p->next;
                continue;
            }
            else {
                break;
            }
        }

        if ( p->start == tmemstart ) 
        {
            // need to replace the block of 'p'
            // or append a new block depend the block lengths
            if( length == p->length )
            {
                p->rdramAddr = rdramaddr;
                return;
            }
            else if( length < p->length )
            {
                TmemInfoEntry *newentry = g_pTMEMFreeList;
                g_pTMEMFreeList = g_pTMEMFreeList->next;

                newentry->length = p->length - length;
                newentry->next = p->next;
                newentry->rdramAddr = p->rdramAddr + p->length;
                newentry->start = p->start + p->length;

                p->length = length;
                p->next = newentry;
                p->rdramAddr = rdramaddr;
            }
        }
        else if( p->start > tmemstart )
        {
            // p->start > tmemstart, need to insert the new block before 'p'
            TmemInfoEntry *newentry = g_pTMEMFreeList;
            g_pTMEMFreeList = g_pTMEMFreeList->next;

            if( length+tmemstart < p->start+p->length )
            {
                newentry->length = p->length - length;
                newentry->next = p->next;
                newentry->rdramAddr = p->rdramAddr + p->length;
                newentry->start = p->start + p->length;

                p->length = length;
                p->next = newentry;
                p->rdramAddr = rdramaddr;
                p->start = tmemstart;
            }
            else if( length+tmemstart == p->start+p->length )
            {

            }
        }
        else
        {
        }
    }
}

uint32 TMEM_GetRdramAddr(uint32 tmemstart, uint32 length)
{
    return 0;
}


/*
 *  New implementation of texture loading
 */

bool IsTmemFlagValid(uint32 tmemAddr)
{
    uint32 index = tmemAddr>>5;
    uint32 bitIndex = (tmemAddr&0x1F);
    return ((g_TmemFlag[index] & (1<<bitIndex))!=0);
}

uint32 GetValidTmemInfoIndex(uint32 tmemAddr)
{
    return 0;
    uint32 index = tmemAddr>>5;
    uint32 bitIndex = (tmemAddr&0x1F);

    if ((g_TmemFlag[index] & (1<<bitIndex))!=0 )    //This address is valid
        return tmemAddr;
    else
    {
        for( uint32 x=index+1; x != 0; x-- )
        {
            uint32 i = x - 1;
            if( g_TmemFlag[i] != 0 )
            {
                for( uint32 y=0x20; y != 0; y-- )
                {
                    uint32 j = y - 1;
                    if( (g_TmemFlag[i] & (1<<j)) != 0 )
                    {
                        return ((i<<5)+j);
                    }
                }
            }
        }
        TRACE0("Error, check me");
        return 0;
    }
}


void SetTmemFlag(uint32 tmemAddr, uint32 size)
{
    uint32 index = tmemAddr>>5;
    uint32 bitIndex = (tmemAddr&0x1F);

#ifdef DEBUGGER
    if( size > 0x200 )
    {
        DebuggerAppendMsg("Check me: tmemaddr=%X, size=%x", tmemAddr, size);
        size = 0x200-tmemAddr;
    }
#endif

    if( bitIndex == 0 )
    {
        uint32 i;
        for( i=0; i< (size>>5); i++ )
        {
            g_TmemFlag[index+i] = 0;
        }

        if( (size&0x1F) != 0 )
        {
            //ErrorMsg("Check me: tmemaddr=%X, size=%x", tmemAddr, size);
            g_TmemFlag[index+i] &= ~((1<<(size&0x1F))-1);
        }

        g_TmemFlag[index] |= 1;
    }
    else
    {
        if( bitIndex + size <= 0x1F )
        {
            uint32 val = g_TmemFlag[index];
            uint32 mask = (1<<(bitIndex))-1;
            mask |= ~((1<<(bitIndex + size))-1);
            val &= mask;
            val |= (1<<bitIndex);
            g_TmemFlag[index] = val;
        }
        else
        {
            //ErrorMsg("Check me: tmemaddr=%X, size=%x", tmemAddr, size);
            uint32 val = g_TmemFlag[index];
            uint32 mask = (1<<bitIndex)-1;
            val &= mask;
            val |= (1<<bitIndex);
            g_TmemFlag[index] = val;
            index++;
            size -= (0x20-bitIndex);

            uint32 i;
            for( i=0; i< (size>>5); i++ )
            {
                g_TmemFlag[index+i] = 0;
            }

            if( (size&0x1F) != 0 )
            {
                //ErrorMsg("Check me: tmemaddr=%X, size=%x", tmemAddr, size);
                g_TmemFlag[index+i] &= ~((1<<(size&0x1F))-1);
            }
        }
    }
}

