/******************************Module*Header**********************************\
*
*                           *******************
*                           * D3D SAMPLE CODE *
*                           *******************
*
* Module Name: d3dtxt.c
*
*  Content: D3D texture setup
*
* Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-2001 Microsoft Corporation.  All rights reserved.
\*****************************************************************************/

#include "glint.h"
#include "dma.h"
#include "chroma.h"
#include "tag.h"

//-----------------------------------------------------------------------------

// Some variables shared through this module (not globals)
// These are set up in _D3DChangeTextureP3RX.

// P3 has 16 texture map base address slots, numbered 0 to 15 hence ...
#define P3_TEX_MAP_MAX_LEVEL    15

typedef struct
{
    DWORD dwTex0MipBase;
    DWORD dwTex0MipMax;
    DWORD dwTex0ActMaxLevel; // Controlled by D3DTSS_MAXMIPLEVEL, default 0
    DWORD dwTex1MipBase;
    DWORD dwTex1MipMax;
    DWORD dwTex1ActMaxLevel; // Same for Texture 1
} P3_MIP_BASES;

#define TSSTATE(stageno,argno)        \
                        ( pContext->TextureStageState[stageno].m_dwVal[argno] )
#define TSSTATESELECT(stageno,argno)  \
                            ( TSSTATE(stageno,argno) & D3DTA_SELECTMASK )
#define TSSTATEINVMASK(stageno,argno) \
                            ( TSSTATE(stageno,argno) & ~D3DTA_COMPLEMENT )
#define TSSTATEALPHA(stageno,argno)   \
                            ( TSSTATE(stageno,argno)  & ~D3DTA_ALPHAREPLICATE )

#define IS_ALPHA_ARG 1
#define IS_COLOR_ARG 0


#if DX8_DDI
//-----------------------------------------------------------------------------
//
// __TXT_MapDX8toDX6TexFilter
//
// map DX8 enums into DX6(&7) texture filtering enums 
//
//-----------------------------------------------------------------------------
DWORD
__TXT_MapDX8toDX6TexFilter( DWORD dwStageState, DWORD dwValue )
{
    switch (dwStageState)
    {
    case D3DTSS_MAGFILTER:
        switch (dwValue)
        {
        case D3DTEXF_POINT            : return D3DTFG_POINT;
        case D3DTEXF_LINEAR           : return D3DTFG_LINEAR;
        case D3DTEXF_FLATCUBIC        : return D3DTFG_FLATCUBIC;
        case D3DTEXF_GAUSSIANCUBIC    : return D3DTFG_GAUSSIANCUBIC;
        case D3DTEXF_ANISOTROPIC      : return D3DTFG_ANISOTROPIC;
        }
        break;
    case D3DTSS_MINFILTER:
        switch (dwValue)
        {
        case D3DTEXF_POINT            : return D3DTFN_POINT;
        case D3DTEXF_LINEAR           : return D3DTFN_LINEAR;
        case D3DTEXF_FLATCUBIC        : return D3DTFN_ANISOTROPIC;
        }
        break;
    case D3DTSS_MIPFILTER:
        switch (dwValue)
        {
        case D3DTEXF_NONE             : return D3DTFP_NONE;
        case D3DTEXF_POINT            : return D3DTFP_POINT;
        case D3DTEXF_LINEAR           : return D3DTFP_LINEAR;
        }
        break;
    }
    return 0x0;
} // __TXT_MapDX8toDX6TexFilter
#endif // DX8_DDI

//-----------------------------------------------------------------------------
//
// _D3D_TXT_ParseTextureStageStates
//
// Parse the texture state stages command token and update our context state
//
// Note : bTranslateDX8FilterValueToDX6 will only be FALSE when it is called
//        from _D3D_SB_ExecuteStateSet if that state set's value has been 
//        changes by _D3D_SB_CaptureStateSet (Basically DX6 filter values are
//        stored in the state set directly, thus no need to translate them.)
//
//-----------------------------------------------------------------------------
void 
_D3D_TXT_ParseTextureStageStates(
    P3_D3DCONTEXT* pContext, 
    D3DHAL_DP2TEXTURESTAGESTATE *pState, 
    DWORD dwCount,
    BOOL bTranslateDX8FilterValueToDX6)
{
    DWORD i;
    DWORD dwStage, dwState, dwValue;
    
    DISPDBG((DBGLVL,"*** In _D3D_TXT_ParseTextureStageStates"));

    for (i = 0; i < dwCount; i++, pState++)
    {
        dwStage = pState->wStage;
        dwState = pState->TSState;
        dwValue = pState->dwValue;
      
        // check for range before continuing
        if ( (dwStage < D3DHAL_TSS_MAXSTAGES) &&
             (dwState < D3DTSS_MAX))
        {

#if DX7_D3DSTATEBLOCKS 
            if (pContext->bStateRecMode)
            {
                // Record this texture stage state into the 
                //current state set being recorded 
                _D3D_SB_RecordStateSetTSS(pContext, dwStage, dwState, dwValue);

                // skip any further processing and go to the next TSS
                continue;
            }
#endif //DX7_D3DSTATEBLOCKS       

#if DX7_TEXMANAGEMENT
            if ((D3DTSS_TEXTUREMAP == dwState) && (0 != dwValue))
            {
                P3_SURF_INTERNAL* pTexture;

                pTexture = GetSurfaceFromHandle(pContext, dwValue);

                // If this is a valid managed texture
                if (CHECK_SURF_INTERNAL_AND_DDSURFACE_VALIDITY(pTexture) &&
                    (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)) 
                {
                    // Update stats
                    _D3D_TM_STAT_Inc_NumTexturesUsed(pContext);
                    _D3D_TM_STAT_Inc_NumUsedTexInVid(pContext, pTexture);
                }
            }
#endif // DX7_TEXMANAGEMENT  

            DISPDBG((DBGLVL,"  Stage = %d, State = 0x%x, Value = 0x%x", 
                               dwStage, dwState, dwValue));

            // Special case a texture handle change and the address update
            switch ( dwState )
            {
            case D3DTSS_TEXTUREMAP:
                DISPDBG((DBGLVL,"  D3DTSS_TEXTUREMAP: Handle=0x%x", dwValue));

                if (pContext->TextureStageState[dwStage].m_dwVal[dwState] != 
                                                                       dwValue)
                {
                    pContext->TextureStageState[dwStage].m_dwVal[dwState] = 
                                                                        dwValue;
                    DIRTY_TEXTURE(pContext);
                }
                break;

            case D3DTSS_ADDRESS:
                DISPDBG((DBGLVL,"  D3DTSS_ADDRESS"));
                // map single set ADDRESS to both U and V controls
                pContext->TextureStageState[dwStage].m_dwVal[D3DTSS_ADDRESSU] = 
                pContext->TextureStageState[dwStage].m_dwVal[D3DTSS_ADDRESSV] = 
                pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;

                DIRTY_TEXTURE(pContext);
                break;
                
            case D3DTSS_COLOROP:
            case D3DTSS_ALPHAOP:
            case D3DTSS_COLORARG1:
            case D3DTSS_COLORARG2:
            case D3DTSS_ALPHAARG1:
            case D3DTSS_ALPHAARG2:
                pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                pContext->Flags &= ~SURFACE_MODULATE;
                DIRTY_TEXTURESTAGEBLEND(pContext);
                DIRTY_TEXTURE(pContext);
                break;
                
            case D3DTSS_TEXCOORDINDEX:
                DISPDBG((DBGLVL,"  D3DTSS_TEXCOORDINDEX: stage %d, value %d", 
                            dwStage, dwValue ));
                pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                
                // Update the offsets to the texture coordinates                                         
                // NOTE: The texture coordinate index can contain various flags
                // in addition to the actual value. These flags are:
                //     D3DTSS_TCI_PASSTHRU (default - resolves to zero)
                //     D3DTSS_TCI_CAMERASPACENORMAL 
                //     D3DTSS_TCI_CAMERASPACEPOSITION 
                //     D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR 
                // and are used for texture coordinate generation.
                //
                // These flags are not relevant when considering the offset of
                // texture coordinates in the vertex stream. These flags appear
                // in the high word of the index value DWORD. Only the low word
                // contains actual index data. Therefore, we will mask of the
                // low word when looking up the offset table for this texture
                // coordinate index.
                pContext->FVFData.dwTexOffset[dwStage] = 
                    pContext->FVFData.dwTexCoordOffset[dwValue & 0x0000FFFFul];
                    
                DIRTY_TEXTURE(pContext);
                break;
                
            case D3DTSS_MIPMAPLODBIAS:
                DISPDBG((DBGLVL,"  D3DTSS_MIPMAPLODBIAS: stage %d, value %d", 
                            dwStage, dwValue ));
                pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                DIRTY_TEXTURE(pContext);
                break;
                
            case D3DTSS_MAGFILTER:        
            case D3DTSS_MINFILTER:
            case D3DTSS_MIPFILTER:
#if DX8_DDI        
                if((!IS_DX7_OR_EARLIER_APP(pContext)) && 
                   bTranslateDX8FilterValueToDX6)
                {
                    // filtering values are somewhat different in DX8 
                    // so translate them before using them.
                    dwValue = __TXT_MapDX8toDX6TexFilter(dwState, dwValue);
                }
#endif DX8_DDI            
                
                pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                DIRTY_TEXTURE(pContext);        
                break;
                
            case D3DTSS_MAXMIPLEVEL:
                DISPDBG((DBGLVL,"  D3DTSS_MAXMIPLEVEL: stage %d, value %d", 
                            dwStage, dwValue ));
                pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                DIRTY_TEXTURE(pContext);
                break;     
                
            default:
                pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                DIRTY_TEXTURE(pContext);
                break;
            } // switch
        } 
        else
        {
            DISPDBG((WRNLVL,"Out of range stage/state %d %d",dwStage,dwState));
        }// if
    } // for
} // _D3D_TXT_ParseTextureStageStates



//-----------------------------------------------------------------------------
//
// SETARG
//
// dwArg = the argument.
// num = argument number (1 or 2).
// bIsAlpha = TRUE if this is the alpha channel, 
//            FALSE if this is the colour channel.
// iD3DStage = D3D stage number.
// iChipStageNo = chip stage number (should only be 0 or 1 on P3)
//
//-----------------------------------------------------------------------------
void
SETARG(
    P3_D3DCONTEXT *pContext, 
    struct TextureCompositeRGBAMode *pMode,
    DWORD dwArg, 
    DWORD num,
    BOOL bIsAlpha,    
    DWORD iD3DStage, 
    DWORD iChipStageNo)    
{                       
    BOOL bSetArgToggleInvert;
    DWORD dwArgValue, dwInvertArgValue;
    BOOL bArgValueAssigned = FALSE,
         bInvertArgValueAssigned;

    bSetArgToggleInvert = FALSE;                            
    switch (dwArg & D3DTA_SELECTMASK)                       
    {                                                       
        case D3DTA_TEXTURE:                                 
            if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
            {                                                   
                dwArgValue = ( pContext->iStageTex[iD3DStage] == 0 ) ?   
                                                         P3RX_TEXCOMP_T0A :   
                                                         P3RX_TEXCOMP_T1A;  
                bArgValueAssigned = TRUE;
                DISPDBG((DBGLVL,"  Tex%dA", pContext->iStageTex[iD3DStage] ));     
            }                                                   
            else                                                
            {                                                   
                dwArgValue = ( pContext->iStageTex[iD3DStage] == 0 ) ?   
                                                         P3RX_TEXCOMP_T0C :   
                                                         P3RX_TEXCOMP_T1C; 
                bArgValueAssigned = TRUE;                                                         
                DISPDBG((DBGLVL,"  Tex%dC", pContext->iStageTex[iD3DStage] ));   
            }                                                   
            break;                
            
        case D3DTA_DIFFUSE:                                     
            if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
            {                                                   
                dwArgValue = P3RX_TEXCOMP_CA;      
                bArgValueAssigned = TRUE;
                DISPDBG((DBGLVL,"  DiffA" ));                        
            }                                                   
            else                                                
            {                                                   
                dwArgValue = P3RX_TEXCOMP_CC;              
                bArgValueAssigned = TRUE;
                DISPDBG((DBGLVL,"  DiffC" ));                        
            }                                                   
            break;        
            
        case D3DTA_CURRENT:                                     
            // Cope with a "current" argument in texcomp0 
            if ( iChipStageNo == 0 )                            
            {                                                                           
                // This is texcomp0
                if ( pContext->bBumpmapEnabled )                                        
                {                                                                       
                    // Emboss bumpmapping is on
                    if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))                   
                    {                                                                   
                        // This is the alpha-channel, where the D3D stages 0 & 1
                        // should have put the heightfield info.
                        dwArgValue = P3RX_TEXCOMP_HEIGHTA;                         
                        bArgValueAssigned = TRUE;
                        DISPDBG((DBGLVL,"  BumpA" ));                                        
                        // And cope with inverted bumpmaps.
                        bSetArgToggleInvert = pContext->bBumpmapInverted;               
                    }                                                                   
                    else                                                                
                    {                                                                   
                        // Colour channel - this will hold the diffuse colour.
                        dwArgValue = P3RX_TEXCOMP_CC;                              
                        bArgValueAssigned = TRUE;
                        DISPDBG((DBGLVL,"  DiffC" ));                                        
                    }                                                                   
                }                                                                       
                else                                                    
                {                                                       
                    // Embossing is off - default to diffuse.
                    if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
                    {                                                   
                        dwArgValue = P3RX_TEXCOMP_CA;              
                        bArgValueAssigned = TRUE;
                        DISPDBG((DBGLVL,"  DiffA" ));                        
                    }                                                   
                    else                                                
                    {                                                   
                        dwArgValue = P3RX_TEXCOMP_CC;              
                        bArgValueAssigned = TRUE;
                        DISPDBG((DBGLVL,"  DiffC" ));                        
                    }                                                   
                }                                                       
            }                                                           
            else                                                        
            {                                                           
                // texcomp stage 1
                if ( pContext->bStage0DotProduct )                      
                {                                                       
                    // Need to take the dotproduct sum result,
                    // even in the alpha channel, according to the docs.
                    dwArgValue = P3RX_TEXCOMP_SUM;                 
                    bArgValueAssigned = TRUE;
                }                                                       
                else                                                    
                {                                                       
                    if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
                    {                                                   
                        dwArgValue = P3RX_TEXCOMP_OA;              
                        bArgValueAssigned = TRUE;
                        DISPDBG((DBGLVL,"  CurrA" ));                        
                    }                                                   
                    else                                                
                    {                                                   
                        dwArgValue = P3RX_TEXCOMP_OC;              
                        bArgValueAssigned = TRUE;
                        DISPDBG((DBGLVL,"  CurrC" ));                        
                    }                                                   
                }                                                       
            }                                                           
            break;      
            
        case D3DTA_TFACTOR:                                     
            if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
            {                                                   
                dwArgValue = P3RX_TEXCOMP_FA;              
                bArgValueAssigned = TRUE;
                DISPDBG((DBGLVL,"  TfactA" ));                       
            }                                                   
            else                                                
            {                                                   
                dwArgValue = P3RX_TEXCOMP_FC;              
                bArgValueAssigned = TRUE;
                DISPDBG((DBGLVL,"  TfactC" ));                       
            }                                                   
            break;                                              
            
        default:                                                
            if ( bIsAlpha )                                     
            {                                                   
                DISPDBG((ERRLVL,"ERROR: Invalid AlphaArgument"));
                SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_ARG );
            }
            else
            {
                DISPDBG((ERRLVL,"ERROR: Invalid ColorArgument"));
                SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_ARG );
            }
            break;
    } // switch
    
    if ( ( (dwArg & D3DTA_COMPLEMENT) == 0 ) == bSetArgToggleInvert )
    {                                                           
        dwInvertArgValue= __PERMEDIA_ENABLE;   
        bInvertArgValueAssigned = TRUE;
        DISPDBG((DBGLVL,"    inverted" ));                           
    }                                                           
    else                                                        
    {                                                           
        dwInvertArgValue = __PERMEDIA_DISABLE;   
        bInvertArgValueAssigned = TRUE;        
    }                                                           
                                                                
    // Set up the I input for MODULATExxx_ADDxxx modes.
    if ( num == 1 )                                             
    {                                                           
        switch (dwArg & D3DTA_SELECTMASK)                       
        {                                                       
            case D3DTA_TEXTURE:                                 
                pMode->I = ( pContext->iStageTex[iD3DStage] == 0 ) ?  
                                                P3RX_TEXCOMP_I_T0A :  
                                                P3RX_TEXCOMP_I_T1A;   
                DISPDBG((DBGLVL,"  I: Tex%dA", pContext->iStageTex[iD3DStage] ));    
                break;    
                
            case D3DTA_DIFFUSE:                                     
                pMode->I = P3RX_TEXCOMP_I_CA;                       
                DISPDBG((DBGLVL,"  I: DiffA" ));                         
                break;                                              
                
            case D3DTA_CURRENT:                                     
                if ( iChipStageNo == 0 )                            
                {                                                   
                    if ( pContext->bBumpmapEnabled )                
                    {                                               
                        // Bumpmapping mode. 
                        pMode->I = P3RX_TEXCOMP_I_HA;               
                        DISPDBG((DBGLVL,"  I: BumpA" ));                 
                    }                                               
                    else                                            
                    {                                               
                        pMode->I = P3RX_TEXCOMP_I_CA;               
                        DISPDBG((DBGLVL,"  I: DiffA" ));                 
                    }                                               
                }                                                   
                else                                                
                {                                                   
                    pMode->I = P3RX_TEXCOMP_I_OA;                   
                    DISPDBG((DBGLVL,"  I: CurrA" ));                     
                }                                                   
                break;                                              
                
            case D3DTA_TFACTOR:                                     
                pMode->I = P3RX_TEXCOMP_I_FA;                       
                DISPDBG((DBGLVL,"  I: TfactA" ));                        
                break;                                              
                
            default:                                                
                if ( bIsAlpha )                                     
                {                                                   
                    DISPDBG((ERRLVL,"ERROR: Invalid AlphaArgument"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_ARG );    
                }                                                   
                else                                                
                {                                                   
                    DISPDBG((ERRLVL,"ERROR: Invalid ColorArgument"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_ARG );    
                }                                                   
                break;                                              
        } // switch
        
        if ( ( (dwArg & D3DTA_COMPLEMENT) == 0 ) == bSetArgToggleInvert )
        {                                                           
            pMode->InvertI = __PERMEDIA_ENABLE;                        
        }                                                           
        else                                                        
        {                                                           
            pMode->InvertI = __PERMEDIA_DISABLE;                       
        }                                                           
    } // if ( num == 1 )          

    if (bArgValueAssigned)
    {
        if (num == 1)
        {
            pMode->Arg1 = dwArgValue;
        }
        else
        {
            pMode->Arg2 = dwArgValue;        
        }
    }

    if (bInvertArgValueAssigned)
    {
        if (num == 1)
        {
            pMode->InvertArg1 = dwInvertArgValue;
        }
        else
        {
            pMode->InvertArg2 = dwInvertArgValue;        
        }
    }    
} // SETARG

//-----------------------------------------------------------------------------
//
// SETTAARG_ALPHA
//
// TexApp blend mode for the alpha channel.
//
//-----------------------------------------------------------------------------
void
SETTAARG_ALPHA(
    P3_D3DCONTEXT *pContext, 
    struct TextureApplicationMode *pMode,
    DWORD dwArg, 
    DWORD num) 
{                                                          
    switch (dwArg & D3DTA_SELECTMASK)                               
    {                                                               
        case D3DTA_TEXTURE:                                         
            DISPDBG((ERRLVL,"ERROR: Invalid TA AlphaArgument"));
            SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_ARG_HERE );    
            break;                                                  
        case D3DTA_DIFFUSE:                                         
            if ( (num) == 1 )                                       
            {                                                       
                pMode->AlphaA = P3RX_TEXAPP_A_CA;          
                DISPDBG((DBGLVL,"  DiffA" ));                        
            }                                                       
            else                                                    
            {                                                       
                DISPDBG((ERRLVL,"ERROR: Invalid TA AlphaArgument"));
                SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_ARG_HERE );
            }                                                       
            break;                                                  
        case D3DTA_CURRENT:                                         
            if ( (num) == 2 )                                       
            {                                                       
                pMode->AlphaB = P3RX_TEXAPP_B_TA;          
                DISPDBG((DBGLVL,"  CurrA" ));                        
            }                                                       
            else                                                    
            {                                                       
                // Can't do
                DISPDBG((ERRLVL,"ERROR: Invalid TA AlphaArgument"));
                SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_ARG_HERE );
            }                                                       
            break;                                                  
        case D3DTA_TFACTOR:                                         
            if ( (num) == 1 )                                       
            {                                                       
                pMode->AlphaA = P3RX_TEXAPP_A_KA;          
                DISPDBG((DBGLVL,"  TfactA" ));                       
            }                                                       
            else                                                    
            {                        
                if ( (num) != 2)
                {
                    DISPDBG((ERRLVL," ** SETTAARG: num must be 1 or 2"));
                }
                pMode->AlphaB = P3RX_TEXAPP_B_KA;          
                DISPDBG((DBGLVL,"  TfactA" ));                       
            }                                                       
            break;                                                  
        default:                                                    
            DISPDBG((ERRLVL,"ERROR: Unknown TA AlphaArgument"));
            SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_ARG );        
            break;                                                  
    }         
    
    if ( (dwArg & D3DTA_COMPLEMENT) != 0 )                          
    {                                                               
        // Can't do COMPLEMENT on the args.
        DISPDBG((ERRLVL,"ERROR: Can't do COMPLEMENT in TA unit"));
        SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_ARG_HERE );            
    }                                                               
} // SETTAARG_ALPHA

