//-----------------------------------------------------------------------------
//
// This file contains the span state validation function.
//
// Copyright (C) Microsoft Corporation, 1997.
//
//-----------------------------------------------------------------------------

#include "pch.cpp"
#pragma hdrstop

include(`bead.mh')
include(`rampbead.mh')
#include "BdStr_mh.h"
#include "RastCap.h"
#include "RastColl.h"
#include "RampOld.h"

static CRastCollection g_RastCollection;

UINT16 g_uDitherTable[16] =
{
    0x00,   0x80,   0x20,   0xa0,
    0xc0,   0x40,   0xe0,   0x60,
    0x30,   0xb0,   0x10,   0x90,
    0xf0,   0x70,   0xd0,   0x50
};

//-----------------------------------------------------------------------------
//
// DoZCompareSetup
//
// Initializes arithmetic Z compare state based on the Z test function.
//
//-----------------------------------------------------------------------------
void DoZCompareSetup(PD3DI_RASTCTX pCtx, BOOL bIsMMX)
{
    INT32 iXorMask;
    if (bIsMMX)
    {
        iXorMask = -1;
    }
    else
    {
        iXorMask = 1;
    }
    D3DCMPFUNC ZFunc = (D3DCMPFUNC)pCtx->pdwRenderState[D3DRENDERSTATE_ZFUNC];
    if (!pCtx->pdwRenderState[D3DRENDERSTATE_ZENABLE])
    {
        // setup ALWAYS if Z is not enabled, since we may be going into
        // the test beads for other reasons (like stencil)
        ZFunc = D3DCMP_ALWAYS;
    }
    switch (ZFunc)
    {
    case D3DCMP_NEVER:
        pCtx->iZAndMask = 0; pCtx->iZNeg = 0; pCtx->iZXorMask = iXorMask;
        break;
    case D3DCMP_ALWAYS:
        pCtx->iZAndMask = 0; pCtx->iZNeg = 0; pCtx->iZXorMask = 0;
        break;

    case D3DCMP_LESS:
        pCtx->iZAndMask = ~0; pCtx->iZNeg = 0; pCtx->iZXorMask = iXorMask;
        break;
    case D3DCMP_GREATEREQUAL:
        pCtx->iZAndMask = ~0; pCtx->iZNeg = 0; pCtx->iZXorMask = 0;
        break;

    case D3DCMP_EQUAL:
        pCtx->iZAndMask = 0x7fffffff; pCtx->iZNeg = 1; pCtx->iZXorMask =
            iXorMask;
        break;
    case D3DCMP_NOTEQUAL:
        pCtx->iZAndMask = 0x7fffffff; pCtx->iZNeg = 1; pCtx->iZXorMask = 0;
        break;

    default:
    case D3DCMP_LESSEQUAL:
        pCtx->iZAndMask = ~0; pCtx->iZNeg = 1; pCtx->iZXorMask = iXorMask;
        break;
    case D3DCMP_GREATER:
        pCtx->iZAndMask = ~0; pCtx->iZNeg = 1; pCtx->iZXorMask = 0;
        break;
    }

    if (bIsMMX)
    {
        pCtx->iZXorMask = ~pCtx->iZXorMask;
    }

}

//-----------------------------------------------------------------------------
//
// DoACompareSetup
//
// Initializes arithmetic alpha compare state based on the alpha test function.
//
//-----------------------------------------------------------------------------
void DoACompareSetup(PD3DI_RASTCTX pCtx, BOOL bIsMMX)
{
    INT32 iXorMask;
    if (bIsMMX)
    {
        iXorMask = -1;
    }
    else
    {
        iXorMask = 1;
    }
    switch ((D3DCMPFUNC)pCtx->pdwRenderState[D3DRENDERSTATE_ALPHAFUNC])
    {
    case D3DCMP_NEVER:
        pCtx->iAAndMask = 0; pCtx->iANeg = 0; pCtx->iAXorMask = iXorMask;
        break;
    default:
    case D3DCMP_ALWAYS:
        pCtx->iAAndMask = 0; pCtx->iANeg = 0; pCtx->iAXorMask = 0;
        break;

    case D3DCMP_LESS:
        pCtx->iAAndMask = ~0; pCtx->iANeg = 0; pCtx->iAXorMask = iXorMask;
        break;
    case D3DCMP_GREATEREQUAL:
        pCtx->iAAndMask = ~0; pCtx->iANeg = 0; pCtx->iAXorMask = 0;
        break;

    case D3DCMP_EQUAL:
        pCtx->iAAndMask = 0x7fffffff; pCtx->iANeg = 1; pCtx->iAXorMask =
            iXorMask;
        break;
    case D3DCMP_NOTEQUAL:
        pCtx->iAAndMask = 0x7fffffff; pCtx->iANeg = 1; pCtx->iAXorMask = 0;
        break;

    case D3DCMP_LESSEQUAL:
        pCtx->iAAndMask = ~0; pCtx->iANeg = 1; pCtx->iAXorMask = iXorMask;
        break;
    case D3DCMP_GREATER:
        pCtx->iAAndMask = ~0; pCtx->iANeg = 1; pCtx->iAXorMask = 0;
        break;
    }

    if (bIsMMX)
    {
        pCtx->iAXorMask = ~pCtx->iAXorMask;
    }

    // iARef is an 8.8 ALPHAREF value for use by RGB and MMX.
    // D3DRENDERSTATE_ALPHAREF is now an 8 bit (0xff max) value.
    pCtx->iARef = pCtx->pdwRenderState[D3DRENDERSTATE_ALPHAREF] << 8;
}

//-----------------------------------------------------------------------------
//
// DoSCompareSetup
//
// Initializes arithmetic stencil compare state based on the stencil function.
//
//-----------------------------------------------------------------------------
void DoSCompareSetup(PD3DI_RASTCTX pCtx, BOOL bIsMMX)
{
    INT32 iXorMask;
    if (bIsMMX)
    {
        iXorMask = -1;
    }
    else
    {
        iXorMask = 1;
    }
    switch ((D3DCMPFUNC)pCtx->pdwRenderState[D3DRENDERSTATE_STENCILFUNC])
    {
    case D3DCMP_NEVER:
        pCtx->iSAndMask = 0; pCtx->iSNeg = 0; pCtx->iSXorMask = iXorMask;
        break;
    default:
    case D3DCMP_ALWAYS:
        pCtx->iSAndMask = 0; pCtx->iSNeg = 0; pCtx->iSXorMask = 0;
        break;

    case D3DCMP_LESS:
        pCtx->iSAndMask = ~0; pCtx->iSNeg = 0; pCtx->iSXorMask = iXorMask;
        break;
    case D3DCMP_GREATEREQUAL:
        pCtx->iSAndMask = ~0; pCtx->iSNeg = 0; pCtx->iSXorMask = 0;
        break;

    case D3DCMP_EQUAL:
        pCtx->iSAndMask = 0x7fffffff; pCtx->iSNeg = 1; pCtx->iSXorMask =
            iXorMask;
        break;
    case D3DCMP_NOTEQUAL:
        pCtx->iSAndMask = 0x7fffffff; pCtx->iSNeg = 1; pCtx->iSXorMask = 0;
        break;

    case D3DCMP_LESSEQUAL:
        pCtx->iSAndMask = ~0; pCtx->iSNeg = 1; pCtx->iSXorMask = iXorMask;
        break;
    case D3DCMP_GREATER:
        pCtx->iSAndMask = ~0; pCtx->iSNeg = 1; pCtx->iSXorMask = 0;
        break;
    }
    if (bIsMMX)
    {
        pCtx->iSXorMask = ~pCtx->iSXorMask;
    }

}

//-----------------------------------------------------------------------------
//
// DoTexAddrSetup
//
// Initializes arithmetic texture address state state based on the texture
// address function.  iTex is the texure to setup.
//
//-----------------------------------------------------------------------------
void DoTexAddrSetup(PD3DI_RASTCTX pCtx, INT32 iTex)
{
    PD3DI_SPANTEX pTex = pCtx->pTexture[iTex];

    // Note that it is essential to turn on mirroring when in CLAMP/BORDER mode
    // to avoid filtering artifacts at the edge of the texture
    switch (pTex->TexAddrU)
    {
        default:
        case D3DTADDRESS_WRAP:
            pTex->iFlipMaskU = 0; pTex->iClampMinU = 0;
            pTex->iClampMaxU = 0; pTex->iClampEnU = 0; break;
        case D3DTADDRESS_MIRROR:
            pTex->iFlipMaskU = pTex->uMaskU+1; pTex->iClampMinU = 0;
            pTex->iClampMaxU = 0; pTex->iClampEnU = 0; break;
        case D3DTADDRESS_CLAMP:
            pTex->iFlipMaskU = pTex->uMaskU+1; pTex->iClampMinU = 0;
            pTex->iClampMaxU = pTex->uMaskU; pTex->iClampEnU = -1; break;
        case D3DTADDRESS_BORDER:
            pTex->iFlipMaskU = pTex->uMaskU+1; pTex->iClampMinU = -1;
            pTex->iClampMaxU = -1; pTex->iClampEnU = -1; break;
    }
    switch (pTex->TexAddrV)
    {
        default:
        case D3DTADDRESS_WRAP:
            pTex->iFlipMaskV = 0; pTex->iClampMinV = 0;
            pTex->iClampMaxV = 0; pTex->iClampEnV = 0; break;
        case D3DTADDRESS_MIRROR:
            pTex->iFlipMaskV = pTex->uMaskV+1; pTex->iClampMinV = 0;
            pTex->iClampMaxV = 0; pTex->iClampEnV = 0; break;
        case D3DTADDRESS_CLAMP:
            pTex->iFlipMaskV = pTex->uMaskV+1; pTex->iClampMinV = 0;
            pTex->iClampMaxV = pTex->uMaskV; pTex->iClampEnV = -1; break;
        case D3DTADDRESS_BORDER:
            pTex->iFlipMaskV = pTex->uMaskV+1; pTex->iClampMinV = -1;
            pTex->iClampMaxV = -1; pTex->iClampEnV = -1; break;
    }
}

// CMMX is no longer linked in
// extern BEADTABLE g_CMMX_BeadTbl;

extern BEADTABLE g_C_BeadTbl;

#ifdef _X86_
extern BEADTABLE g_MMX_BeadTbl;
#else
BEADTABLE g_MMX_BeadTbl = g_C_BeadTbl;
#endif

extern RAMPBEADTABLE g_Ramp_BeadTbl;
HRESULT RampSpanInit(PD3DI_RASTCTX pCtx, PRAMPBEADTABLE pRBT);
extern void Ramp_InitBeadTbl(void);

#if DBG
#define RANGE_CHECK(val,max)    DDASSERT(((val) >= 0) && ((val) < (max)))
#else
#define RANGE_CHECK(val,max)
#endif

#undef DPF_MODNAME
#define DPF_MODNAME "SpanInit"

//-----------------------------------------------------------------------------
//
// SpanInit
//
// Initializes derived state (function pointers and arithmetic compare values)
// based on the render state, surface type, etc.
//
//-----------------------------------------------------------------------------
HRESULT SpanInit(PD3DI_RASTCTX pCtx)
{
    int iZTest,
        iZFormat,
        iZWrite,
        iZFunc,
        iZDeferred,
        iStencil,
        iAlphaTest,
        iShadeMode,
        iSpecular,
        iVertexFog,
        iTexture,
        iTextureAddr[2],
        iTextureBorder[2],
        iTexturePerspective,
        iTextureFilter[2],
        iTextureMip[2],
        iTextureLOD[2],
        iTextureFormat[2],
        iTextureColorKey[2],
        iTextureBlend,
        iTextureAlphaOverride[2],
        iMono,
        iAlphaBlend,
        iAlphaDither[2],
        iBlend,
        iROP,
        iSrcBlend,
        iDestBlend,
        iTargetPixelFormat,
        iDither;
    PFNRENDERSPANS  pfnRenderSpans;
    PFNSPANLAYER    pfnTex1Addr,
                    pfnTex2Addr,
                    pfnColorBlend,
                    pfnColorGen;
    PBEADTABLE      pBeadTable;
    CRastCapRecord  RastCapRec;

#if DBG
    if (SPIGETFLAGS(DBG_USER_FLAGS) & SPIU_BREAK_ON_SPANINIT)
    {
        DebugBreak();
    }
#endif
    // make sure this is always NULL unless it is valid
    pCtx->pfnRampOld = NULL;

    switch (pCtx->BeadSet)
    {
#ifdef _X86_
//    Uncomment these (and comment out cases below) to test MMX on legacy code
//    case D3DIBS_CMMX:
//    case D3DIBS_C:
    case D3DIBS_MMXASRGB:   // this is only different in pCtx->BeadSet for identification
    case D3DIBS_MMX:
        pBeadTable = &g_MMX_BeadTbl;
        break;
#endif
    default:
        SPIDPFM((SPIM_INVALID,"Invalid BeadSet %d\n",
            pCtx->BeadSet));
        // fall through
    case D3DIBS_CMMX:       // CMMX code is no longer used, make this behave the same as C
    case D3DIBS_C:
        pBeadTable = &g_C_BeadTbl;
        break;
    case D3DIBS_RAMP:
        return RampSpanInit(pCtx, &g_Ramp_BeadTbl);
        break;
    }

    DoZCompareSetup(pCtx, (pBeadTable == &g_MMX_BeadTbl));
    DoACompareSetup(pCtx, (pBeadTable == &g_MMX_BeadTbl));
    DoSCompareSetup(pCtx, (pBeadTable == &g_MMX_BeadTbl));

    // The function pointers that point to various beads are stored in
    // multi-dimensional arrays.  In order to access the correct bead, we
    // use numerical indices into these arrays.  The indices are either
    // the exact value of the renderstate used to choose the bead, or are
    // values chosen by looking at the value of the renderstate.

    // values needed to choose Test and AlphaTest beads
    iZTest = (pCtx->pdwRenderState[D3DRENDERSTATE_ZENABLE] ||
              pCtx->pdwRenderState[D3DRENDERSTATE_STENCILENABLE]) ? 1 : 0;
    RastCapRec.Set_ZTest(iZTest);
    switch (pCtx->iZBitCount)
    {
    default :
        SPIDPFM((SPIM_INVALID,"Invalid Z surface bit count, iZBitCount == 0x%x\n",
            pCtx->iZBitCount));
    case 0 :
    case 16 :
        iZFormat = 0;
        break;
    case 32 :
        iZFormat = 1;
        break;
    }
    RastCapRec.Set_ZFormat(iZFormat);
    iZWrite = pCtx->pdwRenderState[D3DRENDERSTATE_ZWRITEENABLE] ? 1 : 0;
    RastCapRec.Set_ZWrite(iZWrite);
    switch (pCtx->pdwRenderState[D3DRENDERSTATE_ZFUNC])
    {
    case D3DCMP_NEVER :
    case D3DCMP_ALWAYS :
        iZFunc = 0;
        break;
    case D3DCMP_LESS :
    case D3DCMP_GREATEREQUAL :
        iZFunc = 1;
        break;
    case D3DCMP_EQUAL :
    case D3DCMP_NOTEQUAL :
        iZFunc = 2;
        break;
    default :
        SPIDPFM((SPIM_INVALID,"Invalid renderstate value, D3DRENDERSTATE_ZFUNC == 0x%x\n",
            pCtx->pdwRenderState[D3DRENDERSTATE_ZFUNC]));
    case D3DCMP_LESSEQUAL :
    case D3DCMP_GREATER :
        iZFunc = 3;
        break;
    }
    if (!pCtx->pdwRenderState[D3DRENDERSTATE_ZENABLE])
    {
        // setup ALWAYS if Z is not enabled, since we may be going into
        // the test beads for other reasons (like stencil)
        iZFunc = 0;
        iZWrite = 0;    // do not write iterated Z, but stencil will be written if needed
    }
    RastCapRec.Set_ZFunc(iZFunc);
    // Ignore stipple (D3DRENDERSTATE_STIPPLEENABLE), since no software rasterizers do it.
    RastCapRec.Set_Stipple(0);
    iAlphaTest = pCtx->pdwRenderState[D3DRENDERSTATE_ALPHATESTENABLE] ? 1 : 0;
    RastCapRec.Set_AlphaTest(iAlphaTest);
    iStencil = pCtx->pdwRenderState[D3DRENDERSTATE_STENCILENABLE] ? 1 : 0;
    RastCapRec.Set_Stencil(iStencil);

    // values needed for TestFail, TexAddr1 and ColorGen beads
    switch (pCtx->pdwRenderState[D3DRENDERSTATE_SHADEMODE])
    {
    case D3DSHADE_FLAT :
        iShadeMode = 0;
        break;
    default :
        SPIDPFM((SPIM_INVALID,"Invalid renderstate value, D3DRENDERSTATE_SHADEMODE == 0x%x\n",
            pCtx->pdwRenderState[D3DRENDERSTATE_SHADEMODE]));
    case D3DSHADE_GOURAUD :
    case D3DSHADE_PHONG :
        iShadeMode = 1;
        break;
    }
    RastCapRec.Set_ShadeMode(iShadeMode);
    iSpecular = pCtx->pdwRenderState[D3DRENDERSTATE_SPECULARENABLE] ? 1 : 0;
    RastCapRec.Set_Specular(iSpecular);
    iVertexFog = pCtx->pdwRenderState[D3DRENDERSTATE_FOGENABLE] ? 1 : 0;
    RastCapRec.Set_VertexFog(iVertexFog);

    // are we texturing at all?
    if (pCtx->cActTex == 0) {

        iTexture = 0;
        RastCapRec.Set_Texture(iTexture);
        iTextureBlend = 0;
        RastCapRec.Set_TextureBlend(iTextureBlend);
        iTexturePerspective = 0;
        RastCapRec.Set_TexturePersp(iTexturePerspective);
        iTextureAlphaOverride[0] = 0;
        RastCapRec.Set_TextureAlphaOverride(0, iTextureAlphaOverride[0]);
        iTextureColorKey[0] = 0;
        RastCapRec.Set_TextureColorKey(0, iTextureColorKey[0]);

        if (pCtx->uDevVer >= 1)
        {
            // DX6, DX5 required legacy behavior
            iAlphaDither[0] = pCtx->pdwRenderState[D3DRENDERSTATE_COLORKEYENABLE] ? 1 : 0;
        }
        else
        {
            // DX3, required legacy behavior
            iAlphaDither[0] =
                (pCtx->pdwRenderState[D3DRENDERSTATE_ALPHABLENDENABLE] ? 1 : 0) ||
                (pCtx->pdwRenderState[D3DRENDERSTATE_COLORKEYENABLE] ? 1 : 0);
        }
    } else {
        iTexturePerspective =
            pCtx->pdwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ? 1 : 0;
        RastCapRec.Set_TexturePersp(iTexturePerspective);

        if (iTexturePerspective) {
            iTexture = 2;
        } else {
            iTexture = 1;
        }

        // If Multi-texture then iTexture must be incremented by two.  This only effects fail cases.
        if (!(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLOROP)] == D3DTOP_DISABLE))
        {
            iTexture += 2;
        }
        RastCapRec.Set_Texture(iTexture);

        for (INT32 i = 0; i < (INT32)pCtx->cActTex; i++)
        {
            DoTexAddrSetup(pCtx, i);

            // values needed to choose TexAddr1 bead, TexRead routine
            // easiest to deal with border separately
            if ((pCtx->pTexture[i]->TexAddrU == D3DTADDRESS_BORDER) ||
                (pCtx->pTexture[i]->TexAddrV == D3DTADDRESS_BORDER)) {
                iTextureBorder[i] = 1;
            } else {
                iTextureBorder[i] = 0;
            }
            RastCapRec.Set_TextureBorder(i, iTextureBorder[i]);
            switch (pCtx->pTexture[i]->TexAddrU)
            {
            default :
                SPIDPFM((SPIM_INVALID,"Invalid texture address mode, TexAddrU == 0x%x\n",
                    pCtx->pTexture[i]->TexAddrU));
            case D3DTADDRESS_WRAP :
                if ((pCtx->pTexture[i]->TexAddrV == D3DTADDRESS_WRAP) ||
                    (pCtx->pTexture[i]->TexAddrV == D3DTADDRESS_MIRROR)) {
                    iTextureAddr[i] = 0;
                } else {
                    iTextureAddr[i] = 1;
                }
                break;
            case D3DTADDRESS_MIRROR :
                if ((pCtx->pTexture[i]->TexAddrV == D3DTADDRESS_WRAP) ||
                    (pCtx->pTexture[i]->TexAddrV == D3DTADDRESS_MIRROR)) {
                    iTextureAddr[i] = 0;
                } else {
                    iTextureAddr[i] = 1;
                }
                break;
            case D3DTADDRESS_CLAMP :
                iTextureAddr[i] = 1;
                break;
            case D3DTADDRESS_BORDER :
                iTextureAddr[i] = 1;
                break;
            }
            RastCapRec.Set_TextureAddr(i, iTextureAddr[i]);

            if (i >= 1)
            {
                // no LOD info for second texture, just use uMagFilter
                switch (pCtx->pTexture[i]->uMagFilter)
                {
                default :
                    SPIDPFM((SPIM_INVALID,"Invalid texture filter mode, uMagFilter == 0x%x\n",
                        pCtx->pTexture[i]->uMagFilter));
                case D3DTFG_POINT :
                    iTextureFilter[i] = 0;
                    break;
                case D3DTFG_LINEAR :
                case D3DTFG_FLATCUBIC:
                case D3DTFG_GAUSSIANCUBIC:
                case D3DTFG_ANISOTROPIC:
                    iTextureFilter[i] = 1;
                    break;
                }
                iTextureMip[i] = 0;
                iTextureLOD[i] = 0;
            }
            else
            {
                switch (pCtx->pTexture[i]->uMagFilter)
                {
                default :
                    SPIDPFM((SPIM_INVALID,"Invalid texture filter mode, uMagFilter == 0x%x\n",
                        pCtx->pTexture[i]->uMagFilter));
                case D3DTFG_POINT :
                    if (pCtx->pTexture[i]->uMinFilter == D3DTFN_POINT)
                    {
                        iTextureFilter[i] = 0;
                    }
                    else
                    {
                        iTextureFilter[i] = 2;
                    }
                    break;
                case D3DTFG_LINEAR :
                case D3DTFG_FLATCUBIC:
                case D3DTFG_GAUSSIANCUBIC:
                case D3DTFG_ANISOTROPIC:
                    if (pCtx->pTexture[i]->uMinFilter == D3DTFN_LINEAR)
                    {
                        iTextureFilter[i] = 1;
                    }
                    else
                    {
                        iTextureFilter[i] = 2;
                    }
                    break;
                }
                iTextureLOD[i] = 1;
                if ( (i == 0) && (pCtx->pTexture[i]->uMipFilter == D3DTFP_LINEAR) )
                {
                    iTextureMip[i] = 1;
                }
                else
                {
                    iTextureMip[i] = 0;
                    // only set LOD to 0 if not mip filtering AND it is NOT the maybe
                    // bilinear filter case
                    if ( (pCtx->pTexture[i]->uMipFilter == D3DTFP_NONE) && (iTextureFilter[i] != 2) )
                    {
                        iTextureLOD[i] = 0;
                    }

                }
            }
            RastCapRec.Set_TextureFilter(i, iTextureFilter[i]);
            RastCapRec.Set_TextureMip(i, iTextureMip[i]);
            RastCapRec.Set_TextureLOD(i, iTextureLOD[i]);

            // set iTextureAlphaOverride if texture doesn't have an alpha and should use
            // iterated alpha
            iTextureAlphaOverride[i] = 0;
            RastCapRec.Set_TextureAlphaOverride(i, iTextureAlphaOverride[i]);

            // values needed to chose TexRead routine
            switch(pCtx->pTexture[i]->Format)
            {
            default :
                SPIDPFM((SPIM_INVALID,"Invalid texture surface bit count, "
                    "Format == 0x%x\n",(DWORD) pCtx->pTexture[i]->Format));
                return DDERR_INVALIDPARAMS;
            case D3DI_SPTFMT_B8G8R8 :
                iTextureFormat[i] = 0;
                iTextureAlphaOverride[i] = 1;
                break;
            case D3DI_SPTFMT_B8G8R8A8 :
                iTextureFormat[i] = 1;
                break;
            case D3DI_SPTFMT_B8G8R8X8 :
                // still necessary to select texture read that sets alpha to 0xff
                // for alpha dither
                iTextureFormat[i] = 0;
                iTextureAlphaOverride[i] = 1;
                break;
            case D3DI_SPTFMT_B5G6R5 :
                iTextureFormat[i] = 2;
                iTextureAlphaOverride[i] = 1;
                break;
            case D3DI_SPTFMT_B5G5R5 :
                iTextureFormat[i] = 3;
                iTextureAlphaOverride[i] = 1;
                break;
            case D3DI_SPTFMT_PALETTE4 :
                if (pCtx->pTexture[i]->pPalette == NULL)
                {
                    D3D_ERR("(Rast) NULL palette in paletted texture");
                    return DDERR_INVALIDPARAMS;
                }
                if (pCtx->pTexture[i]->uFlags & D3DI_SPANTEX_ALPHAPALETTE)
                {
                    iTextureFormat[i] = 11;
                }
                else
                {
                    iTextureFormat[i] = 4;
                    iTextureAlphaOverride[i] = 1;
                }
                break;
            case D3DI_SPTFMT_PALETTE8 :
                if (pCtx->pTexture[i]->pPalette == NULL)
                {
                    D3D_ERR("(Rast) NULL palette in paletted texture");
                    return DDERR_INVALIDPARAMS;
                }
                if (pCtx->pTexture[i]->uFlags & D3DI_SPANTEX_ALPHAPALETTE)
                {
                    iTextureFormat[i] = 12;
                }
                else
                {
                    iTextureFormat[i] = 5;
                    iTextureAlphaOverride[i] = 1;
                }
                break;
            case D3DI_SPTFMT_B5G5R5A1 :
                iTextureFormat[i] = 6;
                break;
            case D3DI_SPTFMT_B4G4R4 :
                iTextureFormat[i] = 7;
                iTextureAlphaOverride[i] = 1;
                break;
            case D3DI_SPTFMT_B4G4R4A4 :
                iTextureFormat[i] = 8;
                break;
            case D3DI_SPTFMT_L8 :
                iTextureFormat[i] = 9;
                iTextureAlphaOverride[i] = 1;
                break;
            case D3DI_SPTFMT_L8A8 :
                iTextureFormat[i] = 10;
                break;
            }
            RastCapRec.Set_TextureFormat(i, iTextureFormat[i]);

            if (pCtx->uDevVer >= 1)
            {
                // DX6, DX5 required legacy behavior
                iTextureColorKey[i] =
                    pCtx->pdwRenderState[D3DRENDERSTATE_COLORKEYENABLE] ?
                    ((pCtx->pTexture[i]->uFlags & D3DI_SPANTEX_HAS_TRANSPARENT) != 0) : 0;
                // DX5 required.  Only alpha dither only when color keying
                iAlphaDither[i] = iTextureColorKey[i];
            }
            else
            {
                // Pre-DX5 Enable ColorKey for COLORKEY OR BLENDENABLE
                iTextureColorKey[i] = (pCtx->pdwRenderState[D3DRENDERSTATE_COLORKEYENABLE] ||
                                       pCtx->pdwRenderState[D3DRENDERSTATE_ALPHABLENDENABLE]) ?
                    ((pCtx->pTexture[i]->uFlags & D3DI_SPANTEX_HAS_TRANSPARENT) != 0) : 0;
                // Alpha dither if Color Key OR Alpha Blend
                iAlphaDither[i] = iTextureColorKey[i] ||
                    (pCtx->pdwRenderState[D3DRENDERSTATE_ALPHABLENDENABLE] != 0);
            }
            RastCapRec.Set_TextureColorKey(i, iTextureColorKey[i]);
        }

        // choose TexBlend bead
        iTextureBlend = -1;
        if ( (pCtx->uDevVer <= 1) && (pCtx->uFlags & RASTCTXFLAGS_APPHACK_MSGOLF)
            && (D3DTBLEND_DECAL == pCtx->pdwRenderState[D3DRENDERSTATE_TEXTUREMAPBLEND]) )
        {
            // MSGolf98 App Hack - use modulate for decal, but also make sure that alpha
            // gets set to 0 on 555 rendertargets so colorkey works
            iTextureBlend = 2;
        }
        else if ((pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLOROP)] == D3DTOP_DISABLE) &&
            (pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)] == pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG1)]) &&
            (pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG2)] == pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG2)]))
        {
            // might be legacy texture blend mode
            if ((pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP)] == D3DTOP_SELECTARG1) &&
                (pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP)] == D3DTOP_SELECTARG1) &&
                (pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)] == D3DTA_TEXTURE))
            {
                // copy, decal, decalmask
                iTextureBlend = 1;
            }
            if ((pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP)] == D3DTOP_MODULATE) &&
                (pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP)] == D3DTOP_SELECTARG1) &&
                (pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)] == D3DTA_TEXTURE) &&
                (pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG2)] == D3DTA_DIFFUSE))
            {
                // modulate, modulatemask
                if (iTextureAlphaOverride[0])
                {
                    iTextureBlend = 3;
                }
                else
                {
                    iTextureBlend = 2;
                }
            }
        }

        if (iTextureBlend == -1)
        {
            INT iCOp = 0;
            INT iCArg1 = 0;
            INT iCArg2 = 0;
            INT iAOp = 0;
            INT iAArg1 = 0;
            INT iAArg2 = 0;

            switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP)])
            {
            default:
                SPIDPFM((SPIM_INVALID,"Invalid COLOROP0[0] == 0x%x\n",
                        (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLOROP)]));
            case D3DTOP_DISABLE:                iCOp = 0;  break;
            case D3DTOP_SELECTARG1:             iCOp = 1;  break;
            case D3DTOP_SELECTARG2:             iCOp = 2;  break;
            case D3DTOP_MODULATE:               iCOp = 3;  break;
            case D3DTOP_MODULATE2X:             iCOp = 4;  break;
            case D3DTOP_MODULATE4X:             iCOp = 5;  break;
            case D3DTOP_ADD:                    iCOp = 6;  break;
            case D3DTOP_ADDSIGNED:              iCOp = 7;  break;
            case D3DTOP_BLENDDIFFUSEALPHA:      iCOp = 8;  break;
            case D3DTOP_BLENDTEXTUREALPHA:      iCOp = 9;  break;
            case D3DTOP_BLENDFACTORALPHA:       iCOp = 10; break;
            case D3DTOP_BLENDTEXTUREALPHAPM:    iCOp = 11; break;
            }

            switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)])
            {
            default:
                SPIDPFM((SPIM_INVALID,"Invalid COLORARG1[0] == 0x%x\n",
                        (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG1)]));
            case (D3DTA_TEXTURE):
                iCArg1 = 0; break;
            case (D3DTA_TEXTURE|D3DTA_COMPLEMENT):
                iCArg1 = 1; break;
            case (D3DTA_ALPHAREPLICATE|D3DTA_TEXTURE):
                iCArg1 = 2; break;
            case (D3DTA_ALPHAREPLICATE|D3DTA_TEXTURE|D3DTA_COMPLEMENT):
                iCArg1 = 3; break;
            }

            switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG2)])
            {
            default:
                SPIDPFM((SPIM_INVALID,"Invalid COLORARG2[0] == 0x%x\n",
                        (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_COLORARG2)]));
            case (D3DTA_DIFFUSE):
                iCArg2 = 0; break;
            case (D3DTA_CURRENT):
                iCArg2 = 1; break;
            case (D3DTA_TFACTOR):
                iCArg2 = 2; break;
            case (D3DTA_COMPLEMENT|D3DTA_DIFFUSE):
                iCArg2 = 3; break;
            case (D3DTA_COMPLEMENT|D3DTA_CURRENT):
                iCArg2 = 4; break;
            case (D3DTA_COMPLEMENT|D3DTA_TFACTOR):
                iCArg2 = 5; break;
            case (D3DTA_ALPHAREPLICATE|D3DTA_DIFFUSE):
                iCArg2 = 6; break;
            case (D3DTA_ALPHAREPLICATE|D3DTA_CURRENT):
                iCArg2 = 7; break;
            case (D3DTA_ALPHAREPLICATE|D3DTA_TFACTOR):
                iCArg2 = 8; break;
            case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_DIFFUSE):
                iCArg2 = 9; break;
            case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_CURRENT):
                iCArg2 = 10; break;
            case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_TFACTOR):
                iCArg2 = 11; break;
            }

            switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP)])
            {
            default:
                SPIDPFM((SPIM_INVALID,"Invalid ALPHAOP[0] == 0x%x\n",
                        (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAOP)]));
            case D3DTOP_DISABLE:                iAOp = 0;  break;
            case D3DTOP_SELECTARG1:             iAOp = 1;  break;
            case D3DTOP_SELECTARG2:             iAOp = 2;  break;
            case D3DTOP_MODULATE:               iAOp = 3;  break;
            case D3DTOP_MODULATE2X:             iAOp = 4;  break;
            case D3DTOP_MODULATE4X:             iAOp = 5;  break;
            case D3DTOP_ADD:                    iAOp = 6;  break;
            case D3DTOP_ADDSIGNED:              iAOp = 7;  break;
            case D3DTOP_BLENDDIFFUSEALPHA:      iAOp = 8;  break;
            case D3DTOP_BLENDTEXTUREALPHA:      iAOp = 9;  break;
            case D3DTOP_BLENDFACTORALPHA:       iAOp = 10; break;
            case D3DTOP_BLENDTEXTUREALPHAPM:    iAOp = 11; break;
            }

            switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG1)])
            {
            default:
                SPIDPFM((SPIM_INVALID,"Invalid ALPHAARG1[0] == 0x%x\n",
                        (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG1)]));
            case (D3DTA_TEXTURE):
            case (D3DTA_ALPHAREPLICATE|D3DTA_TEXTURE):
                iAArg1 = 0; break;
            case (D3DTA_TEXTURE|D3DTA_COMPLEMENT):
            case (D3DTA_ALPHAREPLICATE|D3DTA_TEXTURE|D3DTA_COMPLEMENT):
                iAArg1 = 1; break;
            }

            switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG2)])
            {
            default:
                SPIDPFM((SPIM_INVALID,"Invalid ALPHAARG2[0] == 0x%x\n",
                        (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(0,D3DTSS_ALPHAARG2)]));
            case (D3DTA_DIFFUSE):
            case (D3DTA_ALPHAREPLICATE|D3DTA_DIFFUSE):
                iAArg2 = 0; break;
            case (D3DTA_CURRENT):
            case (D3DTA_ALPHAREPLICATE|D3DTA_CURRENT):
                iAArg2 = 1; break;
            case (D3DTA_TFACTOR):
            case (D3DTA_ALPHAREPLICATE|D3DTA_TFACTOR):
                iAArg2 = 2; break;
            case (D3DTA_COMPLEMENT|D3DTA_DIFFUSE):
            case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_DIFFUSE):
                iAArg2 = 3; break;
            case (D3DTA_COMPLEMENT|D3DTA_CURRENT):
            case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_CURRENT):
                iAArg2 = 4; break;
            case (D3DTA_COMPLEMENT|D3DTA_TFACTOR):
            case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_TFACTOR):
                iAArg2 = 5; break;
            }

            pCtx->pfnTexBlendOpColor[0] = pBeadTable->pTexBlendOpColorBeads->pfnTexBlendOpColor[iCOp];
            pCtx->pfnTexBlendGetColor[0] = pBeadTable->pTexBlendGetColorBeads->pfnTexBlendGetColor[iCArg2][iCArg1];
            pCtx->pfnTexBlendOpAlpha[0] = pBeadTable->pTexBlendOpAlphaBeads->pfnTexBlendOpAlpha[iAOp];
            pCtx->pfnTexBlendGetAlpha[0] = pBeadTable->pTexBlendGetAlphaBeads->pfnTexBlendGetAlpha[iAArg2][iAArg1];

            // general TexBlend
            if (pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLOROP)] == D3DTOP_DISABLE)
            {     \
                // Tex1_Gen
                iTextureBlend = 4;
            }
            else
            {
                // TexM_Gen
                iTextureBlend = 5;

                switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLOROP)])
                {
                default:
                    SPIDPFM((SPIM_INVALID,"Invalid COLOROP1[1] == 0x%x\n",
                            (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLOROP)]));
                case D3DTOP_DISABLE:                iCOp = 0;  break;
                case D3DTOP_SELECTARG1:             iCOp = 1;  break;
                case D3DTOP_SELECTARG2:             iCOp = 2;  break;
                case D3DTOP_MODULATE:               iCOp = 3;  break;
                case D3DTOP_MODULATE2X:             iCOp = 4;  break;
                case D3DTOP_MODULATE4X:             iCOp = 5;  break;
                case D3DTOP_ADD:                    iCOp = 6;  break;
                case D3DTOP_ADDSIGNED:              iCOp = 7;  break;
                case D3DTOP_BLENDDIFFUSEALPHA:      iCOp = 8;  break;
                case D3DTOP_BLENDTEXTUREALPHA:      iCOp = 9;  break;
                case D3DTOP_BLENDFACTORALPHA:       iCOp = 10; break;
                case D3DTOP_BLENDTEXTUREALPHAPM:    iCOp = 11; break;
                }

                switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLORARG1)])
                {
                default:
                    SPIDPFM((SPIM_INVALID,"Invalid COLORARG1[1] == 0x%x\n",
                            (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLORARG1)]));
                case (D3DTA_TEXTURE):
                    iCArg1 = 0; break;
                case (D3DTA_TEXTURE|D3DTA_COMPLEMENT):
                    iCArg1 = 1; break;
                case (D3DTA_ALPHAREPLICATE|D3DTA_TEXTURE):
                    iCArg1 = 2; break;
                case (D3DTA_ALPHAREPLICATE|D3DTA_TEXTURE|D3DTA_COMPLEMENT):
                    iCArg1 = 3; break;
                }

                switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLORARG2)])
                {
                case (D3DTA_DIFFUSE):
                    iCArg2 = 0; break;
                default:
                    SPIDPFM((SPIM_INVALID,"Invalid COLORARG2[1] == 0x%x\n",
                            (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_COLORARG2)]));
                case (D3DTA_CURRENT):
                    iCArg2 = 1; break;
                case (D3DTA_TFACTOR):
                    iCArg2 = 2; break;
                case (D3DTA_COMPLEMENT|D3DTA_DIFFUSE):
                    iCArg2 = 3; break;
                case (D3DTA_COMPLEMENT|D3DTA_CURRENT):
                    iCArg2 = 4; break;
                case (D3DTA_COMPLEMENT|D3DTA_TFACTOR):
                    iCArg2 = 5; break;
                case (D3DTA_ALPHAREPLICATE|D3DTA_DIFFUSE):
                    iCArg2 = 6; break;
                case (D3DTA_ALPHAREPLICATE|D3DTA_CURRENT):
                    iCArg2 = 7; break;
                case (D3DTA_ALPHAREPLICATE|D3DTA_TFACTOR):
                    iCArg2 = 8; break;
                case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_DIFFUSE):
                    iCArg2 = 9; break;
                case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_CURRENT):
                    iCArg2 = 10; break;
                case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_TFACTOR):
                    iCArg2 = 11; break;
                }

                switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ALPHAOP)])
                {
                default:
                    SPIDPFM((SPIM_INVALID,"Invalid ALPHAOP[1] == 0x%x\n",
                            (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ALPHAOP)]));
                case D3DTOP_DISABLE:                iAOp = 0;  break;
                case D3DTOP_SELECTARG1:             iAOp = 1;  break;
                case D3DTOP_SELECTARG2:             iAOp = 2;  break;
                case D3DTOP_MODULATE:               iAOp = 3;  break;
                case D3DTOP_MODULATE2X:             iAOp = 4;  break;
                case D3DTOP_MODULATE4X:             iAOp = 5;  break;
                case D3DTOP_ADD:                    iAOp = 6;  break;
                case D3DTOP_ADDSIGNED:              iAOp = 7;  break;
                case D3DTOP_BLENDDIFFUSEALPHA:      iAOp = 8;  break;
                case D3DTOP_BLENDTEXTUREALPHA:      iAOp = 9;  break;
                case D3DTOP_BLENDFACTORALPHA:       iAOp = 10; break;
                case D3DTOP_BLENDTEXTUREALPHAPM:    iAOp = 11; break;
                }

                switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ALPHAARG1)])
                {
                default:
                    SPIDPFM((SPIM_INVALID,"Invalid ALPHAARG1[1] == 0x%x\n",
                            (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ALPHAARG1)]));
                case (D3DTA_TEXTURE):
                case (D3DTA_ALPHAREPLICATE|D3DTA_TEXTURE):
                    iAArg1 = 0; break;
                case (D3DTA_TEXTURE|D3DTA_COMPLEMENT):
                case (D3DTA_ALPHAREPLICATE|D3DTA_TEXTURE|D3DTA_COMPLEMENT):
                    iAArg1 = 1; break;
                }

                switch(pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ALPHAARG2)])
                {
                case (D3DTA_DIFFUSE):
                case (D3DTA_ALPHAREPLICATE|D3DTA_DIFFUSE):
                    iAArg2 = 0; break;
                default:
                    SPIDPFM((SPIM_INVALID,"Invalid ALPHAARG2[1] == 0x%x\n",
                            (DWORD) pCtx->pdwRenderState[D3DHAL_TSS_OFFSET(1,D3DTSS_ALPHAARG2)]));
                case (D3DTA_CURRENT):
                case (D3DTA_ALPHAREPLICATE|D3DTA_CURRENT):
                    iAArg2 = 1; break;
                case (D3DTA_TFACTOR):
                case (D3DTA_ALPHAREPLICATE|D3DTA_TFACTOR):
                    iAArg2 = 2; break;
                case (D3DTA_COMPLEMENT|D3DTA_DIFFUSE):
                case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_DIFFUSE):
                    iAArg2 = 3; break;
                case (D3DTA_COMPLEMENT|D3DTA_CURRENT):
                case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_CURRENT):
                    iAArg2 = 4; break;
                case (D3DTA_COMPLEMENT|D3DTA_TFACTOR):
                case (D3DTA_ALPHAREPLICATE|D3DTA_COMPLEMENT|D3DTA_TFACTOR):
                    iAArg2 = 5; break;
                }

                pCtx->pfnTexBlendOpColor[1] = pBeadTable->pTexBlendOpColorBeads->pfnTexBlendOpColor[iCOp];
                pCtx->pfnTexBlendGetColor[1] = pBeadTable->pTexBlendGetColorBeads->pfnTexBlendGetColor[iCArg2][iCArg1];
                pCtx->pfnTexBlendOpAlpha[1] = pBeadTable->pTexBlendOpAlphaBeads->pfnTexBlendOpAlpha[iAOp];
                pCtx->pfnTexBlendGetAlpha[1] = pBeadTable->pTexBlendGetAlphaBeads->pfnTexBlendGetAlpha[iAArg2][iAArg1];
            }
        }
        RastCapRec.Set_TextureBlend(iTextureBlend);
    }

    // Ignore mono (D3DRENDERSTATE_MONOENABLE), since no software rasterizers do anything with it.
    iMono = 0;
    RastCapRec.Set_Mono(iMono);

    // values needed to choose ColorBlend bead
    iAlphaBlend =
        pCtx->pdwRenderState[D3DRENDERSTATE_ALPHABLENDENABLE] ? 1 : 0;
    RastCapRec.Set_AlphaBlend(iAlphaBlend);
    if (!iAlphaBlend ||
        ((pCtx->pdwRenderState[D3DRENDERSTATE_SRCBLEND] == D3DBLEND_ONE) &&
         (pCtx->pdwRenderState[D3DRENDERSTATE_DESTBLEND] == D3DBLEND_ZERO))) {
        iBlend = 0;
    } else {
        iBlend = 1;
    }
    RastCapRec.Set_Blend(iBlend);
    if (pCtx->pdwRenderState[D3DRENDERSTATE_ROP2] == R2_COPYPEN) {
        iROP = 0;
    } else {
        iROP = 1;
    }
    RastCapRec.Set_ROP(iROP);

    // value needed to choose DestBlend routine
    switch (pCtx->pdwRenderState[D3DRENDERSTATE_DESTBLEND])
    {
    default :
        SPIDPFM((SPIM_INVALID,"Invalid renderstate value, D3DRENDERSTATE_DESTBLEND == 0x%x\n",
            pCtx->pdwRenderState[D3DRENDERSTATE_DESTBLEND]));
    case D3DBLEND_ZERO :
        iDestBlend = 0;
        break;
    case D3DBLEND_ONE :
        iDestBlend = 1;
        break;
    case D3DBLEND_SRCCOLOR :
        iDestBlend = 2;
        break;
    case D3DBLEND_INVSRCCOLOR :
        iDestBlend = 3;
        break;
    case D3DBLEND_SRCALPHA :
        iDestBlend = 4;
        break;
    case D3DBLEND_INVSRCALPHA :
        iDestBlend = 5;
        break;
    case D3DBLEND_DESTALPHA :
        iDestBlend = 6;
        break;
    case D3DBLEND_INVDESTALPHA :
        iDestBlend = 7;
        break;
    case D3DBLEND_DESTCOLOR :
        iDestBlend = 8;
        break;
    case D3DBLEND_INVDESTCOLOR :
        iDestBlend = 9;
        break;
    case D3DBLEND_SRCALPHASAT :
        iDestBlend = 10;
        break;
    }

    // set iSrcBlend after iDestBlend so BOTHSRCALPHA can override iDestBlend
    // value needed to choose SrcBlend routine
    switch (pCtx->pdwRenderState[D3DRENDERSTATE_SRCBLEND])
    {
    case D3DBLEND_ZERO :
        iSrcBlend = 0;
        break;
    default :
        SPIDPFM((SPIM_INVALID,"Invalid renderstate value, D3DRENDERSTATE_SRCBLEND == 0x%x\n",
            pCtx->pdwRenderState[D3DRENDERSTATE_SRCBLEND]));
    case D3DBLEND_ONE :
        iSrcBlend = 1;
        break;
    case D3DBLEND_SRCCOLOR :
        iSrcBlend = 2;
        break;
    case D3DBLEND_INVSRCCOLOR :
        iSrcBlend = 3;
        break;
    case D3DBLEND_SRCALPHA :
        iSrcBlend = 4;
        break;
    case D3DBLEND_INVSRCALPHA :
        iSrcBlend = 5;
        break;
    case D3DBLEND_DESTALPHA :
        iSrcBlend = 6;
        break;
    case D3DBLEND_INVDESTALPHA :
        iSrcBlend = 7;
        break;
    case D3DBLEND_DESTCOLOR :
        iSrcBlend = 8;
        break;
    case D3DBLEND_INVDESTCOLOR :
        iSrcBlend = 9;
        break;
    case D3DBLEND_SRCALPHASAT :
        iSrcBlend = 10;
        break;
    // these are special and set both source and dest
    case D3DBLEND_BOTHSRCALPHA :
        iSrcBlend = 4;
        iDestBlend = 5;
        break;
    case D3DBLEND_BOTHINVSRCALPHA :
        iSrcBlend = 5;
        iDestBlend = 4;
        break;
    }
    RastCapRec.Set_SrcBlend(iSrcBlend);
    RastCapRec.Set_DestBlend(iDestBlend);

    // values needed to choose BufWrite bead and BufRead routine
    switch (pCtx->iSurfaceType)
    {
    default :
        SPIDPFM((SPIM_INVALID,"Invalid target surface type, iSurfaceType == 0x%x\n",
            pCtx->iSurfaceType));
    case RR_STYPE_B8G8R8X8:
        iTargetPixelFormat = 0;
        break;
    case RR_STYPE_B8G8R8A8:
        iTargetPixelFormat = 1;
        break;
    case RR_STYPE_B5G6R5:
        iTargetPixelFormat = 2;
        break;
    case RR_STYPE_B5G5R5:
        iTargetPixelFormat = 3;
        break;
    case RR_STYPE_B5G5R5A1:
        iTargetPixelFormat = 4;
        break;
    case RR_STYPE_B8G8R8:
        iTargetPixelFormat = 5;
        break;
    case RR_STYPE_PALETTE8:
        iTargetPixelFormat = 6;
        break;
    }
    RastCapRec.Set_TargetPixelFormat(iTargetPixelFormat);
    iDither = pCtx->pdwRenderState[D3DRENDERSTATE_DITHERENABLE] ? 1 : 0;
    RastCapRec.Set_Dither(iDither);
    iZDeferred = iAlphaTest || iAlphaDither[0];

    // Set optional postprocessing beads first so that their addresses
    // can be referenced later.
    pCtx->pfnPixelEnd = pBeadTable->pPixelEndBeads->pfnPixelEnd[0];
    pCtx->pfnSpanEnd = pBeadTable->pSpanEndBeads->pfnSpanEnd[0];

    // start stringing beads together, back to front order avoids a lot of
    // conditional logic