//-----------------------------------------------------------------------------
//
// SETTAARG_COLOR
//
// TexApp blend mode for the color channel.
//
//-----------------------------------------------------------------------------
void 
SETTAARG_COLOR(
    P3_D3DCONTEXT *pContext, 
    struct TextureApplicationMode *pMode,
    DWORD dwArg, 
    DWORD num) 
{                                                                   
    switch (dwArg & D3DTA_SELECTMASK)                               
    {                                                               
            DISPDBG((ERRLVL,"ERROR: Invalid TA ColorArgument"));
            SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_ARG_HERE );    
            break;                                                  
        case D3DTA_DIFFUSE:                                         
            if ( (num) == 1 )                                       
            {                                                       
                if ( (dwArg & D3DTA_ALPHAREPLICATE) != 0 )      
                {                                               
                    pMode->ColorA = P3RX_TEXAPP_A_CA;      
                    DISPDBG((DBGLVL,"  DiffA" ));                    
                }                                               
                else                                            
                {                                               
                    pMode->ColorA = P3RX_TEXAPP_A_CC;      
                    DISPDBG((DBGLVL,"  DiffC" ));                    
                }                                               
                // Set up the I input for MODULATExxx_ADDxxx modes
                pMode->ColorI = P3RX_TEXAPP_I_CA;          
                DISPDBG((DBGLVL,"  I: DiffA" ));                     
            }                                                       
            else                                                    
            {                                                       
                DISPDBG((ERRLVL,"ERROR: Invalid TA ColorArgument"));
                SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_ARG_HERE );
            }                                                       
            break;                                                  
        case D3DTA_CURRENT:                                         
            if ( (num) == 2 )                                       
            {                                                       
                if (dwArg & D3DTA_ALPHAREPLICATE)   
                {                                                   
                    pMode->ColorB = P3RX_TEXAPP_B_TA;          
                    DISPDBG((DBGLVL,"  CurrA" ));                        
                }                                                   
                else                                                
                {                                                   
                    pMode->ColorB = P3RX_TEXAPP_B_TC;          
                    DISPDBG((DBGLVL,"  CurrC" ));                        
                }                                                   
            }                                                       
            else                                                    
            {                                                       
                // Can't do.
                DISPDBG((ERRLVL,"ERROR: Invalid TA ColorArgument"));
                SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_ARG_HERE );
            }                                                       
            break;                                                  
        case D3DTA_TFACTOR:                                         
            if ( (num) == 1 )                                       
            {                                                       
                if ( (dwArg & D3DTA_ALPHAREPLICATE) != 0 )      
                {                                               
                    pMode->ColorA = P3RX_TEXAPP_A_KA;      
                    DISPDBG((DBGLVL,"  TfactA" ));                   
                }                                               
                else                                            
                {                                               
                    pMode->ColorA = P3RX_TEXAPP_A_KC;      
                    DISPDBG((DBGLVL,"  TfactC" ));                   
                }                                               
                // Set up the I input for MODULATExxx_ADDxxx modes. 
                pMode->ColorI = P3RX_TEXAPP_I_KA;          
                DISPDBG((DBGLVL,"  I: TfactA" ));                    
            }                                                       
            else                                                    
            {                           
                if ( (num) != 2)
                {
                    DISPDBG((ERRLVL," ** SETTAARG: num must be 1 or 2"));    
                }
                
                if (dwArg & D3DTA_ALPHAREPLICATE)   
                {                                                   
                    pMode->ColorB = P3RX_TEXAPP_B_KA;          
                    DISPDBG((DBGLVL,"  TfactA" ));                       
                }                                                   
                else                                                
                {                                                   
                    pMode->ColorB = P3RX_TEXAPP_B_KC;          
                    DISPDBG((DBGLVL,"  TfactC" ));                       
                }                                                   
            }                                                       
            break;                                                  
        default:                                                    
            DISPDBG((ERRLVL,"ERROR: Unknown TA ColorArgument"));
            SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_ARG );        
            break;                                                  
    }                                                               
    if ( (dwArg & D3DTA_COMPLEMENT) != 0 )                          
    {                                                               
        // Can't do COMPLEMENT on the args.
        DISPDBG((ERRLVL,"ERROR: Can't do COMPLEMENT in TA unit"));
        SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_ARG_HERE );            
    }                                                               
} // SETTAARG_COLOR

//-----------------------------------------------------------------------------
//
// SETOP
//
// Note - SETOP must be done after SETARG for DISABLE to work.
//
//-----------------------------------------------------------------------------
void 
SETOP(
    P3_D3DCONTEXT *pContext, 
    struct TextureCompositeRGBAMode* pMode, 
    DWORD dwOperation, 
    DWORD iD3DStage, 
    DWORD iChipStageNo, 
    BOOL bIsAlpha)
{                                                                   
    pMode->Enable = __PERMEDIA_ENABLE;                                 
    pMode->Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;                
    pMode->InvertI = __PERMEDIA_DISABLE;                               
    pMode->A = P3RX_TEXCOMP_ARG1;                                   
    pMode->B = P3RX_TEXCOMP_ARG2;                                   
    switch (dwOperation)                                            
    {                                                               
        case D3DTOP_DISABLE:                                        
            if ( bIsAlpha )                                         
            {                                                       
                // Just pass through "current"
                pMode->Operation = P3RX_TEXCOMP_OPERATION_PASS_A;   
                if ( iChipStageNo == 0 )                            
                {                                                   
                    if ( pContext->bBumpmapEnabled )                
                    {                                               
                        // Embossing is on.
                        pMode->Arg1 = P3RX_TEXCOMP_HEIGHTA;    
                    }                                               
                    else                                            
                    {                                               
                        // Current = diffuse in stage0.
                        pMode->Arg1 = P3RX_TEXCOMP_CA;         
                    }                                               
                }                                                   
                else                                                
                {                                                   
                    if ( pContext->bStage0DotProduct )              
                    {                                               
                        pMode->Arg1 = P3RX_TEXCOMP_SUM;        
                    }                                               
                    else                                            
                    {                                               
                        pMode->Arg1 = P3RX_TEXCOMP_OA;         
                    }                                               
                }                                                   
            }                                                       
            else                                                    
            {                                                       
                DISPDBG((ERRLVL,"SETOP: Colour op was DISABLE"
                                     " - should never have got here."));
            }                                                       
            break;                                                  
            
        case D3DTOP_SELECTARG1:                                     
            DISPDBG((DBGLVL,"  D3DTOP_SELECTARG1"));                     
            pMode->Operation = P3RX_TEXCOMP_OPERATION_PASS_A;       
            break;                                                  
            
        case D3DTOP_SELECTARG2:                                     
            DISPDBG((DBGLVL,"  D3DTOP_SELECTARG2"));                     
            pMode->Operation = P3RX_TEXCOMP_OPERATION_PASS_A; // No Pass B  
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            break;                                                  
            
        case D3DTOP_MODULATE:                                       
            DISPDBG((DBGLVL,"  D3DTOP_MODULATE"));                       
            pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;  
            break;                                                  
            
        case D3DTOP_MODULATE2X:                                     
            DISPDBG((DBGLVL,"  D3DTOP_MODULATE2X"));                     
            pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;  
            pMode->Scale = P3RX_TEXCOMP_OPERATION_SCALE_TWO;        
            break;                                                  
            
        case D3DTOP_MODULATE4X:                                     
            DISPDBG((DBGLVL,"  D3DTOP_MODULATE4X"));                     
            pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;  
            pMode->Scale = P3RX_TEXCOMP_OPERATION_SCALE_FOUR;       
            break;                                                  
            
        case D3DTOP_ADD:                                            
            DISPDBG((DBGLVL,"  D3DTOP_ADD"));                            
            pMode->Operation = P3RX_TEXCOMP_OPERATION_ADD_AB;       
            break;                                                  
            
        case D3DTOP_ADDSIGNED:                                      
            DISPDBG((DBGLVL,"  D3DTOP_ADDSIGNED"));                      
            pMode->Operation = P3RX_TEXCOMP_OPERATION_ADDSIGNED_AB; 
            break;                                                  
            
        case D3DTOP_ADDSIGNED2X:                                    
            DISPDBG((DBGLVL,"  D3DTOP_ADDSIGNED2X"));                    
            pMode->Operation = P3RX_TEXCOMP_OPERATION_ADDSIGNED_AB; 
            pMode->Scale = P3RX_TEXCOMP_OPERATION_SCALE_TWO;        
            break;                                                  
            
        case D3DTOP_SUBTRACT:                                       
            DISPDBG((DBGLVL,"  D3DTOP_SUBTRACT"));                       
            pMode->Operation = P3RX_TEXCOMP_OPERATION_SUBTRACT_AB;  
            break;                                                  
            
        case D3DTOP_ADDSMOOTH:                                      
            DISPDBG((DBGLVL,"  D3DTOP_ADDSMOOTH"));                      
            pMode->Operation = P3RX_TEXCOMP_OPERATION_ADD_AB_SUB_MODULATE_AB;
            break;                                                  
            
        case D3DTOP_BLENDDIFFUSEALPHA:                              
            DISPDBG((DBGLVL,"  D3DTOP_BLENDDIFFUSEALPHA"));              
            pMode->Operation = P3RX_TEXCOMP_OPERATION_LERP_ABI;     
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            pMode->B = P3RX_TEXCOMP_ARG1;                           
            pMode->I = P3RX_TEXCOMP_I_CA;                           
            break;                                                  
            
        case D3DTOP_BLENDTEXTUREALPHA:                              
            DISPDBG((DBGLVL,"  D3DTOP_BLENDTEXTUREALPHA"));              
            pMode->Operation = P3RX_TEXCOMP_OPERATION_LERP_ABI;     
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            pMode->B = P3RX_TEXCOMP_ARG1;                           
            pMode->I = ( pContext->iStageTex[iD3DStage] == 0 ) ? 
                                                        P3RX_TEXCOMP_I_T0A : 
                                                        P3RX_TEXCOMP_I_T1A; 
            DISPDBG((DBGLVL,"    alpha: Tex%dA", pContext->iStageTex[iD3DStage] ));  
            break;                                                  
            
        case D3DTOP_BLENDFACTORALPHA:                               
            DISPDBG((DBGLVL,"  D3DTOP_BLENDFACTORALPHA"));               
            pMode->Operation = P3RX_TEXCOMP_OPERATION_LERP_ABI;     
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            pMode->B = P3RX_TEXCOMP_ARG1;                           
            pMode->I = P3RX_TEXCOMP_I_FA;                           
            break;                                                  
            
        case D3DTOP_BLENDCURRENTALPHA:                              
            DISPDBG((DBGLVL,"  D3DTOP_BLENDCURRENTALPHA"));              
            pMode->Operation = P3RX_TEXCOMP_OPERATION_LERP_ABI;     
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            pMode->B = P3RX_TEXCOMP_ARG1;                           
            pMode->I = P3RX_TEXCOMP_I_OA;                           
            break;                                                  
            
        case D3DTOP_BLENDTEXTUREALPHAPM:                            
            DISPDBG((DBGLVL,"  D3DTOP_BLENDTEXTUREALPHAPM"));            
            pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B;    
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            pMode->B = P3RX_TEXCOMP_ARG1;                           
            pMode->I = ( pContext->iStageTex[iD3DStage] == 0 ) ? 
                                                           P3RX_TEXCOMP_I_T0A : 
                                                           P3RX_TEXCOMP_I_T1A; 
            DISPDBG((DBGLVL,"    alpha: Tex%dA", pContext->iStageTex[iD3DStage] ));  
            pMode->InvertI = __PERMEDIA_ENABLE;                        
            break;                                                  
            
        case D3DTOP_PREMODULATE:                                                                            
            DISPDBG((DBGLVL,"  D3DTOP_PREMODULATE"));                                                            
            // result = current_tex * next_stage_tex - ignore arguments.
            if ( ( pContext->iStageTex[iD3DStage] != -1 ) && 
                 ( pContext->iStageTex[iD3DStage+1] != -1 ) )                       
            {                                                                                               
                pMode->Arg1 = ( pContext->iStageTex[iD3DStage] == 0 ) ? 
                                                            P3RX_TEXCOMP_T0C : 
                                                            P3RX_TEXCOMP_T1C;            
                DISPDBG((DBGLVL,"    Arg1: Tex%d", pContext->iStageTex[iD3DStage] ));                                        
                pMode->Arg2 = ( pContext->iStageTex[iD3DStage+1] == 0 ) ? 
                                                            P3RX_TEXCOMP_T0C : 
                                                            P3RX_TEXCOMP_T1C;      
                DISPDBG((DBGLVL,"    Arg2: Tex%d", pContext->iStageTex[iD3DStage+1] ));                                  
            }                                                                                               
            else                                                                                            
            {                                                                                               
                // Not enough textures
                DISPDBG((ERRLVL,"** SETOP: PREMODULATE didn't have two "
                                     "textures to play with."));
                if ( bIsAlpha )                                                                             
                {                                                                                           
                    SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_OP_HERE );                                         
                }                                                                                           
                else                                                                                        
                {                                                                                           
                    SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_OP_HERE );                                         
                }                                                                                           
                pMode->Arg1 = P3RX_TEXCOMP_CC;                                                              
                pMode->Arg2 = P3RX_TEXCOMP_CC;                                                              
            }                                                                                               
            pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;                                          
            pMode->A = P3RX_TEXCOMP_ARG2;                                                                   
            pMode->B = P3RX_TEXCOMP_ARG1;                                                                   
            break;                                                                                          
            
        case D3DTOP_MODULATEALPHA_ADDCOLOR:                         
            DISPDBG((DBGLVL,"  D3DTOP_MODULATEALPHA_ADDCOLOR"));         
            pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B; 
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            pMode->B = P3RX_TEXCOMP_ARG1;                           
            // I input set up by SETARG
            break;                                                  
            
        case D3DTOP_MODULATECOLOR_ADDALPHA:                         
            DISPDBG((DBGLVL,"  D3DTOP_MODULATECOLOR_ADDALPHA"));         
            pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB_ADD_I; 
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            pMode->B = P3RX_TEXCOMP_ARG1;                           
            // I input set up by SETARG
            break;                                                  
            
        case D3DTOP_MODULATEINVALPHA_ADDCOLOR:                      
            DISPDBG((DBGLVL,"  D3DTOP_MODULATEINVALPHA_ADDCOLOR"));      
            pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B; 
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            pMode->B = P3RX_TEXCOMP_ARG1;                           
            // I input set up by SETARG
            pMode->InvertI = 1 - pMode->InvertI;                    
            break;                                                  
            
        case D3DTOP_MODULATEINVCOLOR_ADDALPHA:                      
            DISPDBG((DBGLVL,"  D3DTOP_MODULATEINVCOLOR_ADDALPHA"));      
            pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB_ADD_I; 
            pMode->A = P3RX_TEXCOMP_ARG2;                           
            pMode->B = P3RX_TEXCOMP_ARG1;                           
            pMode->InvertArg1 = 1 - pMode->InvertArg1;              
            // I input set up by SETARG
            break;                                                  
            
        case D3DTOP_DOTPRODUCT3:                                    
            DISPDBG((DBGLVL,"  D3DTOP_DOTPRODUCT3"));                    
            if ( iChipStageNo == 0 )                                
            {                                                       
                pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_SIGNED_AB;   
                // Signal that the special input to stage 1 is needed.
                pContext->bStage0DotProduct = TRUE;                         
            }                                                       
            else                                                    
            {                                                       
                // Can't do stage 1 dotproduct. Fail.
                DISPDBG((ERRLVL,"** SETOP: Can't do DOTPRODUCT3 in second stage."));
                if ( bIsAlpha )                                     
                {                                                   
                    SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_OP_HERE ); 
                }                                                   
                else                                                
                {                                                   
                    SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_OP_HERE ); 
                }                                                   
                pMode->Operation = P3RX_TEXCOMP_OPERATION_PASS_A;   
            }                                                       
            break;                                                  
            
        case D3DTOP_BUMPENVMAP:                                     
        case D3DTOP_BUMPENVMAPLUMINANCE:                            
            DISPDBG((ERRLVL,"** SETOP: Unsupported operation.")); 
            if ( bIsAlpha )                                         
            {                                                       
                SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_ALPHA_OP );       
            }                                                       
            else                                                    
            {                                                       
                SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_COLOR_OP );       
            }                                                       
            break;                                                  
            
        default:                                                    
            DISPDBG((ERRLVL,"** SETOP: Unknown operation."));
            if ( bIsAlpha )                                         
            {                                                       
                SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_OP );         
            }                                                       
            else                                                    
            {                                                       
                SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_OP );         
            }                                                       
            break;                                                  
    } // switch                                                              
} // SETOP

//-----------------------------------------------------------------------------
//
// SETTAOP
//
// Must be done after SETTAARG to set up DISABLE properly.
//
//-----------------------------------------------------------------------------
void 
SETTAOP(
    P3_D3DCONTEXT *pContext, 
    struct TextureApplicationMode* pMode, 
    DWORD dwOperand, 
    BOOL bIsAlpha,    
    DWORD iD3DStage, 
    DWORD iChipStageNo)
{                     
    DWORD dwInvertI, dwOperation, dwI = 0, dwA = 0;
    BOOL bOperation = FALSE,
         bI         = FALSE,
         bA         = FALSE;

    pMode->Enable = __PERMEDIA_ENABLE;                                         
    dwInvertI = __PERMEDIA_DISABLE;                             
    switch (dwOperand)                                                    
    {                                                                       
        case D3DTOP_DISABLE:                                                
            if ( bIsAlpha )                                                 
            {                                                               
                // Just pass through "current"
                dwOperation = P3RX_TEXAPP_OPERATION_PASS_A;  
                bOperation = TRUE;
                dwA = P3RX_TEXAPP_A_CA;                
                bA = TRUE;
            }                                                               
            else                                                            
            {                                                               
                DISPDBG((ERRLVL,"SETTAOP: Colour op was DISABLE "      
                             " should never have got here."));
            }                                                               
            break;                                                          
        case D3DTOP_SELECTARG1:                                             
            DISPDBG((DBGLVL,"  D3DTOP_SELECTARG1"));                             
            dwOperation = P3RX_TEXAPP_OPERATION_PASS_A;   
            bOperation = TRUE;
            break;                                                          
        case D3DTOP_SELECTARG2:                                             
            DISPDBG((DBGLVL,"  D3DTOP_SELECTARG2"));                             
            dwOperation = P3RX_TEXAPP_OPERATION_PASS_B;   
            bOperation = TRUE;
            break;                                                          
        case D3DTOP_MODULATE:                                               
            DISPDBG((DBGLVL,"  D3DTOP_MODULATE"));                               
            dwOperation = P3RX_TEXAPP_OPERATION_MODULATE_AB; 
            bOperation = TRUE;
            break;                                                          
        case D3DTOP_ADD:                                                    
            DISPDBG((DBGLVL,"  D3DTOP_ADD"));                                    
            dwOperation = P3RX_TEXAPP_OPERATION_ADD_AB;    
            bOperation = TRUE;
            break;                                                          
        case D3DTOP_BLENDDIFFUSEALPHA:                                      
            DISPDBG((DBGLVL,"  D3DTOP_BLENDDIFFUSEALPHA"));                      
            dwOperation = P3RX_TEXAPP_OPERATION_LERP_ABI;   
            bOperation = TRUE;
            dwInvertI = 1 - dwInvertI;    
            dwI = P3RX_TEXAPP_I_CA;    
            bI = TRUE;
            break;                                                          
        case D3DTOP_BLENDFACTORALPHA:                                       
            DISPDBG((DBGLVL,"  D3DTOP_BLENDFACTORALPHA"));                       
            dwOperation = P3RX_TEXAPP_OPERATION_LERP_ABI; 
            bOperation = TRUE;
            dwInvertI = 1 - dwInvertI;  
            dwI = P3RX_TEXAPP_I_KA;             
            bI = TRUE;
            break;                                                          
        case D3DTOP_BLENDCURRENTALPHA:                                      
            DISPDBG((DBGLVL,"  D3DTOP_BLENDCURRENTALPHA"));                      
            dwOperation = P3RX_TEXAPP_OPERATION_LERP_ABI;   
            bOperation = TRUE;
            dwInvertI = 1 - dwInvertI;    
            dwI = P3RX_TEXAPP_I_TA;         
            bI = TRUE;
            break;                                                          
        case D3DTOP_MODULATEALPHA_ADDCOLOR:                                 
            DISPDBG((DBGLVL,"  D3DTOP_MODULATEALPHA_ADDCOLOR"));                 
            dwOperation = P3RX_TEXAPP_OPERATION_MODULATE_BI_ADD_A; 
            bOperation = TRUE;
            // I should have been set up by SETTAARG.
            // dwI = P3RX_TEXAPP_I_TA;         
            break;                                                          
        case D3DTOP_MODULATECOLOR_ADDALPHA:                                 
            DISPDBG((DBGLVL,"  D3DTOP_MODULATECOLOR_ADDALPHA"));                 
            dwOperation = P3RX_TEXAPP_OPERATION_MODULATE_AB_ADD_I; 
            bOperation = TRUE;
            // I should have been set up by SETTAARG.
            // dwI = P3RX_TEXAPP_I_TA; 
            break;                                                          
        case D3DTOP_MODULATEINVALPHA_ADDCOLOR:                              
            DISPDBG((DBGLVL,"  D3DTOP_MODULATEINVALPHA_ADDCOLOR"));              
            dwOperation = P3RX_TEXAPP_OPERATION_MODULATE_BI_ADD_A; 
            bOperation = TRUE;
            dwInvertI = 1 - dwInvertI;   
            // I should have been set up by SETTAARG.
            // dwI = P3RX_TEXAPP_I_TA;
            break;                                                          
        case D3DTOP_MODULATE2X:                                             
        case D3DTOP_MODULATE4X:                                             
        case D3DTOP_ADDSIGNED:                                              
        case D3DTOP_ADDSIGNED2X:                                            
        case D3DTOP_SUBTRACT:                                               
        case D3DTOP_ADDSMOOTH:                                              
        case D3DTOP_BLENDTEXTUREALPHA:                                      
        case D3DTOP_BLENDTEXTUREALPHAPM:                                    
        case D3DTOP_PREMODULATE:                                            
        case D3DTOP_MODULATEINVCOLOR_ADDALPHA:                              
        case D3DTOP_DOTPRODUCT3:                                            
            DISPDBG((ERRLVL,"** SETTAOP: Unsupported operation in TA unit."));
            if ( bIsAlpha )                                                 
            {                                                               
                SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_OP_HERE );             
            }                                                               
            else                                                            
            {                                                               
                SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_OP_HERE );             
            }                                                               
            break;                                                          
        case D3DTOP_BUMPENVMAP:                                             
        case D3DTOP_BUMPENVMAPLUMINANCE:                                    
            DISPDBG((ERRLVL,"** SETTAOP: Unsupported operation."));
            if ( bIsAlpha )                                                 
            {                                                               
                SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_ALPHA_OP );               
            }                                                               
            else                                                            
            {                                                               
                SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_COLOR_OP );               
            }                                                               
            break;                                                          
        default:                                                            
            // What is this?. //azn
            DISPDBG((ERRLVL,"** SETTAOP: Unknown operation."));
            if ( bIsAlpha )                                                 
            {                                                               
                SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_OP );                 
            }                                                               
            else                                                            
            {                                                               
                SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_OP );                 
            }                                                               
            break;                                                          
    }       

    if(bIsAlpha)
    {
        pMode->AlphaInvertI = dwInvertI;
        if (bOperation)
        {
            pMode->AlphaOperation = dwOperation;    
        }

        if (bI)
        {
            pMode->AlphaI = dwI;      
        }

        if (bA)
        {
            pMode->AlphaA = dwA;          
        }
    }
    else
    {
        pMode->ColorInvertI = dwInvertI;
        
        if (bOperation)
        {
            pMode->ColorOperation = dwOperation;    
        }

        if (bI)
        {
            pMode->ColorI = dwI;  
        }
        
        if (bA)
        {
            pMode->ColorA = dwA;          
        }
    }
} // SETTAOP