#if DBG
    // null out all bead ptrs
    pCtx->pfnBegin = NULL;
    pCtx->pfnLoopEnd = NULL;
    pCtx->pfnTestPassEnd = NULL;
    pCtx->pfnTestFailEnd = NULL;
    pCtx->pfnTex1AddrEnd = NULL;
    pCtx->pfnTexRead[0] = NULL;
    pCtx->pfnTexRead[1] = NULL;
    pCtx->pfnTex2AddrEnd = NULL;
    pCtx->pfnTexBlendEnd = NULL;
    pCtx->pfnColorGenEnd = NULL;
    pCtx->pfnAlphaTestPassEnd = NULL;
    pCtx->pfnAlphaTestFailEnd = NULL;
    pCtx->pfnSrcBlend =  NULL;
    pCtx->pfnDestBlend =  NULL;
    pCtx->pfnBufRead = NULL;
    pCtx->pfnColorBlendEnd = NULL;
    pCtx->pfnPixelEnd = NULL;
    pCtx->pfnSpanEnd = NULL;
#endif

    // range check indices used to select BufWrite bead
    RANGE_CHECK(iDither,DITHERING_NUM);
    RANGE_CHECK(iTargetPixelFormat,TARGETPIXELFORMAT_NUM);

    // select BufWrite bead, which can be called through pfnColorBlendEnd,
    // pfnAlphaTestPassEnd, pfnColorGenEnd, pfnTexBlendEnd
    pCtx->pfnTexBlendEnd =
        pCtx->pfnColorGenEnd =
        pCtx->pfnAlphaTestPassEnd =
        pCtx->pfnColorBlendEnd = pBeadTable->pBufWriteBeads->pfnBufWrite[iDither][iTargetPixelFormat];