//-----------------------------------------------------------------------------
//
// _D3DDisplayWholeTSSPipe
//
// Dumps the whole TSS pipe state out to the debug stream.
// Also dumps fog, specular and alpha-blend state out.
//
//-----------------------------------------------------------------------------
void _D3DDisplayWholeTSSPipe ( P3_D3DCONTEXT* pContext, int iDebugNumber )
{
#if DBG
    int i;
    char *pszTemp, *pszTempPre, *pszTempPost;
    char *pszOp;
    char *pszArg1Pre, *pszArg1, *pszArg1Post;
    char *pszArg2Pre, *pszArg2, *pszArg2Post;
    char *pszSrc, *pszDest;
    P3_SURF_INTERNAL* pTexture;
    P3_SURF_FORMAT* pFormatSurface;

    DISPDBG((iDebugNumber,"TSS dump:"));

    #define SWITCH_ARG(prefix,opname) \
            case prefix##_##opname##: pszTemp = #opname; break

    i = 0;
    while ( ( i < 8 ) && ( TSSTATE ( i, D3DTSS_COLOROP ) != D3DTOP_DISABLE ) )
    {

        switch ( TSSTATE ( i, D3DTSS_COLOROP ) )
        {
            SWITCH_ARG ( D3DTOP,DISABLE );
            SWITCH_ARG ( D3DTOP,SELECTARG1 );
            SWITCH_ARG ( D3DTOP,SELECTARG2 );
            SWITCH_ARG ( D3DTOP,MODULATE );
            SWITCH_ARG ( D3DTOP,MODULATE2X );
            SWITCH_ARG ( D3DTOP,MODULATE4X );
            SWITCH_ARG ( D3DTOP,ADD );
            SWITCH_ARG ( D3DTOP,ADDSIGNED );
            SWITCH_ARG ( D3DTOP,ADDSIGNED2X );
            SWITCH_ARG ( D3DTOP,SUBTRACT );
            SWITCH_ARG ( D3DTOP,ADDSMOOTH );
            SWITCH_ARG ( D3DTOP,BLENDDIFFUSEALPHA );
            SWITCH_ARG ( D3DTOP,BLENDTEXTUREALPHA );
            SWITCH_ARG ( D3DTOP,BLENDFACTORALPHA );
            SWITCH_ARG ( D3DTOP,BLENDTEXTUREALPHAPM );
            SWITCH_ARG ( D3DTOP,BLENDCURRENTALPHA );
            SWITCH_ARG ( D3DTOP,PREMODULATE );
            SWITCH_ARG ( D3DTOP,MODULATEALPHA_ADDCOLOR );
            SWITCH_ARG ( D3DTOP,MODULATECOLOR_ADDALPHA );
            SWITCH_ARG ( D3DTOP,MODULATEINVALPHA_ADDCOLOR );
            SWITCH_ARG ( D3DTOP,MODULATEINVCOLOR_ADDALPHA );
            SWITCH_ARG ( D3DTOP,BUMPENVMAP );
            SWITCH_ARG ( D3DTOP,BUMPENVMAPLUMINANCE );
            SWITCH_ARG ( D3DTOP,DOTPRODUCT3 );
            default:
                pszTemp = "Unknown";
                break;
        }
        pszOp = pszTemp;

        switch ( TSSTATESELECT ( i, D3DTSS_COLORARG1 ) )
        {
            SWITCH_ARG ( D3DTA,DIFFUSE );
            SWITCH_ARG ( D3DTA,CURRENT );
            SWITCH_ARG ( D3DTA,TEXTURE );
            SWITCH_ARG ( D3DTA,TFACTOR );
            default:
                pszTemp = "Unknown";
                break;
        }
        if ( ( TSSTATE ( i, D3DTSS_COLORARG1 ) & D3DTA_ALPHAREPLICATE ) != 0 )
        {
            pszTempPost = ".A";
        }
        else
        {
            pszTempPost = ".C";
        }
        if ( ( TSSTATE ( i, D3DTSS_COLORARG1 ) & D3DTA_COMPLEMENT ) != 0 )
        {
            pszTempPre = "1-";
        }
        else
        {
            pszTempPre = "";
        }
        pszArg1Pre = pszTempPre;
        pszArg1Post = pszTempPost;
        pszArg1 = pszTemp;


        switch ( TSSTATESELECT ( i, D3DTSS_COLORARG2 ) )
        {
            SWITCH_ARG ( D3DTA,DIFFUSE );
            SWITCH_ARG ( D3DTA,CURRENT );
            SWITCH_ARG ( D3DTA,TEXTURE );
            SWITCH_ARG ( D3DTA,TFACTOR );
            default:
                pszTemp = "Unknown";
                break;
        }
        if ( ( TSSTATE ( i, D3DTSS_COLORARG2 ) & D3DTA_ALPHAREPLICATE ) != 0 )
        {
            pszTempPost = ".A";
        }
        else
        {
            pszTempPost = ".C";
        }
        if ( ( TSSTATE ( i, D3DTSS_COLORARG2 ) & D3DTA_COMPLEMENT ) != 0 )
        {
            pszTempPre = "1-";
        }
        else
        {
            pszTempPre = "";
        }
        pszArg2Pre = pszTempPre;
        pszArg2Post = pszTempPost;
        pszArg2 = pszTemp;


        DISPDBG((iDebugNumber," C%i: %s: %s%s%s, %s%s%s",
                 i, pszOp, pszArg1Pre, pszArg1, pszArg1Post, 
                 pszArg2Pre, pszArg2, pszArg2Post ));


        switch ( TSSTATE ( i, D3DTSS_ALPHAOP ) )
        {
            SWITCH_ARG ( D3DTOP,DISABLE );
            SWITCH_ARG ( D3DTOP,SELECTARG1 );
            SWITCH_ARG ( D3DTOP,SELECTARG2 );
            SWITCH_ARG ( D3DTOP,MODULATE );
            SWITCH_ARG ( D3DTOP,MODULATE2X );
            SWITCH_ARG ( D3DTOP,MODULATE4X );
            SWITCH_ARG ( D3DTOP,ADD );
            SWITCH_ARG ( D3DTOP,ADDSIGNED );
            SWITCH_ARG ( D3DTOP,ADDSIGNED2X );
            SWITCH_ARG ( D3DTOP,SUBTRACT );
            SWITCH_ARG ( D3DTOP,ADDSMOOTH );
            SWITCH_ARG ( D3DTOP,BLENDDIFFUSEALPHA );
            SWITCH_ARG ( D3DTOP,BLENDTEXTUREALPHA );
            SWITCH_ARG ( D3DTOP,BLENDFACTORALPHA );
            SWITCH_ARG ( D3DTOP,BLENDTEXTUREALPHAPM );
            SWITCH_ARG ( D3DTOP,BLENDCURRENTALPHA );
            SWITCH_ARG ( D3DTOP,PREMODULATE );
            SWITCH_ARG ( D3DTOP,MODULATEALPHA_ADDCOLOR );
            SWITCH_ARG ( D3DTOP,MODULATECOLOR_ADDALPHA );
            SWITCH_ARG ( D3DTOP,MODULATEINVALPHA_ADDCOLOR );
            SWITCH_ARG ( D3DTOP,MODULATEINVCOLOR_ADDALPHA );
            SWITCH_ARG ( D3DTOP,BUMPENVMAP );
            SWITCH_ARG ( D3DTOP,BUMPENVMAPLUMINANCE );
            SWITCH_ARG ( D3DTOP,DOTPRODUCT3 );
            default:
                pszTemp = "Unknown";
                break;
        }
        pszOp = pszTemp;


        switch ( TSSTATESELECT ( i, D3DTSS_ALPHAARG1 ) )
        {
            SWITCH_ARG ( D3DTA,DIFFUSE );
            SWITCH_ARG ( D3DTA,CURRENT );
            SWITCH_ARG ( D3DTA,TEXTURE );
            SWITCH_ARG ( D3DTA,TFACTOR );
            default:
                pszTemp = "Unknown";
                break;
        }
        if ( ( TSSTATE ( i, D3DTSS_ALPHAARG1 ) & D3DTA_ALPHAREPLICATE ) != 0 )
        {
            // Alpharep doesn't mean much in the alpha channel.
            pszTempPost = ".AR???";
        }
        else
        {
            pszTempPost = ".A";
        }
        if ( ( TSSTATE ( i, D3DTSS_ALPHAARG1 ) & D3DTA_COMPLEMENT ) != 0 )
        {
            pszTempPre = "1-";
        }
        else
        {
            pszTempPre = "";
        }
        pszArg1Pre = pszTempPre;
        pszArg1Post = pszTempPost;
        pszArg1 = pszTemp;


        switch ( TSSTATESELECT ( i, D3DTSS_ALPHAARG2 ) )
        {
            SWITCH_ARG ( D3DTA,DIFFUSE );
            SWITCH_ARG ( D3DTA,CURRENT );
            SWITCH_ARG ( D3DTA,TEXTURE );
            SWITCH_ARG ( D3DTA,TFACTOR );
            default:
                pszTemp = "Unknown";
                break;
        }
        if ( ( TSSTATE ( i, D3DTSS_ALPHAARG2 ) & D3DTA_ALPHAREPLICATE ) != 0 )
        {
            pszTempPost = ".AR???";
        }
        else
        {
            pszTempPost = ".A";
        }
        if ( ( TSSTATE ( i, D3DTSS_ALPHAARG2 ) & D3DTA_COMPLEMENT ) != 0 )
        {
            pszTempPre = "1-";
        }
        else
        {
            pszTempPre = "";
        }
        pszArg2Pre = pszTempPre;
        pszArg2Post = pszTempPost;
        pszArg2 = pszTemp;

        DISPDBG((iDebugNumber," A%i: %s: %s%s%s, %s%s%s", 
                    i, pszOp, pszArg1Pre, pszArg1, pszArg1Post, 
                       pszArg2Pre, pszArg2, pszArg2Post ));


        if ( TSSTATE ( i, D3DTSS_TEXTUREMAP ) != 0 )
        {
            char szTemp[4];
            // Setup texture 0.
            pTexture = GetSurfaceFromHandle(pContext, 
                                            TSSTATE(i, D3DTSS_TEXTUREMAP));
            if ( pTexture == NULL )
            {
                DISPDBG((iDebugNumber," Tex%i: 0x%x, TCI: %i, INVALID TEXTURE",
                         i, TSSTATE ( i, D3DTSS_TEXTUREMAP ), 
                            TSSTATE ( i, D3DTSS_TEXCOORDINDEX ) ));
            }
            else
            {
                pFormatSurface = pTexture->pFormatSurface;
                ASSERTDD ( pFormatSurface != NULL, 
                           "** _D3DDisplayWholeTSSPipe: "
                           "Surface had NULL format!" );

                // Find the filtering mode.
                szTemp[3] = '\0';
                switch ( TSSTATE ( i, D3DTSS_MINFILTER ) )
                {
                    case D3DTFN_POINT:
                        szTemp[0] = 'P';
                        break;
                    case D3DTFN_LINEAR:
                        szTemp[0] = 'L';
                        break;
                    case D3DTFN_ANISOTROPIC:
                        szTemp[0] = 'A';
                        break;
                    default:
                        szTemp[0] = '?';
                        break;
                }
                switch ( TSSTATE ( i, D3DTSS_MIPFILTER ) )
                {
                    case D3DTFP_NONE:
                        szTemp[1] = 'x';
                        break;
                    case D3DTFP_POINT:
                        szTemp[1] = 'P';
                        break;
                    case D3DTFP_LINEAR:
                        szTemp[1] = 'L';
                        break;
                    default:
                        szTemp[1] = '?';
                        break;
                }
                switch ( TSSTATE ( i, D3DTSS_MAGFILTER ) )
                {
                    case D3DTFG_POINT:
                        szTemp[2] = 'P';
                        break;
                    case D3DTFG_LINEAR:
                        szTemp[2] = 'L';
                        break;
                    case D3DTFG_FLATCUBIC:
                        szTemp[2] = 'F';
                        break;
                    case D3DTFG_GAUSSIANCUBIC:
                        szTemp[2] = 'G';
                        break;
                    case D3DTFG_ANISOTROPIC:
                        szTemp[2] = 'A';
                        break;
                    default:
                        szTemp[2] = '?';
                        break;
                }
                
                DISPDBG((iDebugNumber," Tex%i: 0x%x, TCI: %i, %s:%dx%d %s", 
                         i, TSSTATE ( i, D3DTSS_TEXTUREMAP ), 
                            TSSTATE ( i, D3DTSS_TEXCOORDINDEX ), 
                            pFormatSurface->pszStringFormat, 
                            pTexture->wWidth, 
                            pTexture->wHeight, szTemp ));
            }
        }
        else
        {
            DISPDBG((iDebugNumber," Tex%i: NULL, TCI: %i", 
                     i, TSSTATE ( i, D3DTSS_TEXCOORDINDEX ) ));
        }
        

        i++;
    }

    // Alpha-test.
    if ( pContext->RenderStates[D3DRENDERSTATE_ALPHATESTENABLE] != 0 )
    {
        switch ( pContext->RenderStates[D3DRENDERSTATE_ALPHAFUNC] )
        {
            SWITCH_ARG ( D3DCMP,NEVER );
            SWITCH_ARG ( D3DCMP,LESS );
            SWITCH_ARG ( D3DCMP,EQUAL );
            SWITCH_ARG ( D3DCMP,LESSEQUAL );
            SWITCH_ARG ( D3DCMP,GREATER );
            SWITCH_ARG ( D3DCMP,NOTEQUAL );
            SWITCH_ARG ( D3DCMP,GREATEREQUAL );
            SWITCH_ARG ( D3DCMP,ALWAYS );
            default:
                pszTemp = "Unknown";
                break;
        }
        DISPDBG((iDebugNumber,"Alpha-test: %s:0x%x.", 
                 pszTemp, pContext->RenderStates[D3DRENDERSTATE_ALPHAREF] ));
    }
    else
    {
        DISPDBG((iDebugNumber,"No alpha-test."));
    }


    // Alpha-blend.
    if ( pContext->RenderStates[D3DRENDERSTATE_BLENDENABLE] != 0 )
    {
        switch ( pContext->RenderStates[D3DRENDERSTATE_SRCBLEND] )
        {
            SWITCH_ARG ( D3DBLEND,ZERO );
            SWITCH_ARG ( D3DBLEND,ONE );
            SWITCH_ARG ( D3DBLEND,SRCCOLOR );
            SWITCH_ARG ( D3DBLEND,INVSRCCOLOR );
            SWITCH_ARG ( D3DBLEND,SRCALPHA );
            SWITCH_ARG ( D3DBLEND,INVSRCALPHA );
            SWITCH_ARG ( D3DBLEND,DESTALPHA );
            SWITCH_ARG ( D3DBLEND,INVDESTALPHA );
            SWITCH_ARG ( D3DBLEND,DESTCOLOR );
            SWITCH_ARG ( D3DBLEND,INVDESTCOLOR );
            SWITCH_ARG ( D3DBLEND,SRCALPHASAT );
            SWITCH_ARG ( D3DBLEND,BOTHSRCALPHA );
            SWITCH_ARG ( D3DBLEND,BOTHINVSRCALPHA );
            default:
                pszTemp = "Unknown";
                break;
        }
        pszSrc = pszTemp;

        switch ( pContext->RenderStates[D3DRENDERSTATE_DESTBLEND] )
        {
            SWITCH_ARG ( D3DBLEND,ZERO );
            SWITCH_ARG ( D3DBLEND,ONE );
            SWITCH_ARG ( D3DBLEND,SRCCOLOR );
            SWITCH_ARG ( D3DBLEND,INVSRCCOLOR );
            SWITCH_ARG ( D3DBLEND,SRCALPHA );
            SWITCH_ARG ( D3DBLEND,INVSRCALPHA );
            SWITCH_ARG ( D3DBLEND,DESTALPHA );
            SWITCH_ARG ( D3DBLEND,INVDESTALPHA );
            SWITCH_ARG ( D3DBLEND,DESTCOLOR );
            SWITCH_ARG ( D3DBLEND,INVDESTCOLOR );
            SWITCH_ARG ( D3DBLEND,SRCALPHASAT );
            SWITCH_ARG ( D3DBLEND,BOTHSRCALPHA );
            SWITCH_ARG ( D3DBLEND,BOTHINVSRCALPHA );
            default:
                pszTemp = "Unknown";
                break;
        }
        pszDest = pszTemp;
        DISPDBG((iDebugNumber,"Blend %s:%s", pszSrc, pszDest));
    }
    else
    {
        DISPDBG((iDebugNumber,"No alpha-blend."));
    }

    #undef SWITCH_ARG

#endif //DBG
} // _D3DDisplayWholeTSSPipe

//-----------------------------------------------------------------------------
//
// __TXT_TranslateToChipBlendMode
//
// Translates the blend mode from D3D into what the chip understands
//
//-----------------------------------------------------------------------------
void 
__TXT_TranslateToChipBlendMode( 
    P3_D3DCONTEXT *pContext, 
    TexStageState* pState,
    P3_SOFTWARECOPY* pSoftP3RX, 
    int iTSStage, 
    int iChipStageNo )
{
    struct TextureCompositeRGBAMode* pColorMode;
    struct TextureCompositeRGBAMode* pAlphaMode;
    struct TextureApplicationMode* pTAMode;

    switch(iChipStageNo)
    {
        default:
            DISPDBG((ERRLVL,"ERROR: Invalid texture stage!"));
            // Fall through and treat as #0 in order not to AV anything
        case 0:
            pColorMode = &pSoftP3RX->P3RXTextureCompositeColorMode0;
            pAlphaMode = &pSoftP3RX->P3RXTextureCompositeAlphaMode0;
            pTAMode = NULL;
            break;
        case 1:
            pColorMode = &pSoftP3RX->P3RXTextureCompositeColorMode1;
            pAlphaMode = &pSoftP3RX->P3RXTextureCompositeAlphaMode1;
            pTAMode = NULL;
            break;
        case 2:
            pColorMode = NULL;
            pAlphaMode = NULL;
            pTAMode = &pSoftP3RX->P3RXTextureApplicationMode;
            break;

    }

    DISPDBG((DBGLVL,"*** In __TXT_TranslateToChipBlendMode: "
               "Chip Stage %d, D3D TSS Stage %d", 
               iChipStageNo, iTSStage ));

    // Setup the arguments
    if ( ( iChipStageNo == 0 ) || ( iChipStageNo == 1 ) )
    {
        // Texture composite unit.
        DISPDBG((DBGLVL,"TexComp%d:", iChipStageNo ));
        DISPDBG((DBGLVL,"Arg1:" ));
        
        SETARG(pContext,
               pColorMode, 
               pState->m_dwVal[D3DTSS_COLORARG1], 
               1, 
               IS_COLOR_ARG, 
               iTSStage, 
               iChipStageNo);
               
        SETARG(pContext,
               pAlphaMode, 
               pState->m_dwVal[D3DTSS_ALPHAARG1], 
               1, 
               IS_ALPHA_ARG, 
               iTSStage, 
               iChipStageNo);

        DISPDBG((DBGLVL,"Arg2:" ));
        
        SETARG(pContext,
               pColorMode, 
               pState->m_dwVal[D3DTSS_COLORARG2], 
               2, 
               IS_COLOR_ARG, 
               iTSStage, 
               iChipStageNo);
               
        SETARG(pContext,
               pAlphaMode, 
               pState->m_dwVal[D3DTSS_ALPHAARG2], 
               2, 
               IS_ALPHA_ARG, 
               iTSStage, 
               iChipStageNo);

        DISPDBG((DBGLVL,"Op:" ));
        SETOP(pContext, 
              pColorMode, 
              pState->m_dwVal[D3DTSS_COLOROP], 
              iTSStage, 
              iChipStageNo, 
              IS_COLOR_ARG);
              
        SETOP(pContext, 
              pAlphaMode, 
              pState->m_dwVal[D3DTSS_ALPHAOP], 
              iTSStage, 
              iChipStageNo, 
              IS_ALPHA_ARG);
    }
    else if ( iChipStageNo == 2 ) 
    {
        DISPDBG((DBGLVL,"TexApp:" ));
        DISPDBG((DBGLVL,"Arg1:" ));
        
        SETTAARG_COLOR(pContext, 
                       pTAMode, 
                       pState->m_dwVal[D3DTSS_COLORARG1], 
                       1 );
                 
        if ( ( pState->m_dwVal[D3DTSS_ALPHAOP] != D3DTOP_DISABLE ) && 
             ( pState->m_dwVal[D3DTSS_ALPHAOP] != D3DTOP_SELECTARG2 ) )
        {
            SETTAARG_ALPHA(pContext,
                           pTAMode, 
                           pState->m_dwVal[D3DTSS_ALPHAARG1], 1
                           );
        }

        DISPDBG((DBGLVL,"Arg2:" ));
        SETTAARG_COLOR(pContext, 
                       pTAMode, 
                       pState->m_dwVal[D3DTSS_COLORARG2], 
                       2 );

        if ( ( pState->m_dwVal[D3DTSS_ALPHAOP] != D3DTOP_DISABLE ) && 
             ( pState->m_dwVal[D3DTSS_ALPHAOP] != D3DTOP_SELECTARG1 ) )
        {
            SETTAARG_ALPHA(pContext, 
                           pTAMode, 
                           pState->m_dwVal[D3DTSS_ALPHAARG2], 
                           2);
        }

        DISPDBG((DBGLVL,"Op:" ));
        SETTAOP(pContext,
                pTAMode, 
                pState->m_dwVal[D3DTSS_COLOROP], 
                IS_COLOR_ARG, 
                iTSStage, 
                iChipStageNo);
                
        SETTAOP(pContext,
                pTAMode, 
                pState->m_dwVal[D3DTSS_ALPHAOP], 
                IS_ALPHA_ARG, 
                iTSStage, 
                iChipStageNo);
    } 
    else
    {
        DISPDBG(( ERRLVL,"** __TXT_TranslateToChipBlendMode: "
                         "iChipStage must be 0 to 2" ));    
    }
} // __TXT_TranslateToChipBlendMode

//-----------------------------------------------------------------------------
//
// __TXT_ValidateTextureUnitStage
//
// Validate the texture which we're trying to set up in stage iChipStage of
// the hardware, iTSStage of the D3D TSS.
//
//-----------------------------------------------------------------------------
BOOL
__TXT_ValidateTextureUnitStage(
    P3_D3DCONTEXT* pContext,
    int iChipStage, 
    int iTSStage,
    P3_SURF_INTERNAL* pTexture)
{
    DWORD dwTexHandle = 
            pContext->TextureStageState[iTSStage].m_dwVal[D3DTSS_TEXTUREMAP];
            
    if( CHECK_SURF_INTERNAL_AND_DDSURFACE_VALIDITY(pTexture) &&
        (pTexture->Location != SystemMemory)                 &&  
        (dwTexHandle != 0)                                     )
    {
        // Texture is valid. Mark pCurrentTexturep[iChipStage] to point
        // to its P3_SURF_INTERNAL structure.
        pContext->pCurrentTexture[iChipStage] = pTexture;

        DISPDBG((DBGLVL, "__TXT_ValidateTextureUnitStage: valid texture %x "
                         "(handle %d) for iChipStage= %d iTSStage= %d",
                         pTexture, dwTexHandle, iChipStage, iTSStage));                                                                                    
    }
    else
    {
        // Set texture as invalid & force texturing off
        pContext->bTextureValid = FALSE;
        pContext->pCurrentTexture[iChipStage] = NULL;
        pContext->dwDirtyFlags |= CONTEXT_DIRTY_TEXTURE;

        // Setup error if we're asked to validate the TSS setup
        SET_BLEND_ERROR ( pContext,  BSF_INVALID_TEXTURE );

        DISPDBG((WRNLVL, "__TXT_ValidateTextureUnitStage: INVALID texture %x "
                         "(handle %d) for iChipStage= %d iTSStage= %d "
                         "Location=%d",
                         pTexture, dwTexHandle, iChipStage, iTSStage,
                         (pTexture !=NULL)?pTexture->Location:0));      
    }

    return ( (BOOL)pContext->bTextureValid );
    
} // __TXT_ValidateTextureUnitStage

//-----------------------------------------------------------------------------
//
// __TXT_ConsiderSrcChromaKey
//
// Setup chromakeying for a certain texture bound to a certain stage.
// Note - "stage" is the chip stage, not the D3D stage.
//-----------------------------------------------------------------------------
static void
__TXT_ConsiderSrcChromaKey(
    P3_D3DCONTEXT *pContext, 
    P3_SURF_INTERNAL* pTexture, 
    int stage )
{
    P3_THUNKEDDATA * pThisDisplay = pContext->pThisDisplay;
    P3_SOFTWARECOPY* pSoftP3RX = &pContext->SoftCopyGlint;
    P3_DMA_DEFS();

    if ((pTexture->dwFlagsInt & DDRAWISURF_HASCKEYSRCBLT) &&
        pContext->RenderStates[D3DRENDERSTATE_COLORKEYENABLE])
    {
        DWORD LowerBound = 0x00000000;
        DWORD UpperBound = 0xFFFFFFFF;
        DWORD* pPalEntries = NULL;
        DWORD dwPalFlags = 0;

        DISPDBG((DBGLVL,"    Can Chroma Key texture stage %d", stage));

        pContext->bCanChromaKey = TRUE;

#if DX7_PALETTETEXTURE
        // Get the palette entries
        if (pTexture->pixFmt.dwFlags & DDPF_PALETTEINDEXED8)
        {
            D3DHAL_DP2UPDATEPALETTE *pPalette = NULL;
        
            pPalette = GetPaletteFromHandle(pContext,
                                            pTexture->dwPaletteHandle);
            if (pPalette)
            {
                pPalEntries = (LPDWORD)(pPalette + 1);
            }
            else
            {
                SET_BLEND_ERROR(pContext, BSF_INVALID_TEXTURE);
            }

            dwPalFlags = pTexture->dwPaletteFlags;
        }
#endif        

        // Get the correct chroma value for the texture map to send to the chip.
        Get8888ScaledChroma(pThisDisplay, 
                            pTexture->dwFlagsInt, 
                            &pTexture->pixFmt,
                            pTexture->dwCKLow,
                            pTexture->dwCKHigh,
                            &LowerBound, 
                            &UpperBound, 
                            pPalEntries,
                            dwPalFlags & DDRAWIPAL_ALPHA, 
                            FALSE);

        P3_DMA_GET_BUFFER_ENTRIES( 8);

        // Send the upper and lower bounds for the alpha-map filtering
        if( stage == 0 )
        {
            SEND_P3_DATA(TextureChromaLower0, LowerBound );
            SEND_P3_DATA(TextureChromaUpper0, UpperBound );
            pSoftP3RX->P3RXTextureFilterMode.AlphaMapEnable0 = 
                                                            __PERMEDIA_ENABLE;
            pSoftP3RX->P3RXTextureFilterMode.AlphaMapSense0 = 
                                                    P3RX_ALPHAMAPSENSE_INRANGE;
        }
        else
        {
            ASSERTDD ( stage == 1, 
                       "** __TXT_ConsiderSrcChromaKey: stage must be 0 or 1" );
        }

        // If we are mipmapping, we need to set up texture1's chromakey as well.
        // If not, then this will be overridden when this gets called for tex1.
        SEND_P3_DATA(TextureChromaLower1, LowerBound );
        SEND_P3_DATA(TextureChromaUpper1, UpperBound );
        pSoftP3RX->P3RXTextureFilterMode.AlphaMapEnable1 = __PERMEDIA_ENABLE;
        pSoftP3RX->P3RXTextureFilterMode.AlphaMapSense1 = 
                                                    P3RX_ALPHAMAPSENSE_INRANGE;

        P3_DMA_COMMIT_BUFFER();

        pSoftP3RX->P3RXTextureFilterMode.AlphaMapFiltering = __PERMEDIA_ENABLE;
    }
    else
    {
        DISPDBG((DBGLVL,"    Can't Chroma Key texture stage %d", stage));

        if( stage == 0 )
        {
            pSoftP3RX->P3RXTextureFilterMode.AlphaMapEnable0 = 
                                                            __PERMEDIA_DISABLE;
        }
        else
        {
            ASSERTDD ( stage == 1, 
                       "** __TXT_ConsiderSrcChromaKey: stage must be 0 or 1" );
        }
        
        // If we are mipmapping, we need to set up texture1's chromakey (or 
        // lack of it) as well. If not, then this will be overridden when 
        // this gets called for tex1.
        pSoftP3RX->P3RXTextureFilterMode.AlphaMapEnable1 = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureFilterMode.AlphaMapFiltering = __PERMEDIA_DISABLE;
    }
} // __TXT_ConsiderSrcChromaKey


//-----------------------------------------------------------------------------
//
// __TXT_SetupTexture
//
// This is the new all-singing all-dancing texture setup code.
// Return is TRUE if setup succeeded, FALSE if it failed (for ValidateDevice)
// This sets up either texture 0 or texture 1, taking its wrapping, etc,
// info from iTSStage.
//
//-----------------------------------------------------------------------------
BOOL __TXT_SetupTexture (
        P3_THUNKEDDATA * pThisDisplay,
        int iTexNo,
        int iTSStage,
        P3_D3DCONTEXT* pContext,
        P3_SURF_INTERNAL* pTexture,
        P3_SOFTWARECOPY* pSoftP3RX,
        BOOL bBothTexturesValid,
        P3_MIP_BASES *pMipBases)
{
    P3_SURF_FORMAT* pFormatSurface;
    int iT0MaxLevel, iT1MaxLevel;
 
    P3_DMA_DEFS();

    ASSERTDD ( ( iTexNo >= 0 ) && ( iTexNo <= 1 ), 
               "**__TXT_SetupTexture: we only have two texture units!" );

    if ( pTexture != NULL )
    {
        pFormatSurface = pTexture->pFormatSurface;
    }
    else
    {
        // Suceeded, but should never have got here!
        DISPDBG((ERRLVL,"**__TXT_SetupTexture: should never "
                             "be called with handle of NULL"));
        return ( TRUE );
    }

    P3_DMA_GET_BUFFER();
    P3_ENSURE_DX_SPACE(32);
    WAIT_FIFO(32);

    // Set up the texture-relevant things.

    switch ( iTexNo )
    {
        case 0:
        {
            // Set both bits in case we are mipmapping

            pSoftP3RX->P3RXTextureFilterMode.ForceAlphaToOne0 = 
                                            pFormatSurface->bAlpha ? 
                                                    __PERMEDIA_DISABLE : 
                                                    __PERMEDIA_ENABLE;
            pSoftP3RX->P3RXTextureFilterMode.ForceAlphaToOne1 = 
                                            pFormatSurface->bAlpha ? 
                                                    __PERMEDIA_DISABLE : 
                                                    __PERMEDIA_ENABLE;

            // D3D UV Wrapping
            if (pContext->RenderStates[D3DRENDERSTATE_WRAP0+iTSStage] 
                                                            & D3DWRAP_U)
            {
                pSoftP3RX->P4DeltaFormatControl.WrapS = 1;
            }
            else
            {
                pSoftP3RX->P4DeltaFormatControl.WrapS = 0;
            }

            if (pContext->RenderStates[D3DRENDERSTATE_WRAP0+iTSStage] 
                                                            & D3DWRAP_V)
            {
                pSoftP3RX->P4DeltaFormatControl.WrapT = 1;
            }
            else
            {
                pSoftP3RX->P4DeltaFormatControl.WrapT = 0;
            }

            // U Wrapping
            switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSU ))
            {
                case D3DTADDRESS_CLAMP:
                    pSoftP3RX->P3RXTextureCoordMode.WrapS = 
                                            __GLINT_TEXADDRESS_WRAP_CLAMP;
                    pSoftP3RX->P3RXTextureIndexMode0.WrapU = 
                                            P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                    break;
                case D3DTADDRESS_WRAP:
                    pSoftP3RX->P3RXTextureCoordMode.WrapS = 
                                            __GLINT_TEXADDRESS_WRAP_REPEAT;
                    pSoftP3RX->P3RXTextureIndexMode0.WrapU = 
                                            P3RX_TEXINDEXMODE_WRAP_REPEAT;
                    break;
                case D3DTADDRESS_MIRROR:
                    pSoftP3RX->P3RXTextureCoordMode.WrapS = 
                                            __GLINT_TEXADDRESS_WRAP_MIRROR;
                    pSoftP3RX->P3RXTextureIndexMode0.WrapU = 
                                            P3RX_TEXINDEXMODE_WRAP_MIRROR;
                    break;
                default:
                    DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSU!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                    break;
            }

            // V Wrapping
            switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSV ))
            {
                case D3DTADDRESS_CLAMP:
                    pSoftP3RX->P3RXTextureCoordMode.WrapT = 
                                            __GLINT_TEXADDRESS_WRAP_CLAMP;
                    pSoftP3RX->P3RXTextureIndexMode0.WrapV = 
                                            P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                    break;
                case D3DTADDRESS_WRAP:
                    pSoftP3RX->P3RXTextureCoordMode.WrapT = 
                                            __GLINT_TEXADDRESS_WRAP_REPEAT;
                    pSoftP3RX->P3RXTextureIndexMode0.WrapV = 
                                            P3RX_TEXINDEXMODE_WRAP_REPEAT;
                    break;
                case D3DTADDRESS_MIRROR:
                    pSoftP3RX->P3RXTextureCoordMode.WrapT = 
                                            __GLINT_TEXADDRESS_WRAP_MIRROR;
                    pSoftP3RX->P3RXTextureIndexMode0.WrapV = 
                                            P3RX_TEXINDEXMODE_WRAP_MIRROR;
                    break;
                default:
                    DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSV!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                    break;
            }

#if DX8_3DTEXTURES
            if (pTexture->b3DTexture)
            {
                // W Wrapping
                switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSW ))
                {
                    case D3DTADDRESS_CLAMP:
                        pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                __GLINT_TEXADDRESS_WRAP_CLAMP;
                        pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                            P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                        break;
                    
                    case D3DTADDRESS_WRAP:
                        pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                __GLINT_TEXADDRESS_WRAP_REPEAT;
                        pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                                P3RX_TEXINDEXMODE_WRAP_REPEAT;
                        break;
                    
                    case D3DTADDRESS_MIRROR:
                        pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                __GLINT_TEXADDRESS_WRAP_MIRROR;
                        pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                                P3RX_TEXINDEXMODE_WRAP_MIRROR;
                        break;
                    
                    default:
                        DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSW!"));
                        SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                        break;
                }
            }