#if DBG
    strcpy(pCtx->szBufWrite,rgszBufWrite[iDither][iTargetPixelFormat]);
#endif

    // range check indices used to select ColorBlend bead
    RANGE_CHECK(iROP,ROP_NUM);
    RANGE_CHECK(iBlend,BLEND_NUM);

    // select ColorBlend bead, which can be called through
    // pfnATextEnd, pfnColorGenEnd, or pfnTexBlendEnd
    if ((pfnColorBlend = pBeadTable->pColorBlendBeads->pfnColorBlend[iROP][iBlend]) == NULL) {
        pCtx->pfnColorBlendEnd = NULL;
    } else {
        pCtx->pfnTexBlendEnd =
            pCtx->pfnColorGenEnd =
            pCtx->pfnAlphaTestPassEnd = pfnColorBlend;
    }
#if DBG
    strcpy(pCtx->szColorBlend,rgszColorBlend[iROP][iBlend]);
#endif

    // range check index used to select BufRead bead
    RANGE_CHECK(iTargetPixelFormat,TARGETPIXELFORMAT_NUM);

    // select Buf Read routine
    pCtx->pfnBufRead = pBeadTable->pBufReadBeads->pfnBufRead[iTargetPixelFormat];
#if DBG
    strcpy(pCtx->szBufRead,rgszBufRead[iTargetPixelFormat]);