#endif // DX8_3DTEXTURES

            if(( TSSTATE( iTSStage, D3DTSS_ADDRESSU ) == D3DTADDRESS_CLAMP ) ||
               ( TSSTATE( iTSStage, D3DTSS_ADDRESSV ) == D3DTADDRESS_CLAMP ))
            {
                if( (TSSTATE( iTSStage, D3DTSS_ADDRESSU ) != D3DTADDRESS_CLAMP) ||
                    (TSSTATE( iTSStage, D3DTSS_ADDRESSV ) != D3DTADDRESS_CLAMP))
                {
                    DISPDBG((ERRLVL,"Warning: One texture coord clamped, but not "
                                "the other - can't appply TextureShift"));
                } 

                pSoftP3RX->P4DeltaFormatControl.TextureShift = 
                                                        __PERMEDIA_DISABLE;
            }
            else
            {
                pSoftP3RX->P4DeltaFormatControl.TextureShift = 
                                                        __PERMEDIA_ENABLE;
            }

            ASSERTDD ( pFormatSurface != NULL, 
                       "** SetupTextureUnitStage: logic error: "
                       "pFormatSurace is NULL" );
            switch (pFormatSurface->DeviceFormat)
            {
                case SURF_CI8:
                    pSoftP3RX->P3RXTextureReadMode0.TextureType = 
                                    P3RX_TEXREADMODE_TEXTURETYPE_8BITINDEXED;
                    break;
                    
                case SURF_YUV422:
                    pSoftP3RX->P3RXTextureReadMode0.TextureType = 
                                    P3RX_TEXREADMODE_TEXTURETYPE_422_YVYU;
                    break;
                    
                default:
                    pSoftP3RX->P3RXTextureReadMode0.TextureType = 
                                    P3RX_TEXREADMODE_TEXTURETYPE_NORMAL;
                    break;
            }
            
            // MAG Filter
            switch(TSSTATE ( iTSStage, D3DTSS_MAGFILTER ))
            {
                case D3DTFG_POINT:
                    pSoftP3RX->P3RXTextureIndexMode0.MagnificationFilter = 
                                    __GLINT_TEXTUREREAD_FILTER_NEAREST;
                    break;
                    
                case D3DTFG_LINEAR:
                    pSoftP3RX->P3RXTextureIndexMode0.MagnificationFilter = 
                                    __GLINT_TEXTUREREAD_FILTER_LINEAR;
                    break;
                    
                case D3DTFG_FLATCUBIC:
                case D3DTFG_GAUSSIANCUBIC:
                case D3DTFG_ANISOTROPIC:
                    DISPDBG((ERRLVL,"ERROR: Unsupported MAGFILTER!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_FILTER );
                    break;
                    
                default:
                    DISPDBG((ERRLVL,"ERROR: Unknown MAGFILTER!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                    break;
            }

            switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
            {
                case D3DTFN_POINT:
                    pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter = 
                                            __GLINT_TEXTUREREAD_FILTER_NEAREST;
                    break;
                    
                case D3DTFN_LINEAR:
                    pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter = 
                                            __GLINT_TEXTUREREAD_FILTER_LINEAR;
                    break;
                    
                case D3DTFN_ANISOTROPIC:
                    DISPDBG((ERRLVL,"ERROR: Unsupported MINFILTER!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_FILTER );
                    break;
                    
                default:
                    DISPDBG((ERRLVL,"ERROR: Unknown MINFILTER!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                    break;
            }

            switch(TSSTATE ( iTSStage, D3DTSS_MIPFILTER ))
            {
                case D3DTFP_NONE:
                    // No need to set the minification filter, it was done above
                    break;
                    
                case D3DTFP_POINT:
                    switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
                    {
                        case D3DTFN_POINT:
                            // Point Min, Point Mip
                            pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                        __GLINT_TEXTUREREAD_FILTER_NEARMIPNEAREST;
                            break;
                        case D3DTFN_LINEAR:
                            // Linear Min, Point Mip
                            pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                     __GLINT_TEXTUREREAD_FILTER_LINEARMIPNEAREST;
                            break;
                    }
                    break;
                    
                case D3DTFP_LINEAR:
                    if( bBothTexturesValid )
                    {
                        // We can only do per-poly mipmapping while 
                        // multi-texturing, so don't enable inter-map filtering.

                        // Non-fatal error - drop back to nearest 
                        // mipmap filtering.
                        SET_BLEND_ERROR ( pContext,  BS_INVALID_FILTER );

                        switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
                        {
                            case D3DTFN_POINT:
                                // Point Min, Point Mip
                                pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                                                    __GLINT_TEXTUREREAD_FILTER_NEARMIPNEAREST;
                                break;
                            case D3DTFN_LINEAR:
                                // Linear Min, Point Mip
                                pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                                                    __GLINT_TEXTUREREAD_FILTER_LINEARMIPNEAREST;
                                break;
                        }
                    }
                    else
                    {
                        // Single texture - do inter-map filtering

                        switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
                        {
                            case D3DTFN_POINT:
                                // Point Min, Linear Mip
                                pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                                                    __GLINT_TEXTUREREAD_FILTER_NEARMIPLINEAR;
                                break;
                            case D3DTFN_LINEAR:
                                // Linear Min, Linear Mip
                                pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                                                    __GLINT_TEXTUREREAD_FILTER_LINEARMIPLINEAR;
                                break;
                        }
                    }
                    break;
                default:
                    DISPDBG((ERRLVL,"ERROR: Unknown MIPFILTER!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                    break;
            }

            // MipMapping
            if( (TSSTATE ( iTSStage, D3DTSS_MIPFILTER ) != D3DTFP_NONE) && 
                (pTexture->bMipMap))
            {
                int iLOD;
                INT iTexLOD;

                DISPDBG(( DBGLVL, "Multiple texture levels" ));

                // Load the mipmap levels for texture 0
                // Mip level from pMipBases->dwTex0ActMaxLevel to 
                // pTexture->iMipLevels will be mapped to base address slot
                // from pMipBases->dwTex0Mipbase to dwTex0MipMax
                ASSERTDD ( pMipBases->dwTex0MipBase == 0, 
                          "** __TXT_SetupTexture: "
                          "Texture 0 mipmap base is not 0" );
                          
                iLOD = pMipBases->dwTex0MipBase;
                iTexLOD = pMipBases->dwTex0ActMaxLevel;
                iT0MaxLevel = iTexLOD;

                while(( iTexLOD < pTexture->iMipLevels ) && 
                      ( iLOD <= (int)pMipBases->dwTex0MipMax ))
                {
                    DISPDBG((DBGLVL, "  Setting Texture Base Address %d to 0x%x", 
                                iLOD, pTexture->MipLevels[iLOD].dwOffsetFromMemoryBase));
                                
                    pSoftP3RX->P3RXTextureMapWidth[iLOD] = 
                                pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;

#if DX7_TEXMANAGEMENT
                    // If this is a driver managed texture surface, we need 
                    // to use our privately allocated mem ptr
                    if (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
                    {                        
                        SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                            (DWORD)D3DTMMIPLVL_GETOFFSET(pTexture->MipLevels[iTexLOD], pThisDisplay),
                                            iLOD);
                    }
                    else
#endif
                    {
                        SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                            pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase, 
                                            iLOD);
                    }

                    iLOD++;
                    iTexLOD++;
                }

                // If both textures are enabled we can't do per-pixel 
                // mipmapping because that uses both sets of texcoord 
                // DDAs to generate the LOD level. So we must do per-poly 
                // mipmapping. Per-poly mipmapping can only be done in 
                // hardware on P4 - we use a Delta renderer on P3 when 
                // mipmapping with both textures enabled.

                if( bBothTexturesValid )
                {
                    DISPDBG(( DBGLVL, "Both textures valid" ));

                    // Do per-poly mipmapping in the P4 DeltaFormat unit

                    pSoftP3RX->P3RXTextureCoordMode.EnableLOD = 
                                                            __PERMEDIA_DISABLE;
                    pSoftP3RX->P3RXTextureCoordMode.EnableDY = 
                                                            __PERMEDIA_DISABLE;
                    pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap = 
                                                            __PERMEDIA_ENABLE;

                    {
                        DWORD d;

                        *(float *)&d = 
                                pContext->MipMapLODBias[TEXSTAGE_0] *
                                pTexture->dwPixelPitch *
                                pTexture->wHeight;

                        SEND_P3_DATA(TextureLODScale, d);                       
                    }
                }
                else
                {
                    DISPDBG(( DBGLVL, "Single texture only" ));

                    // Do per-pixel mipmapping

                
                    pSoftP3RX->P3RXTextureCoordMode.EnableLOD = 
                                                            __PERMEDIA_ENABLE;
                    pSoftP3RX->P3RXTextureCoordMode.EnableDY = 
                                                            __PERMEDIA_ENABLE;
                    pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap = 
                                                            __PERMEDIA_DISABLE;

//@@BEGIN_DDKSPLIT
#if 0
// azn this would disable "automatic" mip mapping and we would be using the
// values supplied by writing into LOD and LOD1 (d3dprim.c) - don't forget!
                    pSoftP3RX->P3RXTextureCoordMode.EnableLOD = 
                                                            __PERMEDIA_DISABLE;
                    pSoftP3RX->P3RXTextureCoordMode.EnableDY = 
                                                            __PERMEDIA_DISABLE;
                    pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap = 
                                                            __PERMEDIA_ENABLE;
                    SEND_P3_DATA(TextureLODScale, 0);
#endif
//@@END_DDKSPLIT
                    {
                        float bias;
                        DWORD b;

                        bias = pContext->TextureStageState[TEXSTAGE_0].m_fVal[D3DTSS_MIPMAPLODBIAS];

                        // Convert LOD bias from float to 6.8

                        myFtoi( &b, bias * 256.0f );

                        SEND_P3_DATA(TextureLODBiasS, b);
                        SEND_P3_DATA(TextureLODBiasT, b);
                    }
                }

                pSoftP3RX->P3RXTextureIndexMode0.MipMapEnable = 
                                                            __PERMEDIA_ENABLE;
            }
            else
            {
                int iTexLOD;

                // No mipmapping.
                DISPDBG(( DBGLVL, "Single texture level" ));

                pSoftP3RX->P3RXTextureCoordMode.EnableLOD = __PERMEDIA_DISABLE;
                pSoftP3RX->P3RXTextureCoordMode.EnableDY = __PERMEDIA_DISABLE;
                pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap = 
                                                            __PERMEDIA_DISABLE;
                pSoftP3RX->P3RXTextureIndexMode0.MipMapEnable = 
                                                            __PERMEDIA_DISABLE;

                ASSERTDD ( pMipBases->dwTex0MipBase == 0, 
                          "** __TXT_SetupTexture: "
                          "Texture 0 mipmap base is not 0" );
                          
                // Use the maximum level indicated by D3DTSS_MAXMIPLEVEL
                iTexLOD = pMipBases->dwTex0ActMaxLevel;
                iT0MaxLevel = iTexLOD;

#if DX7_TEXMANAGEMENT
                // If this is a driver managed texture surface, we need 
                // to use our privately allocated mem ptr
                if (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
                {                        
                    SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                        (DWORD)D3DTMMIPLVL_GETOFFSET(pTexture->MipLevels[iTexLOD], pThisDisplay),
                                        0);
                }
                else
#endif                
                {
                    SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                        pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase, 
                                        0);
                }

                // No mipmapping, but could be combining the caches.
                pSoftP3RX->P3RXTextureMapWidth[0] = 
                                    pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;
                pSoftP3RX->P3RXTextureMapWidth[1] = 
                                    pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;
            }

//@@BEGIN_DDKSPLIT
            // There is only one TextureCoordMode, it is possible that it is
            // not used at all
//@@END_DDKSPLIT
            // Set maximum dimension of the texture
            pSoftP3RX->P3RXTextureCoordMode.Width = pTexture->MipLevels[iT0MaxLevel].logWidth;
            pSoftP3RX->P3RXTextureCoordMode.Height = pTexture->MipLevels[iT0MaxLevel].logHeight;
#if DX7_PALETTETEXTURE
            // If it is a palette indexed texture, we simply follow the chain
            // down from the surface to its palette and pull out the LUT values
            // from the PALETTEENTRY's in the palette.
            ASSERTDD ( pFormatSurface != NULL, "** SetupTextureUnitStage: logic error: pFormatSurace is NULL" );
            if (pFormatSurface->DeviceFormat == SURF_CI8)
            {
                WAIT_FIFO(8);

                pSoftP3RX->P3RXLUTMode.Enable = __PERMEDIA_ENABLE;
                pSoftP3RX->P3RXLUTMode.InColorOrder = __PERMEDIA_ENABLE;        
                SEND_P3_DATA(LUTAddress, 0);
                SEND_P3_DATA(LUTTransfer, 0);
                SEND_P3_DATA(LUTIndex, 0);
                COPY_P3_DATA(LUTMode, pSoftP3RX->P3RXLUTMode);

                // In this case simply download the 256 entries each time the 
                // texture handle changes.
                {
                    DWORD dwCount1, dwCount2;
                    D3DHAL_DP2UPDATEPALETTE *pPalette;  // associated palette
                    LPDWORD lpColorTable;           // array of palette entries
        
                    pPalette = GetPaletteFromHandle(pContext, 
                                                    pTexture->dwPaletteHandle);
                    if (pPalette) // If palette can be found
                    {
                        lpColorTable = (LPDWORD)(pPalette + 1);
                        
                        if (pTexture->dwPaletteFlags & DDRAWIPAL_ALPHA)
                        {
                            for (dwCount1 = 0; dwCount1 < 16; dwCount1++)
                            {
                                P3_ENSURE_DX_SPACE(17);
                                WAIT_FIFO(17);
                                P3RX_HOLD_CMD(LUTData, 16);
                                for (dwCount2 = 0; dwCount2 < 16; dwCount2++)
                                {
                                    *dmaPtr++ = *lpColorTable++;
                                    CHECK_FIFO(1);
                                }
                            }
                        }
                        else
                        {
                            for (dwCount1 = 0; dwCount1 < 16; dwCount1++)
                            {
                                P3_ENSURE_DX_SPACE(17);
                                WAIT_FIFO(17);
                                P3RX_HOLD_CMD(LUTData, 16);
                                for (dwCount2 = 0; dwCount2 < 16; dwCount2++)
                                {
                                    *dmaPtr++ = CHROMA_UPPER_ALPHA(*(DWORD*)lpColorTable++);
                                    CHECK_FIFO(1);
                                }
                            }
                        }
                    }
                    else
                    {
                        DISPDBG((ERRLVL,"Palette handle is missing for CI8 surf!"));
                    }
                }

                // Make sure there is room left over for the rest of the routine
                P3_ENSURE_DX_SPACE(2);
                WAIT_FIFO(2);
                SEND_P3_DATA(LUTIndex, 0);

            }
            else
#endif // DX7_PALETTETEXTURE
            {
                // No LUT.
                P3_ENSURE_DX_SPACE(4);
                WAIT_FIFO(4);
                
                pSoftP3RX->P3RXLUTMode.Enable = __PERMEDIA_DISABLE;
                SEND_P3_DATA(LUTTransfer, __PERMEDIA_DISABLE);
                COPY_P3_DATA(LUTMode, pSoftP3RX->P3RXLUTMode)
            }

#if DX8_3DTEXTURES
            P3_ENSURE_DX_SPACE(4);
            WAIT_FIFO(4);

            if (pTexture->b3DTexture)
            {
                //
                // Set size of each 2D texture slice in texel size to TextureMapSize.
                //
                SEND_P3_DATA(TextureMapSize, pTexture->dwSliceInTexel);
            }
            else
            {
                SEND_P3_DATA(TextureMapSize, 0);
            }
#endif // DX8_3DTEXTURES

            P3_DMA_COMMIT_BUFFER();
            __TXT_ConsiderSrcChromaKey( pContext, pTexture, 0 );
            P3_DMA_GET_BUFFER();

            // Setup TextureReadMode
            pSoftP3RX->P3RXTextureReadMode0.MapBaseLevel = 
                                                    pMipBases->dwTex0MipBase;
            pSoftP3RX->P3RXTextureReadMode0.MapMaxLevel = 
                                                    pMipBases->dwTex0MipMax;
            pSoftP3RX->P3RXTextureReadMode0.Width = pTexture->MipLevels[iT0MaxLevel].logWidth;
            pSoftP3RX->P3RXTextureReadMode0.Height = pTexture->MipLevels[iT0MaxLevel].logHeight;
            pSoftP3RX->P3RXTextureReadMode0.TexelSize = pTexture->dwPixelSize;

            pSoftP3RX->P3RXTextureReadMode0.LogicalTexture = 
                                                            __PERMEDIA_DISABLE;

            // Enable stage 0
            pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_ENABLE;
            pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_ENABLE;

            // Never set CombineCaches - chip bug
            pSoftP3RX->P3RXTextureReadMode0.CombineCaches = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureFilterMode.CombineCaches = __PERMEDIA_DISABLE;

            // Always copy TRM0 to TRM1 in case we are combining the caches
            pSoftP3RX->P3RXTextureReadMode1 = pSoftP3RX->P3RXTextureReadMode0;

            // Enable the texture index unit 
            // (this is a bit like the the texture read)
            pSoftP3RX->P3RXTextureIndexMode0.Width = pTexture->MipLevels[iT0MaxLevel].logWidth;
            pSoftP3RX->P3RXTextureIndexMode0.Height = pTexture->MipLevels[iT0MaxLevel].logHeight;

            // Set both formats to be equal for texture 0 - this will be correct 
            // for single-texture per-pixel mipmap or non-mipmapped with a 
            // combined cache. If the second texture is valid it's setup below 
            // will set Format1 appropriately.

            ASSERTDD ( pFormatSurface != NULL, 
                       "** SetupTextureUnitStage: logic error: "
                       "pFormatSurace is NULL" );

            pSoftP3RX->P3RXTextureFilterMode.Format0 = 
                                                pFormatSurface->FilterFormat;
            pSoftP3RX->P3RXTextureFilterMode.Format1 = 
                                                pFormatSurface->FilterFormat;

#if DX8_3DTEXTURES
            if (pTexture->b3DTexture)
            {
                //
                // Enable 3D Texture registers.
                //
                pSoftP3RX->P3RX_P3DeltaMode.Texture3DEnable = __PERMEDIA_ENABLE;
                pSoftP3RX->P3RXTextureReadMode0.Texture3D = __PERMEDIA_ENABLE;
                pSoftP3RX->P3RXTextureIndexMode0.Texture3DEnable = 
                                                            __PERMEDIA_ENABLE;

                //
                // ReadMode1 and IndexMode1 should have same data as 0.
                //
                pSoftP3RX->P3RXTextureReadMode1 = pSoftP3RX->P3RXTextureReadMode0;
                pSoftP3RX->P3RXTextureIndexMode1 = pSoftP3RX->P3RXTextureIndexMode0;

                //
                // And put logDepth into IndexMode1.Width. 
                //
                pSoftP3RX->P3RXTextureIndexMode1.Width = pTexture->logDepth;
            }
            else
            {
                pSoftP3RX->P3RXTextureReadMode0.Texture3D = __PERMEDIA_DISABLE;
                pSoftP3RX->P3RXTextureIndexMode0.Texture3DEnable = 
                                                            __PERMEDIA_DISABLE;
            }
#endif // DX8_3DTEXTURES
            break;
        }

        // Texture Stage 1
        case 1:
        {
            pSoftP3RX->P3RXTextureFilterMode.ForceAlphaToOne1 = 
                                            pFormatSurface->bAlpha ? 
                                                    __PERMEDIA_DISABLE : 
                                                    __PERMEDIA_ENABLE;

            // D3D UV Wrapping
            if (pContext->RenderStates[D3DRENDERSTATE_WRAP0+iTSStage] & 
                                                                    D3DWRAP_U)
            {
                pSoftP3RX->P4DeltaFormatControl.WrapS1 = 1;
            }
            else
            {
                pSoftP3RX->P4DeltaFormatControl.WrapS1 = 0;
            }

            if (pContext->RenderStates[D3DRENDERSTATE_WRAP0+iTSStage] & 
                                                                    D3DWRAP_V)
            {
                pSoftP3RX->P4DeltaFormatControl.WrapT1 = 1;
            }
            else
            {
                pSoftP3RX->P4DeltaFormatControl.WrapT1 = 0;
            }

            // U Addressing
            switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSU ))
            {
                case D3DTADDRESS_CLAMP:
                    pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                __GLINT_TEXADDRESS_WRAP_CLAMP;
                    pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                            P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                    break;
                    
                case D3DTADDRESS_WRAP:
                    pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                __GLINT_TEXADDRESS_WRAP_REPEAT;
                    pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                                P3RX_TEXINDEXMODE_WRAP_REPEAT;
                    break;
                    
                case D3DTADDRESS_MIRROR:
                    pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                __GLINT_TEXADDRESS_WRAP_MIRROR;
                    pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                                P3RX_TEXINDEXMODE_WRAP_MIRROR;
                    break;
                    
                default:
                    DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSU!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                    break;
            }

            // V Addressing
            switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSV ))
            {
                case D3DTADDRESS_CLAMP:
                    pSoftP3RX->P3RXTextureCoordMode.WrapT1 = 
                                                __GLINT_TEXADDRESS_WRAP_CLAMP;
                    pSoftP3RX->P3RXTextureIndexMode1.WrapV = 
                                            P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                    break;
                    
                case D3DTADDRESS_WRAP:
                    pSoftP3RX->P3RXTextureCoordMode.WrapT1 = 
                                                __GLINT_TEXADDRESS_WRAP_REPEAT;
                    pSoftP3RX->P3RXTextureIndexMode1.WrapV = 
                                                P3RX_TEXINDEXMODE_WRAP_REPEAT;
                    break;
                    
                case D3DTADDRESS_MIRROR:
                    pSoftP3RX->P3RXTextureCoordMode.WrapT1 = 
                                                __GLINT_TEXADDRESS_WRAP_MIRROR;
                    pSoftP3RX->P3RXTextureIndexMode1.WrapV = 
                                                P3RX_TEXINDEXMODE_WRAP_MIRROR;
                    break;
                    
                default:
                    DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSV!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                    break;
            }

            if(( TSSTATE( iTSStage, D3DTSS_ADDRESSU ) == D3DTADDRESS_CLAMP ) ||
               ( TSSTATE( iTSStage, D3DTSS_ADDRESSV ) == D3DTADDRESS_CLAMP ))
            {
                if ((TSSTATE( iTSStage, D3DTSS_ADDRESSU ) != D3DTADDRESS_CLAMP) ||
                    (TSSTATE( iTSStage, D3DTSS_ADDRESSV ) != D3DTADDRESS_CLAMP))
                {
                    DISPDBG((ERRLVL,"Warning: One texture coord clamped, but not "
                                "the other - can't appply TextureShift"));
                }
                 
                pSoftP3RX->P4DeltaFormatControl.TextureShift1 = 
                                                            __PERMEDIA_DISABLE;
            }
            else
            {
                pSoftP3RX->P4DeltaFormatControl.TextureShift1 = 
                                                            __PERMEDIA_ENABLE;
            }

            ASSERTDD ( pFormatSurface != NULL, 
                       "** SetupTextureUnitStage: logic error: "
                       "pFormatSurace is NULL" );
            switch (pFormatSurface->DeviceFormat)
            {
                case SURF_CI8:
                    pSoftP3RX->P3RXTextureReadMode1.TextureType = 
                                    P3RX_TEXREADMODE_TEXTURETYPE_8BITINDEXED;
                    break;
                    
                case SURF_YUV422:
                    pSoftP3RX->P3RXTextureReadMode1.TextureType = 
                                    P3RX_TEXREADMODE_TEXTURETYPE_422_YVYU;
                    break;
                    
                default:
                    pSoftP3RX->P3RXTextureReadMode1.TextureType = 
                                    P3RX_TEXREADMODE_TEXTURETYPE_NORMAL;
                    break;
            }
            
            // MAG Filter
            switch(TSSTATE ( iTSStage, D3DTSS_MAGFILTER ))
            {
                case D3DTFG_POINT:
                    pSoftP3RX->P3RXTextureIndexMode1.MagnificationFilter = 
                                            __GLINT_TEXTUREREAD_FILTER_NEAREST;
                    break;
                    
                case D3DTFG_LINEAR:
                    pSoftP3RX->P3RXTextureIndexMode1.MagnificationFilter = 
                                            __GLINT_TEXTUREREAD_FILTER_LINEAR;
                    break;
                    
                case D3DTFG_FLATCUBIC:
                case D3DTFG_GAUSSIANCUBIC:
                case D3DTFG_ANISOTROPIC:
                    DISPDBG((ERRLVL,"ERROR: Unsupported MAGFILTER!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_FILTER );
                    break;
                    
                default:
                    DISPDBG((ERRLVL,"ERROR: Unknown MAGFILTER!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                    break;
            }

            switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
            {
                case D3DTFN_POINT:
                    pSoftP3RX->P3RXTextureIndexMode1.MinificationFilter = 
                                            __GLINT_TEXTUREREAD_FILTER_NEAREST;
                    break;
                case D3DTFN_LINEAR:
                    pSoftP3RX->P3RXTextureIndexMode1.MinificationFilter = 
                                            __GLINT_TEXTUREREAD_FILTER_LINEAR;
                    break;
                case D3DTFN_ANISOTROPIC:
                    DISPDBG((ERRLVL,"ERROR: Unsupported MINFILTER!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_FILTER );
                    break;
                default:
                    DISPDBG((ERRLVL,"ERROR: Unknown MINFILTER!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                    break;
            }

            switch(TSSTATE ( iTSStage, D3DTSS_MIPFILTER ))
            {
                case D3DTFP_NONE:
                    // No need to set the minification filter
                    // it was done above
                    break;
                    
                case D3DTFP_LINEAR:
                case D3DTFP_POINT:
                    if( bBothTexturesValid )
                    {
                        if ( TSSTATE ( iTSStage, D3DTSS_MIPFILTER ) == 
                                                                D3DTFP_LINEAR )
                        {
                            // Can't do trilinear with both textures 
                            // - fall back to per-poly.
                            SET_BLEND_ERROR ( pContext,  BS_INVALID_FILTER );
                        }

                        // We can only do per-poly mipmapping while 
                        // multi-texturing, so don't enable 
                        //inter-map filtering.

                        switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
                        {
                            case D3DTFN_POINT:
                                // Point Min, Point Mip
                                pSoftP3RX->P3RXTextureIndexMode1.MinificationFilter =
                                                                        __GLINT_TEXTUREREAD_FILTER_NEARMIPNEAREST;
                                break;
                                
                            case D3DTFN_LINEAR:
                                // Linear Min, Point Mip
                                pSoftP3RX->P3RXTextureIndexMode1.MinificationFilter =
                                                                        __GLINT_TEXTUREREAD_FILTER_LINEARMIPNEAREST;
                                break;
                        }
                    }
                    else
                    {
                        DISPDBG((ERRLVL,"** Setting up the second stage, but "
                                     "only one texture is valid"));
                    }
                    break;
                    
                default:
                    DISPDBG((ERRLVL,"ERROR: Invalid Mip filter!"));
                    SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                    break;
            }

            // MipMapping
            // If the app chooses to have two mip-mapped textures or a 
            // single mip-mapped texture in stage 1 they only get 
            // per-poly mipmapping.
            if( (TSSTATE ( iTSStage, D3DTSS_MIPFILTER ) != D3DTFP_NONE) && 
                 pTexture->bMipMap )
            {
                int iLOD, iTexLOD;

                // Load the mipmap levels for texture 1
                // Mip level from pMipBases->dwTex1ActMaxLevel to
                // pTexture->iMipLevels will be mapped to base address slot
                // from pMipBases->dwTex1Mipbase to dwTex1MipMax
                iLOD = pMipBases->dwTex1MipBase;
                iTexLOD = pMipBases->dwTex1ActMaxLevel;
                iT1MaxLevel = iTexLOD;
 
                P3_ENSURE_DX_SPACE(32);
                WAIT_FIFO(32);

                while(( iTexLOD < pTexture->iMipLevels ) && 
                      ( iLOD <= (int)pMipBases->dwTex1MipMax ))
                {
                    DISPDBG((DBGLVL, "  Setting Texture Base Address %d to 0x%x", 
                                iLOD, 
                                pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase));
                                
                    pSoftP3RX->P3RXTextureMapWidth[iLOD] = 
                                pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;

#if DX7_TEXMANAGEMENT
                    // If this is a driver managed texture surface, we need 
                    // to use our privately allocated mem ptr
                    if (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
                    {                        
                        SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                            (DWORD)D3DTMMIPLVL_GETOFFSET(pTexture->MipLevels[iTexLOD], pThisDisplay),
                                            iLOD);            
                    }
                    else
#endif   
                    {
                        SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                            pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase, 
                                            iLOD);
                    }   
                    
                    iLOD++;
                    iTexLOD++;
                }

                pSoftP3RX->P3RXTextureCoordMode.EnableLOD = __PERMEDIA_DISABLE;
                pSoftP3RX->P3RXTextureCoordMode.EnableDY = __PERMEDIA_DISABLE;
                pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap1 = 
                                                            __PERMEDIA_ENABLE;
                pSoftP3RX->P3RXTextureIndexMode1.MipMapEnable = 
                                                            __PERMEDIA_ENABLE;

                P3_ENSURE_DX_SPACE(2);
                WAIT_FIFO(2);
                {
                    DWORD d;

                    *(float *)&d = 
                            pContext->MipMapLODBias[TEXSTAGE_1] *
                            pTexture->dwPixelPitch *
                            pTexture->wHeight;

                    SEND_P3_DATA(TextureLODScale1, d);
                }
            }
            else
            {
                int iTexLOD;

                // Use the maximum level indicated by D3DTSS_MAXMIPLEVEL
                iTexLOD = pMipBases->dwTex0ActMaxLevel;
                iT1MaxLevel = iTexLOD;

                pSoftP3RX->P3RXTextureCoordMode.EnableLOD = __PERMEDIA_DISABLE;
                pSoftP3RX->P3RXTextureCoordMode.EnableDY = __PERMEDIA_DISABLE;
                pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap1 = 
                                                            __PERMEDIA_DISABLE;
                pSoftP3RX->P3RXTextureIndexMode1.MipMapEnable = 
                                                            __PERMEDIA_DISABLE;

                P3_ENSURE_DX_SPACE(2);
                WAIT_FIFO(2);

#if DX7_TEXMANAGEMENT
                    // If this is a driver managed texture surface, we need 
                    // to use our privately allocated mem ptr
                    if (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
                    {                        
                        SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                            (DWORD)D3DTMMIPLVL_GETOFFSET(pTexture->MipLevels[iTexLOD], pThisDisplay),
                                            pMipBases->dwTex1MipBase);            
                    }
                    else
#endif
                    {
                        SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                            pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase,
                                            pMipBases->dwTex1MipBase);
                    }
                    
                // No mipmapping.
                pSoftP3RX->P3RXTextureMapWidth[pMipBases->dwTex1MipBase] = 
                                    pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;
            }


            ASSERTDD ( pFormatSurface != NULL, 
                       "** SetupTextureUnitStage: logic error: "
                       "pFormatSurace is NULL" );
                       
            if (pFormatSurface->DeviceFormat == SURF_CI8)
            {
                // In the future, this will work as long as texture 0 isn't
                // palettised, or if they share the palette.
                // But that needs some restructuring - the whole LUT setup 
                // should be in a single bit of code in _D3DChangeTextureP3RX, 
                // since it is a shared resource.
                DISPDBG((ERRLVL,"** SetupTextureUnitStage: allow second texture "
                             "to use LUTs"));
                              
                // For now, fail.
                SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_PALETTES );
            }

            P3_DMA_COMMIT_BUFFER();
            __TXT_ConsiderSrcChromaKey( pContext, pTexture, 1 );
            P3_DMA_GET_BUFFER();

            // Setup TextureReadMode
            pSoftP3RX->P3RXTextureReadMode1.MapBaseLevel = 
                                                    pMipBases->dwTex1MipBase;
            pSoftP3RX->P3RXTextureReadMode1.MapMaxLevel = 
                                                    pMipBases->dwTex1MipMax;
            pSoftP3RX->P3RXTextureReadMode1.Width = pTexture->MipLevels[iT1MaxLevel].logWidth;
            pSoftP3RX->P3RXTextureReadMode1.Height = pTexture->MipLevels[iT1MaxLevel].logHeight;
            pSoftP3RX->P3RXTextureReadMode1.TexelSize = pTexture->dwPixelSize;

            pSoftP3RX->P3RXTextureReadMode1.LogicalTexture = 
                                                            __PERMEDIA_DISABLE;
            
            // Enable the texture index unit (this is a bit like the 
            // the texture read)
            pSoftP3RX->P3RXTextureIndexMode1.Width = pTexture->MipLevels[iT1MaxLevel].logWidth;
            pSoftP3RX->P3RXTextureIndexMode1.Height = pTexture->MipLevels[iT1MaxLevel].logHeight;
            ASSERTDD ( pFormatSurface != NULL, 
                       "** SetupTextureUnitStage: logic error: "
                       "pFormatSurace is NULL" );
            pSoftP3RX->P3RXTextureFilterMode.Format1 = 
                                                pFormatSurface->FilterFormat;

            // Enable stage 1
            pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_ENABLE;
            pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_ENABLE;

#if DX7_PALETTETEXTURE
#if 0
            // D3DValidateDeviceP3() will return error code for this case
            ASSERTDD((pFormatSurface->DeviceFormat != SURF_CI8 && 
                      pFormatSurface->DeviceFormat != SURF_CI4),
                     "Texture surface can't be palettized when using a "
                     "second map!");
#endif
#endif

            break;
        }
    }

    P3_DMA_COMMIT_BUFFER();

    return TRUE;
} // __TXT_SetupTexture

//-----------------------------------------------------------------------------
//
// __bD3DTexturesMatch
//
//
// A function to compare the two textures in two D3D stages, and determine
// if they could be satisfied by the same on-chip texture.
//
// int iStage1              D3D stage number of first texture.
// int iStage2              D3D stage number of second texture.
// *pContext                The context.
//
// result:                  TRUE if the textures match, FALSE if they don't.
//
// An ASSERT is triggered if either stage is not using a texture. In the 
// release build, the result will be TRUE, meaning that we could pack both 
// textures stages requirements into one texture (because one or both do 
// not use a texture).
//
//-----------------------------------------------------------------------------
BOOL 
__bD3DTexturesMatch ( 
    int iStage1, 
    int iStage2, 
    P3_D3DCONTEXT* pContext )
{
    ASSERTDD ( iStage1 != iStage2, 
               "** __bD3DTexturesMatch: both stages are the same "
               "- pointless comparison!" );
               
    if ( TSSTATE ( iStage1, D3DTSS_TEXTUREMAP ) == 0 )
    {
        DISPDBG((ERRLVL,"** __bD3DTexturesMatch: first considered stage's "
                     "texture is NULL"));
                      
        return ( TRUE );
    }
    else if ( TSSTATE ( iStage2, D3DTSS_TEXTUREMAP ) == 0 )
    {
        DISPDBG((ERRLVL,"** __bD3DTexturesMatch: second considered stage's "
                      "texture is NULL"));
        return ( TRUE );
    }
    else
    {
        #define CHECK_EQUALITY(name) ( TSSTATE ( iStage1, name ) == TSSTATE ( iStage2, name ) )
        if (CHECK_EQUALITY ( D3DTSS_TEXTUREMAP ) &&
            CHECK_EQUALITY ( D3DTSS_TEXCOORDINDEX ) )
        {
            // Yes, the textures have the same handle and coord set. Do 
            // some further checks.

            // If the pointers are different, or the texcoord sets are 
            // different (for bumpmapping), this is a common occurrance, 
            // and need not be flagged. However, if they are the same, 
            // but a filter mode or something like that is different,
            // it is likely to be an app bug, so flag it.

            if (
                // Should not need to check ADDRESS 
                // - should have been mirrored to ADDRESS[UV].
                CHECK_EQUALITY ( D3DTSS_ADDRESSU ) &&
                CHECK_EQUALITY ( D3DTSS_ADDRESSV ) &&
                CHECK_EQUALITY ( D3DTSS_MAGFILTER ) &&
                CHECK_EQUALITY ( D3DTSS_MINFILTER ) &&
                CHECK_EQUALITY ( D3DTSS_MIPFILTER ) )
                // I should also check all the other variables like 
                // MIPMAPLODBIAS, but they rely on mipmapping being 
                // enabled, etc, so it's more of a hassle. If an app 
                // really does manage to be this perverse, it's doing well!
            {
                // Looks good.
                return ( TRUE );
            }
            else
            {
                // Well, the texcoords agree and the handle agree, but the 
                // others don't. I bet this is an app bug - you are unlikely 
                // to do this deliberately.
                _D3DDisplayWholeTSSPipe ( pContext, WRNLVL );
                DISPDBG((ERRLVL,"** __bD3DTexturesMatch: textures agree in "
                              "handle and texcoord, but not other things - "
                              "likely app bug."));
                return ( FALSE );
            }
        }
        else
        {
            // No, different textures.
            return ( FALSE );
        }
        #undef CHECK_EQUALITY
    }
    return TRUE;
} // __bD3DTexturesMatch  

//-----------------------------------------------------------------------------
//
// _D3DChangeTextureP3RX
//
// This function does whole setup of necessary texturing state  according to
// the current renderestates and texture stage states. Disables texturing
// accordingly if this is needed.
//
//-----------------------------------------------------------------------------

void 
_D3DChangeTextureP3RX(
    P3_D3DCONTEXT* pContext)
{
    P3_SURF_INTERNAL* pTexture0 = NULL;
    P3_SURF_INTERNAL* pTexture1 = NULL;
    P3_THUNKEDDATA * pThisDisplay = pContext->pThisDisplay;
    P3_SOFTWARECOPY* pSoftP3RX = &pContext->SoftCopyGlint;
    P3_MIP_BASES mipBases;
    DWORD* pFlags = &pContext->Flags;   
    INT i, iLastChipStage;
    DWORD dwT0MipLevels, 
          dwT1MipLevels,
          dwTexAppTfactor, 
          dwTexComp0Tfactor, 
          dwTexComp1Tfactor;
    BOOL bBothTexturesValid,
         bProcessChipStage0, 
         bProcessChipStage1, 
         bProcessChipStage2,
         bAlphaBlendDouble;

    P3_DMA_DEFS();

    DBG_ENTRY(_D3DChangeTextureP3RX);  

    pContext->iTexStage[0] = -1;
    pContext->iTexStage[1] = -1;
    // This is checked against the current state at the end of the routine.
    bAlphaBlendDouble = FALSE;

    // Verify if texturing should be disabled
    if ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) == D3DTOP_DISABLE ) ||
         ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_TEXTUREMAP ) == 0 ) &&
           ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLOROP ) == D3DTOP_DISABLE ) &&
           ( ( ( TSSTATESELECT ( TEXSTAGE_0, D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) &&
               ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) != D3DTOP_SELECTARG2 ) ) ||
             ( ( TSSTATESELECT ( TEXSTAGE_0, D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) &&
               ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) != D3DTOP_SELECTARG1 ) ) )
         ) )
    {
        // Stage 0 is disabled, so they just want the diffuse colour.
        // Or, the texture handle is 0 , stage 1 is D3DTOP_DISABLE and in stage 
        // 0 we are selecting an arg that is not a D3DTA_TEXTURE
       
        DISPDBG((DBGLVL, "All composite units disabled - setting diffuse colour"));
        
        P3_DMA_GET_BUFFER_ENTRIES(20);

        // Turn off texture address generation
        pSoftP3RX->P3RXTextureCoordMode.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(TextureCoordMode, pSoftP3RX->P3RXTextureCoordMode);
    
        // Turn off texture reads
        pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(TextureReadMode0, pSoftP3RX->P3RXTextureReadMode0);
        COPY_P3_DATA(TextureReadMode1, pSoftP3RX->P3RXTextureReadMode1);
        pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(TextureIndexMode0, pSoftP3RX->P3RXTextureIndexMode0);
        COPY_P3_DATA(TextureIndexMode1, pSoftP3RX->P3RXTextureIndexMode1);

        // Turn off the texture filter mode unit
        pSoftP3RX->P3RXTextureFilterMode.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(TextureFilterMode, pSoftP3RX->P3RXTextureFilterMode);
        
        // Turn off texture color mode unit
        pSoftP3RX->P3RXTextureApplicationMode.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(TextureApplicationMode, 
                     pSoftP3RX->P3RXTextureApplicationMode);

        // Not compositing
        SEND_P3_DATA(TextureCompositeMode, __PERMEDIA_DISABLE);

        *pFlags &= ~SURFACE_TEXTURING;

        pSoftP3RX->P3RXLUTMode.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(LUTMode, pSoftP3RX->P3RXLUTMode);

        // Specular texture can be enabled without texturing on
        COPY_P3_DATA(DeltaMode, pSoftP3RX->P3RX_P3DeltaMode);

        P3_DMA_COMMIT_BUFFER();
    
        // Turn off texturing in the render command
        RENDER_TEXTURE_DISABLE(pContext->RenderCommand);

        pContext->bTextureValid = TRUE;
        pContext->pCurrentTexture[0] = NULL;
        pContext->pCurrentTexture[1] = NULL;

        // Track just for debugging purpouses
        pContext->bTexDisabled = TRUE;

        bAlphaBlendDouble = FALSE;
        if ( bAlphaBlendDouble != pContext->bAlphaBlendMustDoubleSourceColour )
        {
            pContext->bAlphaBlendMustDoubleSourceColour = bAlphaBlendDouble;
            DIRTY_ALPHABLEND(pContext);
        }

        DBG_EXIT(_D3DChangeTextureP3RX,1);  
        return;
    }

    if ( TSSTATE ( TEXSTAGE_0, D3DTSS_TEXTUREMAP ) == 0 )
    if ( ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLORARG1 ) == D3DTA_TFACTOR ) &&
           ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) ) ||
         ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLORARG2 ) == D3DTA_TFACTOR ) &&
           ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) == D3DTOP_SELECTARG2 ) ) )
    {
        // This is an unusual way to set up the diffuse color : take
        // it from the the D3DTA_TFACTOR. But some apps use it.
        // we need to treat it separately for the Perm3 setup because
        // it might not be binded with any texture

        DISPDBG((DBGLVL, "Diffuse color comes from D3DTA_TFACTOR"));

        P3_DMA_GET_BUFFER_ENTRIES(30);

        // Turn off texture address generation
        pSoftP3RX->P3RXTextureCoordMode.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(TextureCoordMode, pSoftP3RX->P3RXTextureCoordMode);
    
        // Turn off texture reads
        pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(TextureReadMode0, pSoftP3RX->P3RXTextureReadMode0);
        COPY_P3_DATA(TextureReadMode1, pSoftP3RX->P3RXTextureReadMode1);
        pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(TextureIndexMode0, pSoftP3RX->P3RXTextureIndexMode0);
        COPY_P3_DATA(TextureIndexMode1, pSoftP3RX->P3RXTextureIndexMode1);

        // Turn off the texture filter mode unit
        pSoftP3RX->P3RXTextureFilterMode.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(TextureFilterMode, pSoftP3RX->P3RXTextureFilterMode);

        // Setup texture color mode unit            
        pSoftP3RX->P3RXTextureApplicationMode.Enable = __PERMEDIA_ENABLE;
        pSoftP3RX->P3RXTextureApplicationMode.ColorA = P3RX_TEXAPP_A_KC;
        pSoftP3RX->P3RXTextureApplicationMode.ColorOperation = P3RX_TEXAPP_OPERATION_PASS_A; 
        pSoftP3RX->P3RXTextureApplicationMode.AlphaA = P3RX_TEXAPP_A_KA;                   
        pSoftP3RX->P3RXTextureApplicationMode.AlphaOperation = P3RX_TEXAPP_OPERATION_PASS_A; 
                
        COPY_P3_DATA(TextureApplicationMode, 
                     pSoftP3RX->P3RXTextureApplicationMode);

        // Setup compositing

        pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg1 = P3RX_TEXCOMP_FA;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE; 
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG1;  
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;          
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;          
        COPY_P3_DATA(TextureCompositeAlphaMode0, 
                     pSoftP3RX->P3RXTextureCompositeAlphaMode0);

        pSoftP3RX->P3RXTextureCompositeColorMode0.Enable = __PERMEDIA_ENABLE;
        pSoftP3RX->P3RXTextureCompositeColorMode0.Arg1 = P3RX_TEXCOMP_FC;
        pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg1 = __PERMEDIA_DISABLE; 
        pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG1;  
        pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;          
        pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;          
        COPY_P3_DATA(TextureCompositeColorMode0, 
                     pSoftP3RX->P3RXTextureCompositeColorMode0);
                     
        pSoftP3RX->P3RXTextureCompositeAlphaMode1.Enable = __PERMEDIA_DISABLE;                
        COPY_P3_DATA(TextureCompositeAlphaMode1, 
                     pSoftP3RX->P3RXTextureCompositeAlphaMode1);

        pSoftP3RX->P3RXTextureCompositeColorMode1.Enable = __PERMEDIA_DISABLE;                
        COPY_P3_DATA(TextureCompositeColorMode1, 
                     pSoftP3RX->P3RXTextureCompositeColorMode1);
                     
        SEND_P3_DATA(TextureCompositeMode, __PERMEDIA_ENABLE);

        *pFlags &= ~SURFACE_TEXTURING;

        pSoftP3RX->P3RXLUTMode.Enable = __PERMEDIA_DISABLE;
        COPY_P3_DATA(LUTMode, pSoftP3RX->P3RXLUTMode);

        // Specular texture can be enabled without texturing on
        COPY_P3_DATA(DeltaMode, pSoftP3RX->P3RX_P3DeltaMode);

        P3_DMA_COMMIT_BUFFER();

    
    
        // Turn off texturing in the render command
        // RENDER_TEXTURE_DISABLE(pContext->RenderCommand);
        RENDER_TEXTURE_ENABLE(pContext->RenderCommand);

        pContext->bTextureValid = TRUE;
        pContext->pCurrentTexture[0] = NULL;
        pContext->pCurrentTexture[1] = NULL;

        // Track just for debugging purpouses
        pContext->bTexDisabled = FALSE;

        
        DBG_EXIT(_D3DChangeTextureP3RX,1);  
        return;
    }

    // Track just for debugging purpouses
    pContext->bTexDisabled = FALSE;

    // Dump to the debugger our current TSS setup
    _D3DDisplayWholeTSSPipe(pContext, DBGLVL);

    // Deal with the textures.

    // Find the texture mappings. If D3D stage 0 uses a texture, it must 
    // always be chip texture 0 to keep the bumpmap working. Fortunately, 
    // this is the only non-orthogonal case, so everything else can cope 
    // with this restriction.
    
    for ( i = TEXSTAGE_0; i < D3DTSS_MAX; i++ )
    {
        if ( TSSTATE ( i, D3DTSS_COLOROP ) == D3DTOP_DISABLE )
        {
            // Finished processing.
            break;
        }

        // This code could be slightly optimised - if a texture is set up, 
        // but none of the relevant arguments are TEXTURE (with additional 
        // flags), then of course we don't need to set the texture up at all.
        // Normally, both arguments are "relevant", but with SELECTARG1 and 
        // SELECTARG2, one of them is not. Also, watch out for PREMODULATE - 
        // it is an implicit reference to a stage's texture.

        if (
            ( TSSTATE ( i, D3DTSS_TEXTUREMAP ) == 0 ) ||
            ( (
                ( ( TSSTATESELECT ( i, D3DTSS_COLORARG1 ) != D3DTA_TEXTURE ) || 
                  ( TSSTATE ( i, D3DTSS_COLOROP ) == D3DTOP_SELECTARG2     ) ) &&
                ( ( TSSTATESELECT ( i, D3DTSS_COLORARG2 ) != D3DTA_TEXTURE ) || 
                  ( TSSTATE ( i, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1     ) ) &&
                ( ( TSSTATESELECT ( i, D3DTSS_ALPHAARG1 ) != D3DTA_TEXTURE ) || 
                  ( TSSTATE ( i, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2     ) ) &&
                ( ( TSSTATESELECT ( i, D3DTSS_ALPHAARG2 ) != D3DTA_TEXTURE ) || 
                  ( TSSTATE ( i, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1     ) )
              ) &&
              ( TSSTATE ( i, D3DTSS_COLOROP ) != D3DTOP_PREMODULATE ) &&
              ( TSSTATE ( i-1, D3DTSS_COLOROP ) != D3DTOP_PREMODULATE )
            ) )
        {
            // This D3D stage doesn't use a texture.
            pContext->iStageTex[i] = -1;
        }
        else
        {
            // Note that the below code should be put into a little loop
            // for any future devices that have more than 2 textures, otherwise
            // the code will get big, nested and crufty. But for only 2, it's
            // manageable, and slightly faster this way.

            // A texture is used - is texture 0 free?
            if ( pContext->iTexStage[0] == -1 )
            {
                // Texture 0 is free - make it this stage.
                ASSERTDD ( pContext->iTexStage[1] == -1, 
                           "** _D3DChangeTextureP3RX: pContext->iTexStage[1] "
                           "should be -1 if pContext->iTexStage[0] is" );
                pContext->iTexStage[0] = i;
                pContext->iStageTex[i] = 0;
            }
            else
            {
                // Texture 0 is assigned - see if this is the same as it.
                if ( __bD3DTexturesMatch ( i, 
                                           pContext->iTexStage[0], 
                                           pContext ) )
                {
                    // Yes, they match - no need to use texture 1.
                    pContext->iStageTex[i] = 0;
                }
                else
                {
                    // No, they don't match. Is texture 1 free?
                    if ( pContext->iTexStage[1] == -1 )
                    {
                        // Texture 1 is free - make it this stage.
                        ASSERTDD ( pContext->iTexStage[0] != -1, 
                                   "** _D3DChangeTextureP3RX: "
                                   "pContext->iTexStage[0] should not be "
                                   "-1 if pContext->iTexStage[1] is not." );
                        pContext->iTexStage[1] = i;
                        pContext->iStageTex[i] = 1;
                    }
                    else
                    {
                        // Texture 1 is assigned - see if this is the same 
                        // as it.
                        if ( __bD3DTexturesMatch ( i, 
                                                   pContext->iTexStage[1], 
                                                   pContext ) )
                        {
                            // Yes, they match - mark it.
                            pContext->iStageTex[i] = 1;
                        }
                        else
                        {
                            // No, they don't match, and both chip textures 
                            // have been assigned. Fail a ValidateDevice().
                            DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: app tried "
                                         "to use more than two textures."));
                            SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_TEXTURES );
                            pContext->iStageTex[i] = -1;
                        }
                    }
                }
            }
        }

        // A quick sanity check.
#if DBG
        if ( TSSTATE ( i, D3DTSS_TEXTUREMAP ) == 0 )
        {
            // That's fine, then.
            ASSERTDD ( pContext->iStageTex[i] == -1, 
                       "** _D3DChangeTextureP3RX: something failed with the "
                       "texture-assignment logic" );
        }
        else if ( pContext->iStageTex[i] == -1 )
        {
            // That's fine - texture may have been set up but not referenced.
        }
        else if ( pContext->iTexStage[pContext->iStageTex[i]] == -1 )
        {
            // Oops.
            DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: something failed with "
                          "the texture-assignment logic"));
        }
        else if ( pContext->iTexStage[pContext->iStageTex[i]] == i )
        {
            // That's fine, then.
        }
        else if ( __bD3DTexturesMatch ( i, 
                                        pContext->iTexStage[pContext->iStageTex[i]], 
                                        pContext ) )
        {
            // That's fine, then.
        }
        else
        {
            // Oops.
            DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: something failed with "
                          "the texture-assignment logic"));
        }