#endif

    if (iBlend) {
        // range check index used to select DestBlend bead
        RANGE_CHECK(iDestBlend,DESTBLEND_NUM);

        // select DestBlend routine
        pCtx->pfnDestBlend = pBeadTable->pDestBlendBeads->pfnDestBlend[iDestBlend];
#if DBG
        strcpy(pCtx->szDestBlend,rgszDestBlend[iDestBlend]);
#endif

        // range check index used to select SrcBlend bead
        RANGE_CHECK(iSrcBlend,SRCBLEND_NUM);

        // select SrcBlend routine
        pCtx->pfnSrcBlend = pBeadTable->pSrcBlendBeads->pfnSrcBlend[iSrcBlend];
#if DBG
        strcpy(pCtx->szSrcBlend,rgszSrcBlend[iSrcBlend]);
#endif
    } else {
        pCtx->pfnDestBlend = NULL;
        pCtx->pfnSrcBlend = NULL;
#if DBG
        strcpy(pCtx->szDestBlend,"NULL");
        strcpy(pCtx->szSrcBlend,"NULL");
#endif
    }

    // select AlphaTest/AlphaDitheringTest bead, which can be called through
    // pfnColorBlendEnd, pfnColorGenEnd, or pfnTexBlendEnd

    // range check indices used to select AlphaTest bead
    RANGE_CHECK(iStencil,STENCIL_NUM);
    RANGE_CHECK(iZFormat,ZFORMAT_NUM);
    RANGE_CHECK(iZWrite,ZWRITE_NUM);
    RANGE_CHECK(iAlphaDither[0],COLORKEY_NUM);
    RANGE_CHECK(iAlphaTest,ALPHATEST_NUM);

    if (iAlphaTest || iAlphaDither[0])
    {
        pCtx->pfnTexBlendEnd =
            pCtx->pfnColorGenEnd =
                pBeadTable->pAlphaTestBeads->pfnAlphaTest[iStencil]
                                                         [iZFormat]
                                                         [iZWrite || iStencil]
                                                         [iAlphaDither[0]]
                                                         [iAlphaTest];

        // If AlphaTest fails, it goes to where the BufWrite bead ends
        pCtx->pfnAlphaTestFailEnd = pCtx->pfnPixelEnd;
#if DBG
        strcpy(pCtx->szAlphaTest,rgszAlphaTest[iStencil][iZFormat][iZWrite]
                                              [iAlphaDither[0]][iAlphaTest]);
#endif
    }
    else
    {
        pCtx->pfnAlphaTestPassEnd = NULL;
        pCtx->pfnAlphaTestFailEnd = NULL;
#if DBG
        strcpy(pCtx->szAlphaTest, "NULL");
#endif
    }


    // range check indices used to select ColorGen bead
    RANGE_CHECK(iMono,MONO_NUM);
    RANGE_CHECK(iVertexFog,VERTEXFOG_NUM);
    RANGE_CHECK(iSpecular,SPECULAR_NUM);
    RANGE_CHECK(iShadeMode,SHADEMODE_NUM);

    // select ColorGen bead, which can be called through pfnTexBlendEnd
    if ((pfnColorGen =
        pBeadTable->pColorGenBeads->pfnColorGen[iMono][iVertexFog][iSpecular][iShadeMode]) == NULL) {
        pCtx->pfnColorGenEnd = NULL;
    } else {
        pCtx->pfnTexBlendEnd = pfnColorGen;
    }
#if DBG
    strcpy(pCtx->szColorGen,
        rgszColorGen[iMono][iVertexFog][iSpecular][iShadeMode]);
#endif

    // range check indices used to select TexBlend bead
    RANGE_CHECK(iTextureBlend,TEXTUREBLEND_NUM);

    // select TexBlend bead, which can be called through pfnTexAddr2End,
    // pfnTexAddr1End, pfnTestPassEnd, or pfnLoopEnd
    pCtx->pfnLoopEnd =
        pCtx->pfnTestPassEnd =
        pCtx->pfnTex1AddrEnd =
        pCtx->pfnTex2AddrEnd = pBeadTable->pTexBlendBeads->pfnTexBlend[iTextureBlend];
#if DBG
    strcpy(pCtx->szTexBlend,rgszTexBlend[iTextureBlend]);
#endif

    if (iTexture) {

        if (pCtx->cActTex >= 2)
        {
            // range check indices used to select TexRead bead
            RANGE_CHECK(iTextureColorKey[1],COLORKEY_NUM);
            RANGE_CHECK(iTextureBorder[1],TEXTUREBORDER_NUM);
            RANGE_CHECK(iTextureFormat[1],TEXTUREFORMAT_NUM);

            // select TexAddr2 bead, which can be called through pfnTexAddr1
            // select TexRead routine
            pCtx->pfnTexRead[1] =
                pBeadTable->pTexReadBeads->pfnTexRead[iTextureColorKey[1]][iTextureBorder[1]][iTextureFormat[1]];

#if DBG
            strcpy(pCtx->szTexRead[1],
                rgszTexRead[iTextureColorKey[1]][iTextureBorder[1]][iTextureFormat[1]]);
#endif

            // range check indices used to select Tex1Addr bead
            RANGE_CHECK(iTextureLOD[1],TEXTURELOD_NUM);
            RANGE_CHECK(iTextureFilter[1],TEXTUREFILTER_NUM);
            RANGE_CHECK(iTexturePerspective,TEXTUREPERSPECTIVE_NUM);
            RANGE_CHECK(iTextureAddr[1],TEXTUREADDRESS_NUM);

            // select TexAddr1 bead, which can be called through pfnTestPassEnd or
            // pfnLoopEnd
            if ((pfnTex2Addr = (pBeadTable->pTex2AddrBeads->pfnTex2Addr[iTextureFilter[1]]
                                [iTexturePerspective][iTextureAddr[1]])) == NULL) {
                pCtx->pfnTex2AddrEnd = NULL;
            } else {
                pCtx->pfnLoopEnd =
                    pCtx->pfnTex1AddrEnd = pfnTex2Addr;
            }
#if DBG
            strcpy(pCtx->szTex2Addr,rgszTex2Addr[iTextureFilter[1]]
                [iTexturePerspective][iTextureAddr[1]]);
#endif
        } else {
            pCtx->pfnTex2AddrEnd = NULL;
            pCtx->pfnTexRead[1] = NULL;
#if DBG
            strcpy(pCtx->szTex2Addr,"NULL");
            strcpy(pCtx->szTexRead[1],"NULL");
#endif
        }

        // range check indices used to select TexRead bead
        RANGE_CHECK(iTextureColorKey[0],COLORKEY_NUM);
        RANGE_CHECK(iTextureBorder[0],TEXTUREBORDER_NUM);
        RANGE_CHECK(iTextureFormat[0],TEXTUREFORMAT_NUM);

        // select TexRead routine
        pCtx->pfnTexRead[0] =
            pBeadTable->pTexReadBeads->pfnTexRead[iTextureColorKey[0]][iTextureBorder[0]][iTextureFormat[0]];
#if DBG
        strcpy(pCtx->szTexRead[0],
            rgszTexRead[iTextureColorKey[0]][iTextureBorder[0]][iTextureFormat[0]]);
#endif

        // range check indices used to select Tex1Addr bead
        RANGE_CHECK(iTextureLOD[0],TEXTURELOD_NUM);
        RANGE_CHECK(iTextureFilter[0],TEXTUREFILTER_NUM);
        RANGE_CHECK(iTexturePerspective,TEXTUREPERSPECTIVE_NUM);
        RANGE_CHECK(iTextureAddr[0],TEXTUREADDRESS_NUM);

        // select TexAddr1 bead, which can be called through pfnTestPassEnd or
        // pfnLoopEnd
        if ((pfnTex1Addr = ((iTextureMip[0]) ?
            (pBeadTable->pTex1AddrMipBeads->pfnTex1AddrMip[0]) :
            (pBeadTable->pTex1AddrBeads->pfnTex1Addr[iTextureLOD[0]][iTextureFilter[0]]
                            [iTexturePerspective][iTextureAddr[0]]))) == NULL) {
            pCtx->pfnTex1AddrEnd = NULL;
        } else {
            pCtx->pfnLoopEnd =
                pCtx->pfnTestPassEnd = pfnTex1Addr;
        }
#if DBG
        if (iTextureMip[0])
        {
            strcpy(pCtx->szTex1Addr,rgszTex1AddrMip[0]);
        }
        else
        {
            strcpy(pCtx->szTex1Addr,rgszTex1Addr[iTextureLOD[0]][iTextureFilter[0]]
                [iTexturePerspective][iTextureAddr[0]]);
        }
#endif
    } else {
        pCtx->pfnTex2AddrEnd = NULL;
        pCtx->pfnTexRead[0] = NULL;
        pCtx->pfnTex1AddrEnd = NULL;
#if DBG
        strcpy(pCtx->szTex2Addr,"NULL");
        strcpy(pCtx->szTexRead[0],"NULL");
        strcpy(pCtx->szTex1Addr,"NULL");
#endif
    }

    // select Test bead, which can be called through pfnLoopEnd
    if (iZTest) {

        // range check indices used to select Test bead
        RANGE_CHECK(iStencil,STENCIL_NUM);
        RANGE_CHECK(iZFunc,ZFUNC_NUM);
        RANGE_CHECK(iZWrite,ZWRITE_NUM);
        // ATTENTION range check this
//        RANGE_CHECK(iAlphaTest,ALPHATEST_NUM);
        RANGE_CHECK(iZFormat,ZFORMAT_NUM);
        RANGE_CHECK(iSpecular,SPECULAR_NUM);
        RANGE_CHECK(iVertexFog,VERTEXFOG_NUM);
        RANGE_CHECK(iTexture,TEXTURE_NUM);
        RANGE_CHECK(iShadeMode,SHADEMODE_NUM);

        // if alpha testing, defer Z write
        pCtx->pfnLoopEnd = pBeadTable->pTestBeads->pfnTest[iStencil][iZFunc]
            [iZDeferred][iZWrite][iZFormat];

        // select where this bead is going to go if the test fails
        pCtx->pfnTestFailEnd =
            pBeadTable->pTestFailBeads->pfnTestFail[iSpecular | iVertexFog][iTexture][iShadeMode];

        if (iZDeferred && iStencil)
        {
            // If alpha testing and stenciling, need to
            // defer stencil update until after the alpha test.
            //
            // This is not an issue for Z, since we never want to write Z
            // if the Z test fails (hence we do not care about the outcome of
            // the alpha test.
            pCtx->pfnTestFailEnd = pCtx->pfnTestPassEnd;
        }

#if DBG
        strcpy(pCtx->szTest,rgszTest[iStencil][iZFunc][iZDeferred][iZWrite][iZFormat]);
        strcpy(pCtx->szTestFail,
            rgszTestFail[iSpecular | iVertexFog][iTexture][iShadeMode]);
#endif
    } else {
        pCtx->pfnTestPassEnd = NULL;
        pCtx->pfnTestFailEnd = NULL;
#if DBG
        strcpy(pCtx->szTest,"NULL");
        strcpy(pCtx->szTestFail,"NULL");
#endif
    }

    // select Loop bead
    pCtx->pfnBegin = pBeadTable->pBeginBeads->pfnBegin[0];

    pCtx->pfnRenderSpans = pBeadTable->pRenderSpansBeads->pfnRenderSpans[0];