#endif // DBG
    }
    
    // And a few more gratuitous sanity checks at the end of the loop.
    ASSERTDD ( ( pContext->iTexStage[0] == -1 ) || 
               ( pContext->iStageTex[pContext->iTexStage[0]] == 0 ), 
               "** _D3DChangeTextureP3RX: something failed with the "
               "texture-assignment logic" );
               
    ASSERTDD ( ( pContext->iTexStage[1] == -1 ) || 
               ( pContext->iStageTex[pContext->iTexStage[1]] == 1 ), 
               "** _D3DChangeTextureP3RX: something failed with the "
               "texture-assignment logic" );

#if DBG
    if ( pContext->iTexStage[0] != -1 )
    {
        DISPDBG((DBGLVL, "Setting new texture0 data, Handle: 0x%x", 
                    TSSTATE ( pContext->iTexStage[0], D3DTSS_TEXTUREMAP )));
    }
    else
    {
        DISPDBG((DBGLVL, "Texture0 not used" ));
    }
    
    if ( pContext->iTexStage[1] != -1 )
    {
        DISPDBG((DBGLVL, "Setting new texture1 data, Handle: 0x%x", 
                    TSSTATE ( pContext->iTexStage[1], D3DTSS_TEXTUREMAP )));
    }
    else
    {
        DISPDBG((DBGLVL, "Texture1 not used" ));
    }
#endif //DBG
    
    // Set the texture valid flag to true.  
    // If anything resets it then the texture state is invalid.
    pContext->bTextureValid = TRUE;
    pContext->bCanChromaKey = FALSE;
    pContext->bTex0Valid = FALSE;
    pContext->bTex1Valid = FALSE;
    pContext->bStage0DotProduct = FALSE;

    // Set up the textures.
    if ( pContext->iTexStage[0] != -1 )
    {
        // Setup texture 0.
        pTexture0 = GetSurfaceFromHandle(pContext, 
                                         TSSTATE(pContext->iTexStage[0], 
                                                 D3DTSS_TEXTUREMAP) );
        if (NULL == pTexture0)
        {
            DISPDBG((ERRLVL, "ERROR: Texture Surface (0) is NULL"));
            DBG_EXIT(_D3DChangeTextureP3RX,1);  
            return;            
        }
        
#if DX7_TEXMANAGEMENT  
        if (pTexture0->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
        {
            if (!_D3D_TM_Preload_Tex_IntoVidMem(pContext, pTexture0))
            {
                return; // something bad happened !!!
            }

            _D3D_TM_TimeStampTexture(pContext->pTextureManager,
                                     pTexture0);        
        }
#endif // DX7_TEXMANAGEMENT                                                 

        pContext->bTex0Valid = 
                    __TXT_ValidateTextureUnitStage(pContext, 
                                                   0, 
                                                   pContext->iTexStage[0],
                                                   pTexture0 );
        if ( !pContext->bTex0Valid )
        {
            SET_BLEND_ERROR ( pContext,  BSF_INVALID_TEXTURE );
            // Pretend that no texture was set.
            pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_DISABLE;
            pContext->bTex0Valid = FALSE;
            pTexture0 = NULL;
        }
    }
    else
    {
        pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_DISABLE;
        pContext->bTex0Valid = FALSE;
        pTexture0 = NULL;
    }

    if ( pContext->iTexStage[1] != -1 )
    {
        // Setup texture 1.
        if ( pContext->iTexStage[0] == -1 )
        {
            DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: Should not be "
                          "using tex1 if tex0 not used."));
            SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_BLEND_STAGES );
        }
        
        pTexture1 = GetSurfaceFromHandle(pContext, 
                                         TSSTATE ( pContext->iTexStage[1],
                                                   D3DTSS_TEXTUREMAP ) );
        if (NULL == pTexture1)
        {
            DISPDBG((ERRLVL, "ERROR: Texture Surface (1) is NULL"));
            DBG_EXIT(_D3DChangeTextureP3RX,1);  
            return;            
        }
                                                   