#if DBG
    SPIDPFM((SPIM_REPORT,"Test = %s\n",pCtx->szTest));
    SPIDPFM((SPIM_REPORT,"TestFail = %s\n",pCtx->szTestFail));
    SPIDPFM((SPIM_REPORT,"Tex1Addr = %s\n",pCtx->szTex1Addr));
    SPIDPFM((SPIM_REPORT,"TexRead1 = %s\n",pCtx->szTexRead[0]));
    SPIDPFM((SPIM_REPORT,"TexRead2 = %s\n",pCtx->szTexRead[1]));
    SPIDPFM((SPIM_REPORT,"Tex2Addr = %s\n",pCtx->szTex2Addr));
    SPIDPFM((SPIM_REPORT,"TexBlend = %s\n",pCtx->szTexBlend));
    SPIDPFM((SPIM_REPORT,"ColorGen = %s\n",pCtx->szColorGen));
    SPIDPFM((SPIM_REPORT,"ColorBlend = %s\n",pCtx->szColorBlend));
    SPIDPFM((SPIM_REPORT,"SrcBlend = %s\n",pCtx->szSrcBlend));
    SPIDPFM((SPIM_REPORT,"DestBlend = %s\n",pCtx->szDestBlend));
    SPIDPFM((SPIM_REPORT,"BufRead = %s\n",pCtx->szBufRead));
    SPIDPFM((SPIM_REPORT,"AlphaTest = %s\n",pCtx->szAlphaTest));
    SPIDPFM((SPIM_REPORT,"BufWrite = %s\n",pCtx->szBufWrite));
    SPIDPFM((SPIM_REPORT,"RastCap = 0x%x 0x%x 0x%x\n",
        RastCapRec.GetCapDWord(0),
        RastCapRec.GetCapDWord(1),
        RastCapRec.GetCapDWord(2)));