#if DX7_TEXMANAGEMENT  
        if (pTexture1->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
        {
            if (!_D3D_TM_Preload_Tex_IntoVidMem(pContext, pTexture1))
            {
                return; // something bad happened !!!
            }

            _D3D_TM_TimeStampTexture(pContext->pTextureManager,
                                     pTexture1);        
        }
#endif // DX7_TEXMANAGEMENT                                                     

        pContext->bTex1Valid = 
                    __TXT_ValidateTextureUnitStage(pContext, 
                                                   1, 
                                                   pContext->iTexStage[1], 
                                                   pTexture1 );
        if ( !pContext->bTex1Valid )
        {
            SET_BLEND_ERROR ( pContext,  BSF_INVALID_TEXTURE );
            // Pretend that no texture was set.
            pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_DISABLE;
            pContext->bTex1Valid = FALSE;
            pTexture1 = NULL;
        }
    }
    else
    {
        pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_DISABLE;
        pContext->bTex1Valid = FALSE;
        pTexture1 = NULL;
    }

    bBothTexturesValid = pContext->bTex0Valid && pContext->bTex1Valid;

    if( pContext->bTex0Valid )
    {
        dwT0MipLevels = pContext->TextureStageState[0].m_dwVal[D3DTSS_MAXMIPLEVEL];
#if DX7_TEXMANAGEMENT        
        if ( dwT0MipLevels < pTexture0->m_dwTexLOD)
        {
            dwT0MipLevels = pTexture0->m_dwTexLOD;
        }
#endif // DX7_TEXMANAGEMENT
        if (dwT0MipLevels > ((DWORD)(pTexture0->iMipLevels - 1))) 
        {
            // Set the actuall maximum mip level that will be used in rendering
            mipBases.dwTex0ActMaxLevel = pTexture0->iMipLevels - 1;

            dwT0MipLevels = 1;
        }
        else
        {
            // Set the actuall maximum mip level that will be used in rendering
            mipBases.dwTex0ActMaxLevel = dwT0MipLevels;

            dwT0MipLevels = pTexture0->iMipLevels - dwT0MipLevels;
        }
    }

    if( pContext->bTex1Valid )
    {
        ASSERTDD ( pContext->bTex0Valid, 
                   "** _D3DChangeTextureP3RX: tex1 should not be used "
                   "unless tex0 is used as well" );

        dwT1MipLevels = pContext->TextureStageState[1].m_dwVal[D3DTSS_MAXMIPLEVEL];
#if DX7_TEXMANAGEMENT        
        if ( dwT1MipLevels < pTexture1->m_dwTexLOD)
        {
            dwT1MipLevels = pTexture1->m_dwTexLOD;
        }
#endif // DX7_TEXMANAGEMENT        
        if (dwT1MipLevels > ((DWORD)(pTexture1->iMipLevels - 1))) 
        {
            // Set the actuall maximum mip level that will be used in rendering
            mipBases.dwTex1ActMaxLevel = pTexture1->iMipLevels - 1;

            dwT1MipLevels = 1;
        }
        else
        {
            // Set the actuall maximum mip level that will be used in rendering
            mipBases.dwTex1ActMaxLevel = dwT1MipLevels;

            dwT1MipLevels = pTexture1->iMipLevels - dwT1MipLevels;
        }

        // Enable generation of the second set of texture coordinates.
        // Strictly, we should check whether texture 0 is being used, and
        // if not move the second texture to the first (thus enabling 
        // mipmapping, etc) but that's for later.
        pSoftP3RX->P3RX_P3DeltaMode.TextureEnable1 = __PERMEDIA_ENABLE;
        pSoftP3RX->P3RX_P3DeltaControl.ShareQ = 1;
    }
    else
    {
        // Turn off generation of the second set of texture coordinates
        pSoftP3RX->P3RX_P3DeltaMode.TextureEnable1 = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RX_P3DeltaControl.ShareQ = 0;
    }

    if( bBothTexturesValid )
    {
        float totBases, baseRatio;
        DWORD t0Count, res;

        totBases = (float)dwT0MipLevels + dwT1MipLevels;

        // Adjust mip levels to fit in N - 2 slots as each texture
        // needs at least one slot.

        baseRatio = ( P3_TEX_MAP_MAX_LEVEL - 1 ) / totBases;

        // Calculate number of slots for texture 0, texture 1 then
        // gets the remainder.

        myFtoi( &res, dwT0MipLevels * baseRatio );
        t0Count = 1 + res;

        ASSERTDD( t0Count > 0, "No slots for texture 0" );
        ASSERTDD( t0Count <= P3_TEX_MAP_MAX_LEVEL, "No slots for texture 1" );

#define FIXED_ALLOC 0
#if FIXED_ALLOC
        mipBases.dwTex0MipBase = 0;
        mipBases.dwTex0MipMax  = min( dwT0MipLevels - 1, 7 );
        mipBases.dwTex1MipBase = 8;
        mipBases.dwTex1MipMax  = 8 + min( dwT1MipLevels - 1, 
                                          P3_TEX_MAP_MAX_LEVEL - 8 );
#else
        mipBases.dwTex0MipBase = 0;
        mipBases.dwTex0MipMax  = min( dwT0MipLevels - 1, t0Count - 1 );
        mipBases.dwTex1MipBase = t0Count;
        mipBases.dwTex1MipMax  = t0Count + min( dwT1MipLevels - 1, 
                                                P3_TEX_MAP_MAX_LEVEL - t0Count );
#endif
    }
    else
    {
        if( pContext->bTex0Valid )
        {
            mipBases.dwTex0MipBase = 0;
            mipBases.dwTex0MipMax  = min( dwT0MipLevels - 1, 
                                          P3_TEX_MAP_MAX_LEVEL );
            mipBases.dwTex1MipBase = 0;
            mipBases.dwTex1MipMax  = min( dwT0MipLevels - 1, 
                                          P3_TEX_MAP_MAX_LEVEL );
        }

        if( pContext->bTex1Valid )
        {
            mipBases.dwTex0MipBase = 0;
            mipBases.dwTex0MipMax  = min( dwT1MipLevels - 1, 
                                          P3_TEX_MAP_MAX_LEVEL );
            mipBases.dwTex1MipBase = 0;
            mipBases.dwTex1MipMax  = min( dwT1MipLevels - 1, 
                                          P3_TEX_MAP_MAX_LEVEL );
        }
    }

    DISPDBG(( DBGLVL, "tex0 base %d", mipBases.dwTex0MipBase ));
    DISPDBG(( DBGLVL, "tex0 max  %d", mipBases.dwTex0MipMax ));
    DISPDBG(( DBGLVL, "tex1 base %d", mipBases.dwTex1MipBase ));
    DISPDBG(( DBGLVL, "tex1 max  %d", mipBases.dwTex1MipMax ));

    // Recalculate the LOD biases for per-poly mipmapping
    pContext->MipMapLODBias[TEXSTAGE_0] =
             pow4( pContext->TextureStageState[TEXSTAGE_0].
                                                m_fVal[D3DTSS_MIPMAPLODBIAS] );

    pContext->MipMapLODBias[TEXSTAGE_1] = 
             pow4( pContext->TextureStageState[TEXSTAGE_1].
                                                m_fVal[D3DTSS_MIPMAPLODBIAS] );

    if ( pTexture0 != NULL )
    {
        __TXT_SetupTexture ( pThisDisplay, 
                             0, 
                             pContext->iTexStage[0], 
                             pContext, 
                             pTexture0, 
                             pSoftP3RX, 
                             bBothTexturesValid, 
                             &mipBases);
    }
    
    if ( pTexture1 != NULL )
    {
        __TXT_SetupTexture ( pThisDisplay, 
                             1, 
                             pContext->iTexStage[1], 
                             pContext, 
                             pTexture1, 
                             pSoftP3RX, 
                             bBothTexturesValid, 
                             &mipBases);

#if DX7_PALETTETEXTURE
        if (GET_BLEND_ERROR(pContext) == BSF_TOO_MANY_PALETTES)
        {
            if (pTexture0 && 
                (pTexture0->dwPaletteHandle == pTexture1->dwPaletteHandle)) 
            {
                RESET_BLEND_ERROR(pContext);
            }
        }
#endif
    }

    // Fix up the D3DRENDERSTATE_MODULATE case.
    if( pTexture0 != NULL )
    {
        if( pContext->Flags & SURFACE_MODULATE )
        {
            // If SURFACE_MODULATE is set then we must have seen a 
            // DX5-style texture blend
            // Note : bAlpha is true for CI8 and CI4 textures

            BOOL bSelectArg1 = pTexture0->pFormatSurface->bAlpha;

#if DX7_PALETTETEXTURE
            if( pTexture0->pixFmt.dwFlags & DDPF_PALETTEINDEXED8 )
            {
                bSelectArg1 = pTexture0->dwPaletteFlags & DDRAWIPAL_ALPHA;
            }
#endif            

            if( bSelectArg1 )
            {
                TSSTATE( pContext->iChipStage[0], D3DTSS_ALPHAOP ) = 
                                                            D3DTOP_SELECTARG1;
            }
            else
            {
                TSSTATE( pContext->iChipStage[0], D3DTSS_ALPHAOP ) = 
                                                            D3DTOP_SELECTARG2;
            }
        }
    }

    P3_DMA_GET_BUFFER();

    // Textures set up - now do the blending.

    // These might be overidden later for special blends.
    dwTexAppTfactor = pContext->RenderStates[D3DRENDERSTATE_TEXTUREFACTOR];
    dwTexComp0Tfactor = pContext->RenderStates[D3DRENDERSTATE_TEXTUREFACTOR];
    dwTexComp1Tfactor = pContext->RenderStates[D3DRENDERSTATE_TEXTUREFACTOR];

    // Detect the stage 0 & 1 bumpmap setup code.
    if (( TSSTATE ( TEXSTAGE_0, D3DTSS_TEXTUREMAP ) != 0 ) &&
        ( TSSTATE ( TEXSTAGE_1, D3DTSS_TEXTUREMAP ) != 0 ) &&
        ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLOROP ) != D3DTOP_DISABLE ) )
    {
        // Looking good for a bumpmap. Now find various special cases.
        // First of all, do they want anything in the stage 2 current colour?
        if (
            ( ( ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_COLORARG1 ) != D3DTA_CURRENT ) &&
                ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_COLORARG2 ) != D3DTA_CURRENT ) ) ||
              ( ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) &&
                ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_COLORARG1 ) != D3DTA_CURRENT ) ) ||
              ( ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLOROP ) == D3DTOP_SELECTARG2 ) &&
                ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_COLORARG2 ) != D3DTA_CURRENT ) ) ) &&

              ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) != D3DTOP_DOTPRODUCT3 ) &&
              ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLOROP ) != D3DTOP_DOTPRODUCT3 ) )
        {
            // Nope - they don't care what the current colour channel is, and
            // no dotproducts are used in stages 0 and 1 (they affect the alpha 
            // channel) so ignore what is in the colour channel - this is a 
            // bumpmap so far.

            // Now see if they want a bumpmap or an inverted bumpmap. People 
            // are so fussy.
            
            // Check first stage.
            if (( ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
                    ( TSSTATEALPHA ( TEXSTAGE_0, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) ) ||
                  ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2 ) &&
                    ( TSSTATEALPHA ( TEXSTAGE_0, D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) ) ) &&
                ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) )
            {
                // First stage fine and not inverted. Check second stage.
                if (( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) ||
                    ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) ) )
                {
                    // Fine, not inverted.
                    pContext->bBumpmapEnabled = TRUE;
                    pContext->bBumpmapInverted = FALSE;
                }
                else if (
                    ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) ) ||
                    ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) ) )
                {
                    // Fine, inverted.
                    pContext->bBumpmapEnabled = TRUE;
                    pContext->bBumpmapInverted = TRUE;
                }
                else
                {
                    // Nope, second stage is no good.
                    pContext->bBumpmapEnabled = FALSE;
                    pContext->bBumpmapInverted = FALSE;
                }
            }
            else if (
                ( ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
                    ( TSSTATEALPHA ( TEXSTAGE_0, D3DTSS_ALPHAARG1 ) == (D3DTA_TEXTURE | D3DTA_COMPLEMENT) ) ) ||
                  ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2 ) &&
                    ( TSSTATEALPHA ( TEXSTAGE_0, D3DTSS_ALPHAARG2 ) == (D3DTA_TEXTURE | D3DTA_COMPLEMENT) ) ) ) &&
                ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) )
            {
                // First stage fine and inverted. Check second stage.
                if (( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) ||
                    ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) ) )
                {
                    // Fine, inverted.
                    pContext->bBumpmapEnabled = TRUE;
                    pContext->bBumpmapInverted = TRUE;
                }
                else if (
                    ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) ) ||
                    ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) ) )
                {
                    // Fine, not inverted.
                    pContext->bBumpmapEnabled = TRUE;
                    pContext->bBumpmapInverted = FALSE;
                }
                else
                {
                    // Nope, second stage is no good.
                    pContext->bBumpmapEnabled = FALSE;
                    pContext->bBumpmapInverted = FALSE;
                }
            }
            else
            {
                // Nope, first stage is no good.
                pContext->bBumpmapEnabled = FALSE;
                pContext->bBumpmapInverted = FALSE;
            }
        }
        else
        {
            // Could do some more checking, e.g. is all they want in the current colour
            // channel easily available from a single input, e.g. tex0.c, in which case
            // that's fine. A non-bumpmap variant also needs to sense that the first
            // stage is simply a selectarg1/2 and thus can ignore the first stage as
            // a texcomp stage.
            // But that's for later.
            pContext->bBumpmapEnabled = FALSE;
            pContext->bBumpmapInverted = FALSE;
        }

    }
    else
    {
        pContext->bBumpmapEnabled = FALSE;
        pContext->bBumpmapInverted = FALSE;
    }

    if ( pContext->bBumpmapEnabled )
    {
        DISPDBG((DBGLVL,"Enabling emboss bumpmapping"));
        // Remap stages 1 & 2 out of existence.
        pContext->iChipStage[0] = TEXSTAGE_2;
        pContext->iChipStage[1] = TEXSTAGE_3;
        pContext->iChipStage[2] = TEXSTAGE_4;
        pContext->iChipStage[3] = TEXSTAGE_5;
    }
    else
    {
        // Normal mapping.
        pContext->iChipStage[0] = TEXSTAGE_0;
        pContext->iChipStage[1] = TEXSTAGE_1;
        pContext->iChipStage[2] = TEXSTAGE_2;
        pContext->iChipStage[3] = TEXSTAGE_3;
    }

    iLastChipStage = 0;
    // Set these flags to FALSE as the stages are processed.
    bProcessChipStage0 = TRUE;
    bProcessChipStage1 = TRUE;
    bProcessChipStage2 = TRUE;

    // Turn on the basic enables.
    pSoftP3RX->P3RXTextureApplicationMode.Enable = __PERMEDIA_ENABLE;