#if 0
    DWORD dwCheckSum = RastCapRec.GetCapDWord(0) ^ RastCapRec.GetCapDWord(1) ^ RastCapRec.GetCapDWord(2);
    static DWORD dwCheckSumLast;
    if (dwCheckSum != dwCheckSumLast)
    {
        dwCheckSumLast = dwCheckSum;
        DPF(0, "Spaninit");
        DPF(0, "Test = %s",pCtx->szTest);
        DPF(0, "TestFail = %s",pCtx->szTestFail);
        DPF(0, "Tex1Addr = %s",pCtx->szTex1Addr);
        DPF(0, "TexRead1 = %s",pCtx->szTexRead[0]);
        DPF(0, "TexRead2 = %s",pCtx->szTexRead[1]);
        DPF(0, "Tex2Addr = %s",pCtx->szTex2Addr);
        DPF(0, "TexBlend = %s",pCtx->szTexBlend);
        DPF(0, "ColorGen = %s",pCtx->szColorGen);
        DPF(0, "ColorBlend = %s",pCtx->szColorBlend);
        DPF(0, "SrcBlend = %s",pCtx->szSrcBlend);
        DPF(0, "DestBlend = %s",pCtx->szDestBlend);
        DPF(0, "BufRead = %s",pCtx->szBufRead);
        DPF(0, "AlphaTest = %s",pCtx->szAlphaTest);
        DPF(0, "BufWrite = %s",pCtx->szBufWrite);
        DPF(0, "RastCap = 0x%x 0x%x 0x%x",
            RastCapRec.GetCapDWord(0),
            RastCapRec.GetCapDWord(1),
            RastCapRec.GetCapDWord(2));
    }
#endif
#endif

    // is there a monolithic rasterizer that can do exactly
    // what is needed?
    RASTFNREC* pfnRastFnRec;
    if ((pfnRastFnRec = g_RastCollection.Search(pCtx, &RastCapRec)) != NULL)
    {
        // yes, then replace beaded rasterizer with monolithic
        DDASSERT(pfnRastFnRec->pfnRastFunc != 0);
        pCtx->pfnRenderSpans = pfnRastFnRec->pfnRastFunc;

#if DBG
        // null out all the others
        pCtx->pfnBegin =
            pCtx->pfnLoopEnd =
            pCtx->pfnTestPassEnd =
            pCtx->pfnTestFailEnd =
            pCtx->pfnTex1AddrEnd =
            pCtx->pfnTex2AddrEnd =
            pCtx->pfnTexBlendEnd =
            pCtx->pfnColorGenEnd =
            pCtx->pfnAlphaTestPassEnd =
            pCtx->pfnAlphaTestFailEnd =
            pCtx->pfnColorBlendEnd =
            pCtx->pfnPixelEnd = NULL;
#endif
    }

#if DBG
    static RASTFNREC* pfnLastRastFnRec = (RASTFNREC*)-1;
    if (pfnLastRastFnRec != pfnRastFnRec)
    {
        if (pfnRastFnRec)
        {
            D3D_INFO(1, "SpanInit: Fast path rasterizer %s chosen", pfnRastFnRec->pszRastDesc);
        }
        else
        {
            D3D_WARN(1, "SpanInit: No fast path rasterizer chosen");
        }
        pfnLastRastFnRec = pfnRastFnRec;
    }
#endif

    return S_OK;
}