//  pSoftP3RX->P3RXTextureApplicationMode.EnableKs = __PERMEDIA_DISABLE;
    pSoftP3RX->P3RXTextureApplicationMode.EnableKd = __PERMEDIA_DISABLE;
    pSoftP3RX->P3RXTextureApplicationMode.MotionCompEnable = __PERMEDIA_DISABLE;


    // Handle chip stage 0.

    // Detect the very special-case glossmap+bumpmap code. There is no easy way
    // to generalise it, so the whole chunk gets checked here.
    if ( bProcessChipStage0 && bProcessChipStage1 && bProcessChipStage2 && pContext->bTex0Valid && pContext->bTex1Valid &&
        // Colour channel of stage 0 can be whatever you want.
        ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLOROP ) == D3DTOP_MODULATEALPHA_ADDCOLOR ) &&  // Early-out test - nothing uses this!
        ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
        ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAARG1 ) == D3DTA_DIFFUSE ) &&
        ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&
        ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) &&
        ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
        ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
        ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) &&
        ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&
        ( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) &&
        ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
        ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) &&
        ( TSSTATE ( TEXSTAGE_3, D3DTSS_COLOROP ) == D3DTOP_MODULATE2X ) &&
        ( TSSTATE ( TEXSTAGE_3, D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&
        ( TSSTATE ( TEXSTAGE_3, D3DTSS_COLORARG2 ) == (D3DTA_CURRENT | D3DTA_ALPHAREPLICATE) ) &&
        ( TSSTATE ( TEXSTAGE_3, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
        ( TSSTATE ( TEXSTAGE_3, D3DTSS_ALPHAARG1 ) == D3DTA_TFACTOR ) &&
        ( TSSTATE ( TEXSTAGE_4, D3DTSS_COLOROP ) == D3DTOP_DISABLE ) &&
        ( pContext->iStageTex[0] == 0 ) &&
        ( pContext->iStageTex[1] == 1 ) &&
        ( pContext->iStageTex[2] == 0 )
        )
    {
        int iMode;
        // OK, looks good. Check which way round the bumpmapping is being done.
        if (( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
            ( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAARG2 ) == (D3DTA_CURRENT | D3DTA_COMPLEMENT) ) )
        {
            // Standard emboss.
            iMode = 0;
        }
        else if (( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAARG1 ) == (D3DTA_TEXTURE | D3DTA_COMPLEMENT) ) &&
                 ( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) )
        {
            // Inverted emboss.
            iMode = 1;
        }
        else
        {
            // No good - can't do it.
            iMode = -1;
        }

        if ( iMode == -1 )
        {
            // Nope.
            SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_BLEND_STAGES );
            bProcessChipStage0 = FALSE;
            bProcessChipStage1 = FALSE;
            bProcessChipStage2 = FALSE;
            iLastChipStage = 3;
        }
        else
        {
            // Set up the colour channel of tc0.
            // Alpha channel will be overridden later.
            __TXT_TranslateToChipBlendMode(pContext, 
                                           &pContext->TextureStageState[0], 
                                           pSoftP3RX, 
                                           0, 
                                           0);

            // Pass through bump.a, maybe inverted.
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg1 = P3RX_TEXCOMP_HEIGHTA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg2 = P3RX_TEXCOMP_CA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.I = P3RX_TEXCOMP_I_CA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG1;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.B = P3RX_TEXCOMP_ARG2;
            if ( iMode )
            {
                // Inverted bumpmap.
                pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_ENABLE;
            }
            else
            {
                // Non-inverted bumpmap.
                pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE;
            }
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg2 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertI = __PERMEDIA_DISABLE;

            // Do tex1.c * diff.a + current.c
            pSoftP3RX->P3RXTextureCompositeColorMode1.Enable = __PERMEDIA_ENABLE;
            pSoftP3RX->P3RXTextureCompositeColorMode1.Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B;
            pSoftP3RX->P3RXTextureCompositeColorMode1.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
            pSoftP3RX->P3RXTextureCompositeColorMode1.Arg1 = P3RX_TEXCOMP_T1C;
            pSoftP3RX->P3RXTextureCompositeColorMode1.Arg2 = P3RX_TEXCOMP_OC;
            pSoftP3RX->P3RXTextureCompositeColorMode1.I = P3RX_TEXCOMP_I_CA;
            pSoftP3RX->P3RXTextureCompositeColorMode1.A = P3RX_TEXCOMP_ARG1;
            pSoftP3RX->P3RXTextureCompositeColorMode1.B = P3RX_TEXCOMP_ARG2;
            pSoftP3RX->P3RXTextureCompositeColorMode1.InvertArg1 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeColorMode1.InvertArg2 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeColorMode1.InvertI = __PERMEDIA_DISABLE;

            // Pass through bump.a again.
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.Enable = __PERMEDIA_ENABLE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.Arg1 = P3RX_TEXCOMP_OA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.Arg2 = P3RX_TEXCOMP_CA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.I = P3RX_TEXCOMP_I_CA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.A = P3RX_TEXCOMP_ARG1;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.B = P3RX_TEXCOMP_ARG2;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.InvertArg1 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.InvertArg2 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.InvertI = __PERMEDIA_DISABLE;

            // Do current.c * current.a, by doing B*I+A. A=black, B=current.c, I=current.a
            pSoftP3RX->P3RXTextureApplicationMode.ColorA = P3RX_TEXAPP_A_KC;
            pSoftP3RX->P3RXTextureApplicationMode.ColorB = P3RX_TEXAPP_B_TC;
            pSoftP3RX->P3RXTextureApplicationMode.ColorI = P3RX_TEXAPP_I_TA;
            pSoftP3RX->P3RXTextureApplicationMode.ColorInvertI = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureApplicationMode.ColorOperation = P3RX_TEXAPP_OPERATION_MODULATE_BI_ADD_A;
            // Set the colour channel to black (allow the alpha channel to be preserved).
            dwTexAppTfactor &= 0xff000000;

            // Alpha channel selects the constant color.
            pSoftP3RX->P3RXTextureApplicationMode.AlphaA = P3RX_TEXAPP_A_KA;
            pSoftP3RX->P3RXTextureApplicationMode.AlphaB = P3RX_TEXAPP_B_KA;
            pSoftP3RX->P3RXTextureApplicationMode.AlphaInvertI = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureApplicationMode.AlphaOperation = P3RX_TEXAPP_OPERATION_PASS_B;
            
            // Do *2 in alpha-blend unit.
            bAlphaBlendDouble = TRUE;

            // We don't actually need the remap (and it doesn't mean much),
            // but it stops erroneous errors being flagged.
            pContext->iChipStage[0] = TEXSTAGE_0;
            pContext->iChipStage[1] = TEXSTAGE_1;
            pContext->iChipStage[2] = TEXSTAGE_3;
            pContext->iChipStage[3] = TEXSTAGE_4;

            bProcessChipStage0 = FALSE;
            bProcessChipStage1 = FALSE;
            bProcessChipStage2 = FALSE;
            iLastChipStage = 3;
        }
    }



    // Detect the special-case 3-blend-unit bumpmapping mode.
    // Third stage will be set up by the standard routines - only the first
    // two are special-cased and shoehorned into TexComp0.
    if ( bProcessChipStage0 && !pContext->bBumpmapEnabled && pContext->bTex0Valid && pContext->bTex1Valid &&
        ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) == D3DTOP_MODULATE ) &&
        ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) &&
        ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLORARG2 ) == D3DTA_DIFFUSE ) &&
        ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) &&
        ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&

        ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
        ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
        // ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAARG2 ) == D3DTA_DIFFUSE ) dont care && 

        ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) &&
        (
          ( ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) &&
            ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) ||
          ( ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
            ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) )
        ) )
    {
        // Yep, looks good. Set it up.
        ASSERTDD ( pContext->iTexStage[0] == 0, "** _D3DChangeTextureP3RX: textures not correct for special bumpmapping" );
        ASSERTDD ( pContext->iTexStage[1] == 1, "** _D3DChangeTextureP3RX: textures not correct for special bumpmapping" );

        pSoftP3RX->P3RXTextureCompositeColorMode0.Enable = __PERMEDIA_ENABLE;
        pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;
        pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
        pSoftP3RX->P3RXTextureCompositeColorMode0.Arg1 = P3RX_TEXCOMP_T0C;
        pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_CC;
        pSoftP3RX->P3RXTextureCompositeColorMode0.I = P3RX_TEXCOMP_I_CA;
        pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG1;
        pSoftP3RX->P3RXTextureCompositeColorMode0.B = P3RX_TEXCOMP_ARG2;
        pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg1 = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg2 = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_DISABLE;

        pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg1 = P3RX_TEXCOMP_HEIGHTA;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg2 = P3RX_TEXCOMP_CA;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.I = P3RX_TEXCOMP_I_CA;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG1;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.B = P3RX_TEXCOMP_ARG2;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg2 = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertI = __PERMEDIA_DISABLE;

        if ( ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
             ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) )
        {
            // Inverted bumpmap.
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_ENABLE;
        }
        else
        {
            // Normal bumpmap.
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE;
        }

        // Done chip stage 0, TSS stage 0 & 1. Move chip stage 1 on a notch.
        pContext->iChipStage[0] = TEXSTAGE_0;
        pContext->iChipStage[1] = TEXSTAGE_2;
        pContext->iChipStage[2] = TEXSTAGE_3;
        pContext->iChipStage[3] = TEXSTAGE_4;
        iLastChipStage = 1;
        bProcessChipStage0 = FALSE;
    }

    // Detect a chipstage 0 MODULATE+ADD concatenation. Used by lightmaps.
    // This compresses two stages into texcomp0. The alpha channel has
    // two modes - either one of the two stages just does a selectarg1 (current),
    // and the other gets set up as normal, or (for specular stuff) they
    // both do ADDSIGNED (cur, cur), in which case it's special-cased.
    if ( bProcessChipStage0 && pContext->bBumpmapEnabled &&
        ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLOROP ) == D3DTOP_MODULATE ) &&
        ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLORARG1 ) == ( D3DTA_CURRENT | D3DTA_ALPHAREPLICATE ) ) &&
        ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLORARG2 ) == D3DTA_DIFFUSE ) &&
        ( ( ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) == D3DTOP_ADD ) &&
            ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&
            ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) ) ||
          ( ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) &&
            ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) ) ) )
    {
        // Colour channel is correct and can be squashed down to one stage.
        // Check that the alpha channel is OK.
        int bOK;
        if (( ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
              ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) ) ||
            ( ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2 ) &&
              ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) )
        {
            // Stage 0 is set to pass-through - set up texcomp0 as stage 1.
            // Colour channel will be overridden later.
            __TXT_TranslateToChipBlendMode(pContext, 
                                           &pContext->TextureStageState[pContext->iChipStage[1]], 
                                           pSoftP3RX, 
                                           pContext->iChipStage[1], 
                                           0);
            bOK = TRUE;
        }
        else if (( ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
                   ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) ) ||
                 ( ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2 ) &&
                   ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) )
        {
            // Stage 1 is set to pass-through - set up texcomp0 as stage 0.
            // Colour channel will be overridden later.
            __TXT_TranslateToChipBlendMode(pContext, 
                                           &pContext->TextureStageState[pContext->iChipStage[0]], 
                                           pSoftP3RX, 
                                           pContext->iChipStage[0], 
                                           0);
            bOK = TRUE;
        }
        else if (( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) &&
                 ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) &&
                 ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) &&
                 ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) &&
                 ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) &&
                 ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) )
        {
            // Set up to do ( 4 * cur.a - 1.5 ), or rather 4 * ( cur.a - 0.375 )
            dwTexComp0Tfactor = 0x60606060;     // All channels set to (0.375)
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_SUBTRACT_AB;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_FOUR;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg1 = P3RX_TEXCOMP_HEIGHTA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg2 = P3RX_TEXCOMP_FA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.I = P3RX_TEXCOMP_I_CA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG1;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.B = P3RX_TEXCOMP_ARG2;
            if ( pContext->bBumpmapInverted )
            {
                pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_ENABLE;
            }
            else
            {
                pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE;
            }
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg2 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertI = __PERMEDIA_DISABLE;
            bOK = TRUE;
        }
        else
        {
            bOK = FALSE;
        }

        if ( bOK )
        {
            // OK, the alpha channel is fine - set up the colour channel now.
            pSoftP3RX->P3RXTextureCompositeColorMode0.Enable = __PERMEDIA_ENABLE;
            if ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) == D3DTOP_ADD )
            {
                // Yes, this is the ((diff.c*cur.a)+tex.c) case.
                pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B;
                pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
                pSoftP3RX->P3RXTextureCompositeColorMode0.Arg1 = P3RX_TEXCOMP_CC;
                if ( pContext->iStageTex[pContext->iChipStage[1]] == 0 )
                {
                    pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_T0C;
                }
                else
                {
                    pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_T1C;
                }
                pSoftP3RX->P3RXTextureCompositeColorMode0.I = P3RX_TEXCOMP_I_HA;
                pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG1;
                pSoftP3RX->P3RXTextureCompositeColorMode0.B = P3RX_TEXCOMP_ARG2;
                pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg1 = __PERMEDIA_DISABLE;
                pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg2 = __PERMEDIA_DISABLE;
                if ( pContext->bBumpmapInverted )
                {
                    pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_ENABLE;
                }
                else
                {
                    pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_DISABLE;
                }
            }
            else
            {
                // Yes, this is just the (diff.c*cur.a) case.
                pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;
                pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
                pSoftP3RX->P3RXTextureCompositeColorMode0.Arg1 = P3RX_TEXCOMP_CC;
                pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_HEIGHTA;
                pSoftP3RX->P3RXTextureCompositeColorMode0.I = P3RX_TEXCOMP_I_OA;
                pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG1;
                pSoftP3RX->P3RXTextureCompositeColorMode0.B = P3RX_TEXCOMP_ARG2;
                pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg1 = __PERMEDIA_DISABLE;
                pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg2 = __PERMEDIA_DISABLE;
                if ( pContext->bBumpmapInverted )
                {
                    pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_ENABLE;
                }
                else
                {
                    pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_DISABLE;
                }
            }

            // Done chip stage 0, TSS stage 0 & 1. Move chip stage 1 on a notch.
            pContext->iChipStage[1]++;
            pContext->iChipStage[2]++;
            pContext->iChipStage[3]++;
            iLastChipStage = 1;
            bProcessChipStage0 = FALSE;
        }
    }


    if ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLOROP ) == D3DTOP_DISABLE )
    {
        // Nothing more to do.
        bProcessChipStage0 = FALSE;
        bProcessChipStage1 = FALSE;
        bProcessChipStage2 = FALSE;
    }

    if ( pContext->iStageTex[pContext->iChipStage[0]] == -1 )
    {
        // This stage has no texture - is anyone trying to use it?
        if (( TSSTATESELECT ( pContext->iChipStage[0], D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLOROP ) != D3DTOP_SELECTARG2 ) ||
            ( TSSTATESELECT ( pContext->iChipStage[0], D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLOROP ) != D3DTOP_SELECTARG1 ) ||
            ( TSSTATESELECT ( pContext->iChipStage[0], D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG2 ) ||
            ( TSSTATESELECT ( pContext->iChipStage[0], D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG1 ) )
        {
            // Panic! In future, we should feed white to the argument using the TFACTOR thing,
            // but for now just disable the rest of the pipeline.
            bProcessChipStage0 = FALSE;
            bProcessChipStage1 = FALSE;
            bProcessChipStage2 = FALSE;
        }
    }

    if ( bProcessChipStage0 )
    {
        // Set up stage 0
        DISPDBG((DBGLVL,"Texture Stage 0 is valid - setting it up"));
        __TXT_TranslateToChipBlendMode(pContext, 
                                       &pContext->TextureStageState[pContext->iChipStage[0]], 
                                       pSoftP3RX, 
                                       pContext->iChipStage[0], 
                                       0);
        iLastChipStage = 1;
        bProcessChipStage0 = FALSE;
    }


    // Handle chip stage 1.


    if ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) == D3DTOP_DISABLE )
    {
        // Nothing more to do.
        bProcessChipStage1 = FALSE;
        bProcessChipStage2 = FALSE;
    }

    if ( pContext->iStageTex[pContext->iChipStage[1]] == -1 )
    {
        // This stage has no texture - is anyone trying to use it?
        if (( TSSTATESELECT ( pContext->iChipStage[1], D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) != D3DTOP_SELECTARG2 ) ||
            ( TSSTATESELECT ( pContext->iChipStage[1], D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) != D3DTOP_SELECTARG1 ) ||
            ( TSSTATESELECT ( pContext->iChipStage[1], D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG2 ) ||
            ( TSSTATESELECT ( pContext->iChipStage[1], D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG1 ) )
        {
            // Panic! In future, we should feed white to the argument using the TFACTOR thing,
            // but for now just disable the rest of the pipeline.
            bProcessChipStage1 = FALSE;
            bProcessChipStage2 = FALSE;
        }
    }


    if ( bProcessChipStage1 )
    {
        // Set up stage 1
        DISPDBG((DBGLVL,"Texture Stage 1 is valid - setting it up"));
        __TXT_TranslateToChipBlendMode(pContext, 
                                       &pContext->TextureStageState[pContext->iChipStage[1]],
                                       pSoftP3RX, 
                                       pContext->iChipStage[1], 
                                       1);

        iLastChipStage = 2;
        bProcessChipStage1 = FALSE;
    }



    // Handle chip stage 2.


    if ( TSSTATE ( pContext->iChipStage[2], D3DTSS_COLOROP ) == D3DTOP_DISABLE )
    {
        // Nothing more to do.
        bProcessChipStage2 = FALSE;
    }

    if ( pContext->iStageTex[pContext->iChipStage[2]] == -1 )
    {
        // This stage has no texture - is anyone trying to use it?
        if (( TSSTATESELECT ( pContext->iChipStage[2], D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[2], D3DTSS_COLOROP ) != D3DTOP_SELECTARG2 ) ||
            ( TSSTATESELECT ( pContext->iChipStage[2], D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[2], D3DTSS_COLOROP ) != D3DTOP_SELECTARG1 ) ||
            ( TSSTATESELECT ( pContext->iChipStage[2], D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[2], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG2 ) ||
            ( TSSTATESELECT ( pContext->iChipStage[2], D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[2], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG1 ) )
        {
            // Panic! In future, we should feed white to the argument using the TFACTOR thing,
            // but for now just disable the rest of the pipeline.
            bProcessChipStage2 = FALSE;
        }
    }

    if ( bProcessChipStage2 )
    {
        // Set up chip stage 2 - texapp.
        DISPDBG((DBGLVL,"Texture Stage 2 is valid - setting it up"));
        DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: Cool - an app is using the "
                     "TexApp unit - tell someone!"));
        __TXT_TranslateToChipBlendMode(pContext, 
                                       &pContext->TextureStageState[pContext->iChipStage[2]],
                                       pSoftP3RX, 
                                       pContext->iChipStage[2], 
                                       2);
        iLastChipStage = 3;
        bProcessChipStage2 = FALSE;
    }

    // This must be last.
    if ( TSSTATE ( pContext->iChipStage[3], D3DTSS_COLOROP ) != D3DTOP_DISABLE )
    {
        // Oops - ran out of stages to set up.
        SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_BLEND_STAGES );
        iLastChipStage = 3;
    }

    switch ( iLastChipStage )
    {
        case 0:
            DISPDBG((DBGLVL,"Texture Composite 0 is disabled"));
            // This should have been caught ages ago.
            pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_CC;
            pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg2 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG2;
            pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
            pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
            pSoftP3RX->P3RXTextureCompositeColorMode0.Enable = __PERMEDIA_ENABLE;
            
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg2 = P3RX_TEXCOMP_CA;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG2;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
            // fall through
        case 1:
            DISPDBG((DBGLVL,"Texture Composite 1 is disabled"));
            // Make sure the second stage passes the texel that the first stage generated
            if ( pContext->bStage0DotProduct )
            {
                // First stage was a dot-product - do the summing (even in the alpha channel).
                pSoftP3RX->P3RXTextureCompositeColorMode1.Arg2 = P3RX_TEXCOMP_SUM;
                pSoftP3RX->P3RXTextureCompositeAlphaMode1.Arg2 = P3RX_TEXCOMP_SUM;
            }
            else
            {
                pSoftP3RX->P3RXTextureCompositeColorMode1.Arg2 = P3RX_TEXCOMP_OC;
                pSoftP3RX->P3RXTextureCompositeAlphaMode1.Arg2 = P3RX_TEXCOMP_OA;
            }
            pSoftP3RX->P3RXTextureCompositeColorMode1.InvertArg2 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeColorMode1.A = P3RX_TEXCOMP_ARG2;
            pSoftP3RX->P3RXTextureCompositeColorMode1.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
            pSoftP3RX->P3RXTextureCompositeColorMode1.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
            pSoftP3RX->P3RXTextureCompositeColorMode1.Enable = __PERMEDIA_ENABLE;
            
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.InvertArg2 = __PERMEDIA_DISABLE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.A = P3RX_TEXCOMP_ARG2;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
            pSoftP3RX->P3RXTextureCompositeAlphaMode1.Enable = __PERMEDIA_ENABLE;
            // fall through
        case 2:
            // Texapp to passthrough.
            DISPDBG((DBGLVL,"Texture Application is disabled"));
            pSoftP3RX->P3RXTextureApplicationMode.ColorB = P3RX_TEXAPP_B_TC;
            pSoftP3RX->P3RXTextureApplicationMode.ColorOperation = P3RX_TEXAPP_OPERATION_PASS_B;
            pSoftP3RX->P3RXTextureApplicationMode.ColorInvertI = __PERMEDIA_DISABLE;

            pSoftP3RX->P3RXTextureApplicationMode.AlphaB = P3RX_TEXAPP_B_TC;
            pSoftP3RX->P3RXTextureApplicationMode.AlphaOperation = P3RX_TEXAPP_OPERATION_PASS_B;
            pSoftP3RX->P3RXTextureApplicationMode.AlphaInvertI = __PERMEDIA_DISABLE;
            // fall through
        case 3:
            // Nothing else in the pipeline to disable.
            // fall through
            break;
        default:
            DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: iLastChipStage was > 3 - oops."));
            break;
    }


    // Set up the alpha-map filtering to reflect the single/multi/mip-mapped texturing status
    // All the other colour-key stuff has already been set up.
    if( pContext->bCanChromaKey )
    {
        ASSERTDD ( pTexture0 != NULL, "** _D3DChangeTextureP3RX: pTexture was NULL" );
        if( pTexture0->bMipMap )
        {
            pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit0 = 4;
            pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit1 = 4;
            if ( pContext->bTex0Valid )
            {
                // Filter mode is irrelevant - this just works!
                pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit01 = 7;
            }
            else
            {
                DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: Trying to mipmap without a valid texture."));
                pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit01 = 8;
            }
            ASSERTDD ( !pContext->bTex1Valid, "** _D3DChangeTextureP3RX: Trying to mipmap with too many textures." );
        }
        else
        {
            // No mipmapping.
            if ( pContext->bTex0Valid )
            {
                // Don't care about filter mode - this just works.
                pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit0 = 7;
            }
            else
            {
                pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit0 = 4;
            }
            if ( pContext->bTex1Valid )
            {
                // Don't care about filter mode - this just works.
                pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit1 = 7;
            }
            else
            {
                pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit1 = 4;
            }
        }
    }

    // Enable Texture Address calculation
    pSoftP3RX->P3RXTextureCoordMode.Enable = __PERMEDIA_ENABLE;

    // Enable filtering
    pSoftP3RX->P3RXTextureFilterMode.Enable = __PERMEDIA_ENABLE;

//  // Enable Texel color generation
//  pSoftP3RX->P3RXTextureApplicationMode.Enable = __PERMEDIA_ENABLE;

    // Do we need to share the texture coordinates ?
    if ( pContext->bTex0Valid && pContext->bTex1Valid &&
        ( TSSTATE ( pContext->iTexStage[0], D3DTSS_TEXCOORDINDEX ) ==
          TSSTATE ( pContext->iTexStage[1], D3DTSS_TEXCOORDINDEX ) ) )
    {
        pSoftP3RX->P3RX_P3DeltaControl.ShareS = __PERMEDIA_ENABLE;
        pSoftP3RX->P3RX_P3DeltaControl.ShareT = __PERMEDIA_ENABLE;
    }
    else
    {
        pSoftP3RX->P3RX_P3DeltaControl.ShareS = __PERMEDIA_DISABLE;
        pSoftP3RX->P3RX_P3DeltaControl.ShareT = __PERMEDIA_DISABLE;
    }

    P3_ENSURE_DX_SPACE((P3_LOD_LEVELS*2));
    WAIT_FIFO((P3_LOD_LEVELS*2));
    for (i = 0; i < P3_LOD_LEVELS; i++)
    {
        COPY_P3_DATA_OFFSET(TextureMapWidth0, pSoftP3RX->P3RXTextureMapWidth[i], i);
    }

    if ( ( GET_BLEND_ERROR(pContext) & BLEND_STATUS_FATAL_FLAG ) != 0 )
    {
        // Got a fatal blend error - signal it to the user.

        DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: invalid blend mode"));
        
        _D3DDisplayWholeTSSPipe ( pContext, WRNLVL );

        // And make sure this is re-evaluated next time we render,
        // so that this (probably very munged) invalid setup doesn't cripple
        // any subsequent valid renderstates.
        DIRTY_EVERYTHING(pContext);
    }


    P3_ENSURE_DX_SPACE(32);
    WAIT_FIFO(32);
    SEND_P3_DATA(TextureCompositeMode, __PERMEDIA_ENABLE);
    COPY_P3_DATA(TextureFilterMode, pSoftP3RX->P3RXTextureFilterMode);

    COPY_P3_DATA(TextureApplicationMode, pSoftP3RX->P3RXTextureApplicationMode);
    COPY_P3_DATA(TextureCoordMode, pSoftP3RX->P3RXTextureCoordMode);
    COPY_P3_DATA(DeltaControl, pSoftP3RX->P3RX_P3DeltaControl);

    // Copy the current TFACTOR values.
    SEND_P3_DATA ( TextureEnvColor, FORMAT_8888_32BIT_BGR(dwTexAppTfactor) );
    SEND_P3_DATA ( TextureCompositeFactor0, FORMAT_8888_32BIT_BGR(dwTexComp0Tfactor) );
    SEND_P3_DATA ( TextureCompositeFactor1, FORMAT_8888_32BIT_BGR(dwTexComp1Tfactor) );
    DISPDBG((DBGLVL,"Current TFACTOR values. %x %x %x",
                    dwTexAppTfactor,
                    dwTexComp0Tfactor,
                    dwTexComp1Tfactor));

    COPY_P3_DATA(DeltaMode, pSoftP3RX->P3RX_P3DeltaMode);

    P3_ENSURE_DX_SPACE(32);
    WAIT_FIFO(32);

    COPY_P3_DATA(TextureCompositeColorMode0, pSoftP3RX->P3RXTextureCompositeColorMode0);
    COPY_P3_DATA(TextureCompositeColorMode1, pSoftP3RX->P3RXTextureCompositeColorMode1);
    COPY_P3_DATA(TextureCompositeAlphaMode0, pSoftP3RX->P3RXTextureCompositeAlphaMode0);
    COPY_P3_DATA(TextureCompositeAlphaMode1, pSoftP3RX->P3RXTextureCompositeAlphaMode1);

    COPY_P3_DATA(TextureReadMode1, pSoftP3RX->P3RXTextureReadMode1);
    COPY_P3_DATA(TextureIndexMode1, pSoftP3RX->P3RXTextureIndexMode1);

    COPY_P3_DATA(TextureReadMode0, pSoftP3RX->P3RXTextureReadMode0);
    COPY_P3_DATA(TextureIndexMode0, pSoftP3RX->P3RXTextureIndexMode0);

    // Make sure the texture cache is invalidated
    P3RX_INVALIDATECACHE(__PERMEDIA_ENABLE, __PERMEDIA_DISABLE);
    
    SEND_P3_DATA(LOD, 0);
    SEND_P3_DATA(LOD1, 0);

    {
        struct LodRange range;

        // Clear down whole register

        *(DWORD *)&range = 0;

        // Each of the Min and Max LODs are in 4.8 format. We only deal 
        // with integer LODs in the range (0, N) so we just compute the 
        // upper value N and shift it up 8 bits.

        range.Min = 0;
        range.Max = ( mipBases.dwTex0MipMax - mipBases.dwTex0MipBase ) << 8;
        COPY_P3_DATA( LodRange0, range );

        range.Min = 0;
        range.Max = ( mipBases.dwTex1MipMax - mipBases.dwTex1MipBase ) << 8;
        COPY_P3_DATA( LodRange1, range );
    }

    *pFlags |= SURFACE_TEXTURING;

    // Turn texturing on in the render command
    RENDER_TEXTURE_ENABLE(pContext->RenderCommand);
 
    P3_DMA_COMMIT_BUFFER();

    // See if the alpha-blend unit needs to be updated.
    if ( bAlphaBlendDouble != pContext->bAlphaBlendMustDoubleSourceColour )
    {
        pContext->bAlphaBlendMustDoubleSourceColour = bAlphaBlendDouble;
        DIRTY_ALPHABLEND(pContext);
    }

    DBG_EXIT(_D3DChangeTextureP3RX,0);  
    
} // _D3DChangeTextureP3RX