//-----------------------------------------------------------------------------
//
// RampSpanInit
//
// Initializes derived state (function pointers and arithmetic compare values)
// based on the render state, surface type, etc. for Ramp.  This is independent of
// and quite different from the non-ramp beads.
//
//-----------------------------------------------------------------------------
HRESULT RampSpanInit(PD3DI_RASTCTX pCtx, PRAMPBEADTABLE pBeadTable)
{
    int iTargetPixelFormat,
        iZTest,
        iZWrite,
        iDither,
        iTexture,
        iTrans,
        iTextureCopy = 0,
        iTextureMonoCopy = 0,
        iTextureFormat,
        iTextureColorKey,
        iShadeMode;

    static BOOL bInitRampBeadTbl = FALSE;
    static RAMPOLDBEADS* pRampOld_BeadTbl = NULL;
    static PFNRAMPOLDTRI pfnRampOldTri = NULL;

    if (!bInitRampBeadTbl)
    {
        bInitRampBeadTbl = TRUE;
        Ramp_InitBeadTbl();

#if _X86_
        // also see if there are fast DX5 based ramp routines
        HINSTANCE hRampOldDLL = LoadLibrary("d3dramp.dll");
        if (hRampOldDLL)
        {
            pRampOld_BeadTbl = (RAMPOLDBEADS*) GetProcAddress(hRampOldDLL, "g_RampOld_BeadTbl");
            pfnRampOldTri = (PFNRAMPOLDTRI) GetProcAddress(hRampOldDLL, "RampOldTri");
        }
#endif

    }

    DoZCompareSetup(pCtx, FALSE);

    switch (pCtx->iSurfaceType)
    {
    default :
        SPIDPFM((SPIM_INVALID,"Invalid target surface type, "
                 "iSurfaceType == 0x%x\n",
                 pCtx->iSurfaceType));
        return DDERR_INVALIDPARAMS;
    case RR_STYPE_PALETTE8:
        iTargetPixelFormat = 0;
        break;
    case RR_STYPE_B5G6R5:
    case RR_STYPE_B5G5R5:
    case RR_STYPE_B5G5R5A1:
        iTargetPixelFormat = 1;
        break;
    case RR_STYPE_B8G8R8X8:
    case RR_STYPE_B8G8R8A8:
    case RR_STYPE_B8G8R8:
        iTargetPixelFormat = 2;
        break;
    }
    iZTest = pCtx->pdwRenderState[D3DRENDERSTATE_ZENABLE] ? 1 : 0;
    iZWrite = pCtx->pdwRenderState[D3DRENDERSTATE_ZWRITEENABLE] ? 1 : 0;
    iDither = pCtx->pdwRenderState[D3DRENDERSTATE_DITHERENABLE] ? 1 : 0;

    // are we texturing at all?
    if (pCtx->cActTex == 0)
    {
        iTexture = 0;
        iTextureFormat = 0;
        iTextureColorKey = pCtx->pdwRenderState[D3DRENDERSTATE_COLORKEYENABLE];
    }
    else
    {
        iTexture =
            pCtx->pdwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ? 9 : 1;

        // values needed to chose TexRead routine
        switch(pCtx->pTexture[0]->Format)
        {
        case D3DI_SPTFMT_PALETTE4 :
            if (pCtx->pTexture[0]->pPalette == NULL)
            {
                D3D_ERR("(Rast) NULL palette in paletted texture");
                return DDERR_INVALIDPARAMS;
            }
            iTextureFormat = 0;
            break;
        case D3DI_SPTFMT_B5G6R5:
        case D3DI_SPTFMT_B5G5R5:
        case D3DI_SPTFMT_B5G5R5A1:
            if (pCtx->pdwRenderState[D3DRENDERSTATE_TEXTUREMAPBLEND]
                 == D3DTBLEND_COPY)
            {
                iTextureFormat = 2;
                break;
            }
        default :
            SPIDPFM((SPIM_INVALID,"Invalid texture surface bit count, "
                "Format == 0x%x\n",(DWORD) pCtx->pTexture[0]->Format));
            return DDERR_INVALIDPARAMS;
        case D3DI_SPTFMT_PALETTE8 :
            if (pCtx->pTexture[0]->pPalette == NULL)
            {
                D3D_ERR("(Rast) NULL palette in paletted texture");
                return DDERR_INVALIDPARAMS;
            }
            iTextureFormat = 1;
            break;
        }

        iTexture += iTextureFormat;

        if ((pCtx->pdwRenderState[D3DRENDERSTATE_COLORKEYENABLE] ||
             pCtx->pdwRenderState[D3DRENDERSTATE_ALPHABLENDENABLE]) &&
             ((pCtx->pTexture[0]->uFlags & D3DI_SPANTEX_HAS_TRANSPARENT) != 0))
        {
            iTextureColorKey = 1;
            iTexture += 2;
        }
        else
        {
            iTextureColorKey = 0;
        }

        if (pCtx->pdwRenderState[D3DRENDERSTATE_TEXTUREMAPBLEND] ==
            D3DTBLEND_COPY)
        {
            iTextureCopy = 1;
            if ( (D3DI_SPTFMT_PALETTE4 == pCtx->pTexture[0]->Format) ||
                 (D3DI_SPTFMT_PALETTE8 == pCtx->pTexture[0]->Format) )
            {
                iTextureMonoCopy = 1;
            }
            else
            {
                iTextureMonoCopy = 2;
            }
            iTexture += 4;
        }
    }

    iTrans = pCtx->pdwRenderState[D3DRENDERSTATE_ALPHABLENDENABLE] ||
        iTextureColorKey;

    switch (pCtx->pdwRenderState[D3DRENDERSTATE_SHADEMODE])
    {
    case D3DSHADE_FLAT :
        iShadeMode = 0;
        break;
    default :
        SPIDPFM((SPIM_INVALID,"Invalid renderstate value, "
                 "D3DRENDERSTATE_SHADEMODE == 0x%x\n",
                 pCtx->pdwRenderState[D3DRENDERSTATE_SHADEMODE]));
    case D3DSHADE_GOURAUD :
    case D3DSHADE_PHONG :
        iShadeMode = 1;
        break;
    }

#if DBG
    static PFNRAMPOLD pfnRampOldOld = NULL;
#endif

    // see if we can use an old DX5 ramp assembly routine
    if ( (pRampOld_BeadTbl) &&
         (iTargetPixelFormat <= 1) && (iDither == 0) &&
         ((iTexture == 0) || ((iTextureFormat == 1) && (iTextureCopy == 0))) )
    {

        int iRampOldTexture = 0;
        if (iTexture)
        {
            if (iTexture >= 9)
            {
                iRampOldTexture = 2;
            }
            else
            {
                iRampOldTexture = 1;
            }
        }

        int iRampOldTrans = iTrans;
        if (iRampOldTexture)
        {
            iRampOldTrans = iTextureColorKey;
        }

        pCtx->pfnRampOld = pRampOld_BeadTbl->pfnRampOld[iTargetPixelFormat][iZTest][iShadeMode][iRampOldTexture][iRampOldTrans];
        if (pCtx->pfnRampOld)
        {
            pCtx->pfnRampOldTri = pfnRampOldTri;
        }
    }

#if DBG
    if ((pfnRampOldOld == NULL) && (pCtx->pfnRampOld != NULL))
    {
        D3D_INFO(1, "Starting to use DX5 Ramp routines.");
        pfnRampOldOld = pCtx->pfnRampOld;
    }

    if ((pfnRampOldOld != NULL) && (pCtx->pfnRampOld == NULL))
    {
        D3D_INFO(1, "Starting to use DX6 Ramp routines.");
// Uncomment this to see why DX5 rasterizer was not chosen
//#define WHY_IS_RAMP_SLOW_DEBUG 1
//
#if WHY_IS_RAMP_SLOW_DEBUG
        D3D_INFO(1, "Not using DX5 Ramp routines because:");
        D3D_INFO(1, "    pRampOld_BeadTbl 0x%x, iTargetPixelFormat %d, iDither %d",
                 pRampOld_BeadTbl, iTargetPixelFormat, iDither);
        D3D_INFO(1, "    iTexture %d, iTextureFormat %d, iTextureCopy %d",
                 iTexture, iTextureFormat, iTextureCopy);
        D3D_INFO(1, "    pCtx->pfnRampOld 0x%x, iZTest %d, iShadeMode %d, iTrans %d",
                 pCtx->pfnRampOld, iZTest, iShadeMode, iTrans);
#endif

        pfnRampOldOld = pCtx->pfnRampOld;
    }
#endif

    // Select TexRead routine.
    pCtx->pfnTexRead[0] =
        pBeadTable->pTexReadBeads->pfnTexRead[iTextureCopy][iTextureColorKey]
        [iTextureFormat];

    // Select Mono routine.
    pCtx->pfnLoopEnd = pBeadTable->pMonoBeads->pfnMono[iTextureMonoCopy];

    // Select Begin routine.
    pCtx->pfnBegin = pBeadTable->pBeginBeads->pfnBegin[0];

    // Select RenderSpans routine.
    // Specialized beads can only be used when:
    //    Z compare is less-equal.
    //    Target surface is 8 or 16 bit.
    //    When in COPY mode, target surface is 8.
    if ((iZTest &&
         pCtx->pdwRenderState[D3DRENDERSTATE_ZFUNC] != D3DCMP_LESSEQUAL) ||
        iTargetPixelFormat > 1 ||
        iTextureMonoCopy > 1)
    {
        pCtx->pfnRenderSpans = pBeadTable->pfnRenderSpansAny;
    }
    else
    {
        pCtx->pfnRenderSpans = pBeadTable->pRenderSpansBeads->pfnRenderSpans
            [iTexture][iShadeMode][iTrans][iDither][iZWrite][iZTest]
            [iTargetPixelFormat];
    }

#define DBG_RAMPCHOICE
// #define DBG_RAMPCHOICE_ALL
#ifdef DBG_RAMPCHOICE
#ifndef DBG_RAMPCHOICE_ALL
    static BOOL bShowedChoice = FALSE;
#endif
    char msg[80];
    static UINT32 iOldRampRsIndex = 0xffffffff;
    UINT32 iRampRsIndex;

    iRampRsIndex =
        iTargetPixelFormat +
        iZTest * RAMPFORMAT_NUM +
        iZWrite * (RAMPZTEST_NUM * RAMPFORMAT_NUM) +
        iDither * (RAMPZWRITE_NUM * RAMPZTEST_NUM * RAMPFORMAT_NUM) +
        iTrans * (RAMPDITHER_NUM * RAMPZWRITE_NUM * RAMPZTEST_NUM *
                  RAMPFORMAT_NUM) +
        iShadeMode * (RAMPTRANS_NUM * RAMPDITHER_NUM * RAMPZWRITE_NUM *
                      RAMPZTEST_NUM * RAMPFORMAT_NUM) +
        iTexture * (RAMPSHADEMODE_NUM * RAMPTRANS_NUM * RAMPDITHER_NUM *
                    RAMPZWRITE_NUM * RAMPZTEST_NUM * RAMPFORMAT_NUM);
#ifndef DBG_RAMPCHOICE_ALL
    if (!bShowedChoice)
#endif
    {
        if (iRampRsIndex != iOldRampRsIndex)
        {
            if (pCtx->pfnRenderSpans == pBeadTable->pfnRenderSpansAny)
            {
                OutputDebugStringA("!! GENERIC RAMP !!: ");
#ifndef DBG_RAMPCHOICE_ALL
                bShowedChoice = TRUE;
#else
            }
#endif
            sprintf(msg, "Ramp %d:%d:%d:%d:%d:%d:%d = %d\n",
                    iTexture, iShadeMode, iTrans, iDither, iZWrite, iZTest,
                    iTargetPixelFormat, iRampRsIndex);
            OutputDebugStringA(msg);
            iOldRampRsIndex = iRampRsIndex;
#ifndef DBG_RAMPCHOICE_ALL
            }
#endif
        }
    }
#endif

    return S_OK;
}