/******************************Module*Header**********************************\
*
*                           *******************
*                           * D3D SAMPLE CODE *
*                           *******************
*
* Module Name: d3d.c
*
* Content: Main context and texture management callbacks for D3D
*
* Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-1999 Microsoft Corporation.  All rights reserved.
\*****************************************************************************/

#include "precomp.h"
#include "d3dhw.h"
#include "d3dcntxt.h"
#include "d3ddelta.h"
#include "d3dtxman.h"
#define ALLOC_TAG ALLOC_TAG_3D2P
BOOL D3DInitialised = FALSE;

//-----------------------------Public Routine----------------------------------
//
// DWORD D3DContextCreate
//
// The ContextCreate callback is invoked when a new Direct3D device is being 
// created by a Direct3D application. The driver is required to generate a 
// unique context id for this new context. Direct3D will then use this context 
// id in every subsequent callback invocation for this Direct3D device. 
//
// Context is the current rasterization state. For instance, if there are 3 
// applications running, each will have a different state at any point in time.
// When each one is running, the hardware has to make sure that the context, 
// (whether doing Gouraud shading, for example) is the same as the last time 
// that application got a time slice. 
//
// State is anything that the particular device needs to know per context 
// i.e. what surface is being rendered to, shading, texture, texture handles, 
// what physical surfaces those texture handles represent, etc. The context 
// encapsulates all state for the Direct3D device - state is not shared 
// between contexts. Therefore the driver needs to maintain full state 
// information for each context. This state will be changed by calls to the 
// RenderState callback. In the case of rasterization only hardware, the 
// driver need only maintain rasterization state. As well as state, the driver 
// will also want to store the lpDDS, lpDDSZ, and dwPid from the callback 
// data argument. 
//
// The driver should not create a context handle of zero. This is guaranteed 
// to be an invalid context handle. 
//
// Parameters
//      pccd
//           Pointer to a structure containing things including the current
//           rendering surface, the current Z surface, and the DirectX object
//           handle, etc.
//
//          .lpDDGbl    
//                Points to the DirectDraw structure representing the 
//                DirectDraw object. 
//          .lpDDLcl(replaces lpDDGbl in DX7)    
//                Points to the DirectDraw structure representing the 
//                DirectDraw object. 
//          .lpDDS      
//                This is the surface that is to be used as the rendering 
//                target, i.e., the 3D accelerator sprays its bits at this 
//                surface. 
//          .lpDDSZ     
//                The surface that is to be used as the Z buffer. If this 
//                is NULL, no Z buffering is to be performed. 
//          .dwPid      
//                The process id of the Direct3D application that initiated 
//                the creation of the Direct3D device. 
//          .dwhContext 
//                The driver should place the context ID that it wants Direct3D 
//                to use when communicating with the driver. This should be 
//                unique. 
//          .ddrval     
//                Return code. DD_OK indicates success. 
//
// Return Value
//      Returns one of the following values: 
//                DDHAL_DRIVER_HANDLED  
//                DDHAL_DRIVER_NOTHANDLED   
//
//-----------------------------------------------------------------------------

TextureCacheManager P2TextureManager;
DWORD   P2TMcount = 0;

DWORD CALLBACK 
D3DContextCreate(LPD3DHAL_CONTEXTCREATEDATA pccd)
{
    PERMEDIA_D3DCONTEXT* pContext;
    PermediaSurfaceData* pPrivateData;
    DWORD dwSlotNum;

    LPDDRAWI_DIRECTDRAW_GBL lpDDGbl=pccd->lpDDLcl->lpGbl;

    // Remember the global data for this context.
    PPDev ppdev = (PPDev)lpDDGbl->dhpdev;
    PERMEDIA_DEFS(ppdev);

    DBG_D3D((6,"Entering D3DContextCreate"));

    // Find an empty slot in the global D3D context table
    for (dwSlotNum = 1; dwSlotNum < MAX_CONTEXT_NUM; dwSlotNum++) 
    {
        if (ContextSlots[dwSlotNum] == 0) 
            break;
    }

    // return if we have no contexts left
    if (dwSlotNum == MAX_CONTEXT_NUM)
    {
        pccd->ddrval = D3DHAL_OUTOFCONTEXTS;
        return (DDHAL_DRIVER_HANDLED);
    }

    // Now allocate the drivers D3D context memory.  Simply a chunk of
    // RAM with the relevent data in it.
    pContext = (PERMEDIA_D3DCONTEXT *)
        ENGALLOCMEM( FL_ZERO_MEMORY, sizeof(PERMEDIA_D3DCONTEXT), ALLOC_TAG);

    if (pContext == NULL)
    {
        DBG_D3D((0,"ERROR: Couldn't allocate Context mem"));
        pccd->ddrval = DDERR_OUTOFMEMORY;
        return (DDHAL_DRIVER_HANDLED);
    }
    else
    {
        DBG_D3D((4,"Allocated Context Mem"));
        memset((void *)pContext, 0, sizeof(PERMEDIA_D3DCONTEXT));
    }

    // Setup the drivers's D3D context
    pContext->Hdr.pSelf = (UINT_PTR)pContext;

    // Set up the DRIVER rendering context structure for sanity checks
    pContext->Hdr.MagicNo = RC_MAGIC_NO;

    // Remember the card we are running on
    pContext->ppdev = ppdev;

    // Set context handle in driver's D3D context
    pccd->dwhContext = dwSlotNum;                 //out:Context handle
    ContextSlots[dwSlotNum] = (UINT_PTR)pContext;

    DBG_D3D((4,"Allocated Direct3D context: 0x%x",pccd->dwhContext));

    // Allocate a register context
    P2CtxtPtr pP2ctxt;

    pP2ctxt = P2AllocateNewContext( pContext->ppdev, NULL, 0, P2CtxtWriteOnly);

    if (pP2ctxt == NULL)
    {
        DBG_D3D((0,"ERROR: Couldn't allocate Register Context"));
        CleanDirect3DContext(pContext, pccd->dwhContext);
        pccd->ddrval = DDERR_OUTOFMEMORY;
        return (DDHAL_DRIVER_HANDLED);
    }
    else
    {
        DBG_D3D((4,"Allocated Register context: 0x%x",pP2ctxt));

        // Record the register context in the window render context
        pContext->hPermediaContext = pP2ctxt;

    }

    // No texture at present
    pContext->CurrentTextureHandle = 0;

    // Initialize texture management for this context
    if (0 == P2TMcount)
    {
        if (D3D_OK != TextureCacheManagerInitialize(&P2TextureManager))
        {
            DBG_D3D((0,"ERROR: Couldn't initialize TextureCacheManager"));
            CleanDirect3DContext(pContext, pccd->dwhContext);
            pccd->ddrval = DDERR_OUTOFMEMORY;
            return (DDHAL_DRIVER_HANDLED);
        }
    }
    P2TMcount++;
    pContext->pTextureManager = &P2TextureManager;

    // Remember the local DD object and get the 
    // correct array of surfaces for this context
    pContext->pDDLcl = pccd->lpDDLcl;
    pContext->pHandleList = GetSurfaceHandleList(pccd->lpDDLcl);
    if (pContext->pHandleList == NULL)
    {
        DBG_D3D((0,"ERROR: Couldn't get a surface handle for lpDDLcl"));
        CleanDirect3DContext(pContext, pccd->dwhContext);
        pccd->ddrval = DDERR_OUTOFMEMORY;
        return (DDHAL_DRIVER_HANDLED);
    }

    DBG_D3D((4,"Getting pHandleList=%08lx for pDDLcl %08lx",
                                 pContext->pHandleList,pccd->dwPID));

    pContext->RenderSurfaceHandle = DDS_LCL(pccd->lpDDS)->lpSurfMore->dwSurfaceHandle;
    if (NULL != pccd->lpDDSZ) 
        pContext->ZBufferHandle = DDS_LCL(pccd->lpDDSZ)->lpSurfMore->dwSurfaceHandle;
    else
        pContext->ZBufferHandle = 0;
    // Now write the default setup to the chip.
    if (DD_OK != InitPermediaContext(pContext))
    {
        DBG_D3D((0,"ERROR: D3DContextCreate receives bad parameters "));
        CleanDirect3DContext(pContext, pccd->dwhContext);
        pccd->ddrval = D3DHAL_CONTEXT_BAD;
        return (DDHAL_DRIVER_HANDLED);
    }

    // ---------------- Setup default states in driver ------------------------

    // On context creation, no render states are overridden
    STATESET_INIT(pContext->overrides);

#if D3D_STATEBLOCKS
    // Default state block recording mode = no recording
    pContext->bStateRecMode = FALSE;
    pContext->pCurrSS = NULL;
    pContext->pIndexTableSS = NULL;
    pContext->dwMaxSSIndex = 0;
#endif //D3D_STATEBLOCKS

    pContext->Hdr.Flags = CTXT_HAS_GOURAUD_ENABLED ;
    pContext->CullMode = D3DCULL_CCW;

    // Set the last alpha value to 16 to force a new
    // send of the flat stipple patterns.
    pContext->LastAlpha = 16;

    pContext->bKeptStipple  = FALSE;  // By default, stippling is off
    pContext->bCanChromaKey = FALSE;  // Turn Chroma keying off by default
    pContext->LowerChromaColor = 0x0; // These are the default chromakey values
    pContext->UpperChromaColor = 0x0;

    pContext->FakeBlendNum = 0;       // No need to emulate any blend mode


    // Initialise the RenderCommand.  States will add to this
    pContext->RenderCommand = 0;
    RENDER_SUB_PIXEL_CORRECTION_ENABLE(pContext->RenderCommand);

    // Setup TSS defaults for stage 0
    pContext->TssStates[D3DTSS_TEXTUREMAP] = 0;
    pContext->TssStates[D3DTSS_COLOROP] = D3DTOP_MODULATE;
    pContext->TssStates[D3DTSS_ALPHAOP] = D3DTOP_SELECTARG1;
    pContext->TssStates[D3DTSS_COLORARG1] = D3DTA_TEXTURE;
    pContext->TssStates[D3DTSS_COLORARG2] = D3DTA_CURRENT;
    pContext->TssStates[D3DTSS_ALPHAARG1] = D3DTA_TEXTURE;
    pContext->TssStates[D3DTSS_ALPHAARG2] = D3DTA_CURRENT;
    pContext->TssStates[D3DTSS_TEXCOORDINDEX] = 0;
    pContext->TssStates[D3DTSS_ADDRESS] = D3DTADDRESS_WRAP;
    pContext->TssStates[D3DTSS_ADDRESSU] = D3DTADDRESS_WRAP;
    pContext->TssStates[D3DTSS_ADDRESSV] = D3DTADDRESS_WRAP;
    pContext->TssStates[D3DTSS_MAGFILTER] = D3DTFG_POINT;
    pContext->TssStates[D3DTSS_MINFILTER] = D3DTFN_POINT;
    pContext->TssStates[D3DTSS_MIPFILTER] = D3DTFP_NONE;

    pContext->TssStates[D3DTSS_BUMPENVMAT00] = 0;           // info we don't use
    pContext->TssStates[D3DTSS_BUMPENVMAT01] = 0;           // in this sample 
    pContext->TssStates[D3DTSS_BUMPENVMAT10] = 0;
    pContext->TssStates[D3DTSS_BUMPENVMAT11] = 0;
    pContext->TssStates[D3DTSS_BUMPENVLSCALE] = 0;
    pContext->TssStates[D3DTSS_BUMPENVLOFFSET] = 0;
    pContext->TssStates[D3DTSS_BORDERCOLOR] = 0x00000000;
    pContext->TssStates[D3DTSS_MAXMIPLEVEL] = 0;
    pContext->TssStates[D3DTSS_MAXANISOTROPY] = 1;

    // Force a change in texture before any 
    // rendering takes place for this context
    DIRTY_TEXTURE;

    DBG_D3D((6,"Exiting D3DContextCreate"));

    pccd->ddrval = DD_OK;
    return (DDHAL_DRIVER_HANDLED);
} // D3DContextCreate

//-----------------------------Public Routine----------------------------------
//
// DWORD D3DContextDestroy
//
// This callback is invoked when a Direct3D Device is being destroyed. As each 
// device is represented by a context ID, the driver is passed a context to 
// destroy. 
//
// The driver should free all resources it allocated to the context being 
// deleted. For example, the driver should free any texture resources it 
// associated with the context. The driver should not free the DirectDraw 
// surface(s) associated with the context because these will be freed by 
// DirectDraw in response to an application or Direct3D runtime request.
//
// Parameters
//     pcdd
//          Pointer to Context destroy information.
//
//          .dwhContext 
//               The ID of the context to be destroyed. 
//          .ddrval
//               Return code. DD_OK indicates success. 
//
// Return Value
//      Returns one of the following values: 
//                DDHAL_DRIVER_HANDLED   
//                DDHAL_DRIVER_NOTHANDLED    
//
//-----------------------------------------------------------------------------
DWORD CALLBACK 
D3DContextDestroy(LPD3DHAL_CONTEXTDESTROYDATA pcdd)
{
    PERMEDIA_D3DCONTEXT *pContext;

    // Deleting context
    DBG_D3D((6,"Entering D3DContextDestroy, context = %08lx",pcdd->dwhContext));

    pContext = (PERMEDIA_D3DCONTEXT *)ContextSlots[pcdd->dwhContext] ;

    if ( pContext != NULL && pContext->Hdr.MagicNo == RC_MAGIC_DISABLE)
        // render context has been deliberately disabled.
        // set the magic number back to valid to allow the cleanup
        // to proceed in the normal way.
        pContext->Hdr.MagicNo = RC_MAGIC_NO ;

    CHK_CONTEXT( pContext, pcdd->ddrval, "D3DContextDestroy");

    DBG_D3D((4,"Freeing context resources"));
    CleanDirect3DContext(pContext, pcdd->dwhContext);

    pcdd->ddrval = DD_OK;

    DBG_D3D((6,"Exiting D3DContextDestroy"));

    return (DDHAL_DRIVER_HANDLED);
} // D3DContextDestroy


//-----------------------------------------------------------------------------
//
// void __InitD3DTextureWithDDSurfInfo
//
//-----------------------------------------------------------------------------
void  
__InitD3DTextureWithDDSurfInfo(PPERMEDIA_D3DTEXTURE pTexture, 
                               LPDDRAWI_DDRAWSURFACE_LCL lpSurf, 
                               PPDev ppdev)
{
    DBG_D3D((10,"Entering lpSurf=%08lx %08lx",lpSurf,lpSurf->lpGbl->fpVidMem));
    
    pTexture->pTextureSurface = 
            (PermediaSurfaceData*)lpSurf->lpGbl->dwReserved1;

    if (NULL != pTexture->pTextureSurface)
    {
        pTexture->pTextureSurface->dwFlags |= P2_SURFACE_NEEDUPDATE;
        // need to recover this as CreateSurfaceEx may call us during TextureSwap()
        pTexture->dwPaletteHandle = pTexture->pTextureSurface->dwPaletteHandle;
    }
    // Need to remember the sizes and the log of the sizes of the maps
    pTexture->fpVidMem = lpSurf->lpGbl->fpVidMem;
    pTexture->lPitch = lpSurf->lpGbl->lPitch;
    pTexture->wWidth = (WORD)(lpSurf->lpGbl->wWidth);
    pTexture->wHeight = (WORD)(lpSurf->lpGbl->wHeight);
    pTexture->dwRGBBitCount=lpSurf->lpGbl->ddpfSurface.dwRGBBitCount;
    pTexture->m_dwBytes = pTexture->wHeight * pTexture->lPitch; 
    // Magic number for validity check
    pTexture->MagicNo = TC_MAGIC_NO;
    pTexture->dwFlags = lpSurf->dwFlags; 
    pTexture->dwCaps = lpSurf->ddsCaps.dwCaps;
    pTexture->dwCaps2= lpSurf->lpSurfMore->ddsCapsEx.dwCaps2;
    if (DDRAWISURF_HASCKEYSRCBLT & pTexture->dwFlags)
    {
         pTexture->dwKeyLow = lpSurf->ddckCKSrcBlt.dwColorSpaceLowValue;
         pTexture->dwKeyHigh = lpSurf->ddckCKSrcBlt.dwColorSpaceHighValue;
         DBG_D3D((4, "ColorKey exists (%08lx %08lx) on surface %d",
                     pTexture->dwKeyLow,pTexture->dwKeyHigh,
                     lpSurf->lpSurfMore->dwSurfaceHandle));
    }

    if (DD_P2AGPCAPABLE(ppdev) && pTexture->dwCaps & DDSCAPS_NONLOCALVIDMEM) 
    {
        pTexture->lSurfaceOffset = DD_AGPSURFBASEOFFSET(lpSurf->lpGbl);
    }

#if D3D_MIPMAPPING
    // Verify if texture has mip maps atteched
    if (lpSurf->ddsCaps.dwCaps & DDSCAPS_MIPMAP)
    {
        LPDDRAWI_DDRAWSURFACE_LCL lpNextSurf;
        int LOD;

        lpNextSurf = lpSurf;
        LOD = 0;

        pTexture->bMipMap = TRUE;

        // Calculate the number of mipmap levels (if this is a mipmap)
        pTexture->iMipLevels = (DWORD)((pTexture->wWidth > pTexture->wHeight) ?
                                                  log2((int)pTexture->wWidth) :
                                             log2((int)pTexture->wHeight)) + 1;

        // Walk the chain of surfaces and find all of the mipmap levels
        for (LOD = 0; LOD < pTexture->iMipLevels; LOD++)
        {
            DBG_D3D((4, "Loading texture LOD:%d, Ptr:0x%x",
                        LOD, lpNextSurf->lpGbl->fpVidMem));

            // Store the offsets for each of the mipmap levels
            StorePermediaLODLevel(ppdev, pTexture, lpNextSurf, LOD);

            // Is there another surface in the chain?
            if (lpNextSurf->lpAttachList)
            {
                lpNextSurf = lpNextSurf->lpAttachList->lpAttached;
                if (lpNextSurf == NULL)
                    break;
            }
            else 
                break;
        }

        // This isn't really a MipMap if LOD is 0
        if (LOD == 0)
        {
            DBG_D3D((4, "Texture was not a mipmap - only 1 level"));
            pTexture->bMipMap = FALSE;
            pTexture->iMipLevels = 1;
        }
        else
        {
            // Fill in the remaining levels with the smallest LOD
            // (this is for applications that haven't bothered to
            // pass us all of the LOD's).
            if (LOD < (pTexture->iMipLevels - 1))
            {
                int iLastLOD = LOD;

                DBG_D3D((4,"Filling in missing mipmaps!"));

                for (;LOD < MAX_MIP_LEVELS; LOD++)
                {
                    pTexture->MipLevels[LOD] = pTexture->MipLevels[iLastLOD];
                }
            }
        }
    }
    else 
#endif //D3D_MIPMAPPING
    {
        // NOT A MIPMAP, simply store away the offset of level 0
        pTexture->bMipMap = FALSE;
        pTexture->iMipLevels = 1;
        StorePermediaLODLevel(ppdev, pTexture, lpSurf, 0);
    }

    // If debugging show what has just been created
    DISPTEXTURE((ppdev, pTexture, &lpSurf->lpGbl->ddpfSurface));

    DBG_D3D((10,"Exiting __InitD3DTextureWithDDSurfInfo"));
} // __InitD3DTextureWithDDSurfInfo


//-----------------------------------------------------------------------------
// Direct3D HAL Table.
//
// This table contains all of the HAL calls that this driver supports in the 
// D3DHAL_Callbacks structure. These calls pertain to device context, scene 
// capture, execution, textures, transform, lighting, and pipeline state. 
// None of this is emulation code. The calls take the form of a return code 
// equal to: HalCall(HalCallData* lpData). All of the information in this 
// table will be implementation specific according to the specifications of 
// the hardware.
//
//-----------------------------------------------------------------------------

#define PermediaTriCaps {                                   \
    sizeof(D3DPRIMCAPS),                                    \
    D3DPMISCCAPS_CULLCCW    |        /* miscCaps */         \
    D3DPMISCCAPS_CULLCW     |                               \
    D3DPMISCCAPS_CULLNONE   |                               \
    D3DPMISCCAPS_MASKPLANES |                               \
    D3DPMISCCAPS_MASKZ      |                               \
    D3DPMISCCAPS_LINEPATTERNREP,                            \
    D3DPRASTERCAPS_DITHER    |          /* rasterCaps */    \
    D3DPRASTERCAPS_PAT       |                              \
    D3DPRASTERCAPS_SUBPIXEL  |                              \
    D3DPRASTERCAPS_ZTEST     |                              \
    D3DPRASTERCAPS_FOGVERTEX |                              \
    D3DPRASTERCAPS_STIPPLE,                                 \
    D3DPCMPCAPS_NEVER        |                              \
    D3DPCMPCAPS_LESS         |                              \
    D3DPCMPCAPS_EQUAL        |                              \
    D3DPCMPCAPS_LESSEQUAL    |                              \
    D3DPCMPCAPS_GREATER      |                              \
    D3DPCMPCAPS_NOTEQUAL     |                              \
    D3DPCMPCAPS_GREATEREQUAL |                              \
    D3DPCMPCAPS_ALWAYS       |                              \
    D3DPCMPCAPS_LESSEQUAL,           /* zCmpCaps */         \
    D3DPBLENDCAPS_SRCALPHA |         /* sourceBlendCaps */  \
    D3DPBLENDCAPS_ONE,                                      \
    D3DPBLENDCAPS_INVSRCALPHA |      /* destBlendCaps */    \
    D3DPBLENDCAPS_ZERO        |                             \
    D3DPBLENDCAPS_ONE,                                      \
    0,                               /* alphatestCaps */    \
    D3DPSHADECAPS_COLORFLATRGB|      /* shadeCaps */        \
    D3DPSHADECAPS_COLORGOURAUDRGB |                         \
    D3DPSHADECAPS_SPECULARFLATRGB |                         \
    D3DPSHADECAPS_SPECULARGOURAUDRGB |                      \
    D3DPSHADECAPS_FOGFLAT        |                          \
    D3DPSHADECAPS_FOGGOURAUD     |                          \
    D3DPSHADECAPS_ALPHAFLATBLEND |                          \
    D3DPSHADECAPS_ALPHAFLATSTIPPLED,                        \
    D3DPTEXTURECAPS_PERSPECTIVE |   /* textureCaps */       \
    D3DPTEXTURECAPS_ALPHA       |                           \
    D3DPTEXTURECAPS_POW2        |                           \
    D3DPTEXTURECAPS_TRANSPARENCY,                           \
    D3DPTFILTERCAPS_NEAREST |       /* textureFilterCaps*/  \
    D3DPTFILTERCAPS_LINEAR,                                 \
    D3DPTBLENDCAPS_DECAL         |  /* textureBlendCaps */  \
    D3DPTBLENDCAPS_DECALALPHA    |                          \
    D3DPTBLENDCAPS_MODULATE      |                          \
    D3DPTBLENDCAPS_MODULATEALPHA |                          \
    D3DPTBLENDCAPS_COPY,                                    \
    D3DPTADDRESSCAPS_WRAP   |       /* textureAddressCaps */\
    D3DPTADDRESSCAPS_MIRROR |                               \
    D3DPTADDRESSCAPS_CLAMP  |                               \
    D3DPTADDRESSCAPS_INDEPENDENTUV,                         \
    8,                              /* stippleWidth */      \
    8                               /* stippleHeight */     \
}          

static D3DDEVICEDESC_V1 PermediaCaps = {
    sizeof(D3DDEVICEDESC_V1),                       /* dwSize */
    D3DDD_COLORMODEL           |                    /* dwFlags */
    D3DDD_DEVCAPS              |
    D3DDD_TRICAPS              |
    D3DDD_LINECAPS             |
    D3DDD_DEVICERENDERBITDEPTH |
    D3DDD_DEVICEZBUFFERBITDEPTH,
    D3DCOLOR_RGB /*| D3DCOLOR_MONO*/,              /* dcmColorModel */
    D3DDEVCAPS_FLOATTLVERTEX |                     /* devCaps */
    D3DDEVCAPS_DRAWPRIMITIVES2 |
    D3DDEVCAPS_DRAWPRIMITIVES2EX    |
#if D3DDX7_TL
    D3DDEVCAPS_HWTRANSFORMANDLIGHT  |
#endif //D3DDX7_TL
    D3DDEVCAPS_SORTINCREASINGZ  |
    D3DDEVCAPS_SORTEXACT |
    D3DDEVCAPS_TLVERTEXSYSTEMMEMORY |
    D3DDEVCAPS_EXECUTESYSTEMMEMORY |
    D3DDEVCAPS_TEXTUREVIDEOMEMORY,
    { sizeof(D3DTRANSFORMCAPS), 
      0 },                                         /* transformCaps */
    FALSE,                                         /* bClipping */
    { sizeof(D3DLIGHTINGCAPS), 
      0 },                                         /* lightingCaps */
    PermediaTriCaps,                               /* lineCaps */
    PermediaTriCaps,                               /* triCaps */
    DDBD_16 | DDBD_32,                             /* dwDeviceRenderBitDepth */
    DDBD_16,                                       /* Z Bit depths */
    0,                                             /* dwMaxBufferSize */
    0                                              /* dwMaxVertexCount */
};

// Alpha Stipple patterns from Foley And Van Dam

DWORD FlatStipplePatterns[128] =
{
    //Pattern 0
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        
    // Pattern 1
    0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00,

    // Pattern 2
    0xAA, 0x00, 0x22, 0x00, 0xAA, 0x00, 0x22, 0x00,

    // Pattern 3
    0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,

    // Pattern 4
    0xAA, 0x44, 0xAA, 0x00, 0xAA, 0x44, 0xAA, 0x00,

    // Pattern 5
    0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11,

    // Pattern 6
    0xAA, 0x55, 0xAA, 0x11, 0xAA, 0x55, 0xAA, 0x11,

    // Pattern 7
    0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,

    // Pattern 8
    0xEE, 0x55, 0xAA, 0x55, 0xEE, 0x55, 0xAA, 0x55,

    // Pattern 9
    0xEE, 0x55, 0xBB, 0x55, 0xEE, 0x55, 0xBB, 0x55,

    // Pattern 10
    0xFF, 0x55, 0xBB, 0x55, 0xFF, 0x55, 0xBB, 0x55,

    // Pattern 11
    0xFF, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0xFF, 0x55,

    // Pattern 12
    0xFF, 0xdd, 0xFF, 0x55, 0xFF, 0xdd, 0xFF, 0x55,

    // Pattern 13
    0xFF, 0xdd, 0xFF, 0x77, 0xFF, 0xdd, 0xFF, 0x77,

    // Pattern 14
    0xFF, 0xFF, 0xFF, 0x77, 0xFF, 0xFF, 0xFF, 0x77,

    // Pattern 15
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};


//-----------------------------------------------------------------------------
// gD3DTextureFormats is a static structure which contains information 
// pertaining to pixel format, dimensions, bit depth, surface requirements, 
// overlays, and FOURCC codes of the supported texture formats.  These texture 
// formats will vary with the driver implementation according to the 
// capabilities of the hardware. 
//-----------------------------------------------------------------------------
DDSURFACEDESC gD3DTextureFormats [] = 
{
    // 5:5:5 RGB format
    {
    sizeof(DDSURFACEDESC),              // dwSize 
    DDSD_CAPS | DDSD_PIXELFORMAT,       // dwFlags 
    0,                                  // dwHeight 
    0,                                  // dwWidth 
    0,                                  // lPitch 
    0,                                  // dwBackBufferCount 
    0,                                  // dwZBufferBitDepth 
    0,                                  // dwAlphaBitDepth 
    0,                                  // dwReserved 
    NULL,                               // lpSurface 
    { 0, 0 },                           // ddckCKDestOverlay 
    { 0, 0 },                           // ddckCKDestBlt 
    { 0, 0 },                           // ddckCKSrcOverlay 
    { 0, 0 },                           // ddckCKSrcBlt 
    {
      sizeof(DDPIXELFORMAT),            // ddpfPixelFormat.dwSize 
      DDPF_RGB,                         // ddpfPixelFormat.dwFlags 
      0,                                // ddpfPixelFormat.dwFourCC
      16,                               // ddpfPixelFormat.dwRGBBitCount
      0x7c00,                           // ddpfPixelFormat.dwRBitMask
      0x03e0,                           // ddpfPixelFormat.dwGBitMask
      0x001f,                           // ddpfPixelFormat.dwBBitMask
      0                                 // ddpfPixelFormat.dwAlphaBitMask
    },
    DDSCAPS_TEXTURE,                    // ddscaps.dwCaps 
    },

    // 1:5:5:5 ARGB format
    {
    sizeof(DDSURFACEDESC),              // dwSize 
    DDSD_CAPS | DDSD_PIXELFORMAT,       // dwFlags 
    0,                                  // dwHeight 
    0,                                  // dwWidth 
    0,                                  // lPitch 
    0,                                  // dwBackBufferCount 
    0,                                  // dwZBufferBitDepth 
    0,                                  // dwAlphaBitDepth 
    0,                                  // dwReserved 
    NULL,                               // lpSurface 
    { 0, 0 },                           // ddckCKDestOverlay 
    { 0, 0 },                           // ddckCKDestBlt 
    { 0, 0 },                           // ddckCKSrcOverlay 
    { 0, 0 },                           // ddckCKSrcBlt 
    {
      sizeof(DDPIXELFORMAT),            // ddpfPixelFormat.dwSize 
      DDPF_RGB | DDPF_ALPHAPIXELS,      // ddpfPixelFormat.dwFlags 
      0,                                // ddpfPixelFormat.dwFourCC
      16,                               // ddpfPixelFormat.dwRGBBitCount
      0x7c00,                           // ddpfPixelFormat.dwRBitMask
      0x03e0,                           // ddpfPixelFormat.dwGBitMask
      0x001f,                           // ddpfPixelFormat.dwBBitMask
      0x8000                            // ddpfPixelFormat.dwAlphaBitMask
    },
    DDSCAPS_TEXTURE,                    // ddscaps.dwCaps 
    },

    // 5:6:5 RGB format
    {
    sizeof(DDSURFACEDESC),              // dwSize 
    DDSD_CAPS | DDSD_PIXELFORMAT,       // dwFlags 
    0,                                  // dwHeight 
    0,                                  // dwWidth 
    0,                                  // lPitch 
    0,                                  // dwBackBufferCount 
    0,                                  // dwZBufferBitDepth 
    0,                                  // dwAlphaBitDepth 
    0,                                  // dwReserved 
    NULL,                               // lpSurface 
    { 0, 0 },                           // ddckCKDestOverlay 
    { 0, 0 },                           // ddckCKDestBlt 
    { 0, 0 },                           // ddckCKSrcOverlay 
    { 0, 0 },                           // ddckCKSrcBlt 
    {
      sizeof(DDPIXELFORMAT),            // ddpfPixelFormat.dwSize 
      DDPF_RGB,                         // ddpfPixelFormat.dwFlags 
      0,                                // ddpfPixelFormat.dwFourCC
      16,                               // ddpfPixelFormat.dwRGBBitCount
      0xf800,                           // ddpfPixelFormat.dwRBitMask
      0x07e0,                           // ddpfPixelFormat.dwGBitMask
      0x001f,                           // ddpfPixelFormat.dwBBitMask
      0                                 // ddpfPixelFormat.dwAlphaBitMask
    },
    DDSCAPS_TEXTURE,                    // ddscaps.dwCaps 
    },

    // 4:4:4:4 ARGB format
    {
    sizeof(DDSURFACEDESC),              // dwSize 
    DDSD_CAPS | DDSD_PIXELFORMAT,       // dwFlags 
    0,                                  // dwHeight 
    0,                                  // dwWidth 
    0,                                  // lPitch 
    0,                                  // dwBackBufferCount 
    0,                                  // dwZBufferBitDepth 
    0,                                  // dwAlphaBitDepth 
    0,                                  // dwReserved 
    NULL,                               // lpSurface 
    { 0, 0 },                           // ddckCKDestOverlay 
    { 0, 0 },                           // ddckCKDestBlt 
    { 0, 0 },                           // ddckCKSrcOverlay 
    { 0, 0 },                           // ddckCKSrcBlt 
    {
      sizeof(DDPIXELFORMAT),            // ddpfPixelFormat.dwSize 
      DDPF_RGB | DDPF_ALPHAPIXELS,      // ddpfPixelFormat.dwFlags 
      0,                                // ddpfPixelFormat.dwFourCC
      16,                               // ddpfPixelFormat.dwRGBBitCount
      0x0f00,                           // ddpfPixelFormat.dwRBitMask
      0x00f0,                           // ddpfPixelFormat.dwGBitMask
      0x000f,                           // ddpfPixelFormat.dwBBitMask
      0xf000                            // ddpfPixelFormat.dwAlphaBitMask
    },
    DDSCAPS_TEXTURE,                    // ddscaps.dwCaps 
    },

    // 8:8:8 RGB format
    {
    sizeof(DDSURFACEDESC),              // dwSize 
    DDSD_CAPS | DDSD_PIXELFORMAT,       // dwFlags 
    0,                                  // dwHeight 
    0,                                  // dwWidth 
    0,                                  // lPitch 
    0,                                  // dwBackBufferCount 
    0,                                  // dwZBufferBitDepth 
    0,                                  // dwAlphaBitDepth 
    0,                                  // dwReserved 
    NULL,                               // lpSurface 
    { 0, 0 },                           // ddckCKDestOverlay 
    { 0, 0 },                           // ddckCKDestBlt 
    { 0, 0 },                           // ddckCKSrcOverlay 
    { 0, 0 },                           // ddckCKSrcBlt 
    {
      sizeof(DDPIXELFORMAT),            // ddpfPixelFormat.dwSize 
      DDPF_RGB,                         // ddpfPixelFormat.dwFlags 
      0,                                // ddpfPixelFormat.dwFourCC
      32,                               // ddpfPixelFormat.dwRGBBitCount
      0x00ff0000,                       // ddpfPixelFormat.dwRBitMask
      0x0000ff00,                       // ddpfPixelFormat.dwGBitMask
      0x000000ff,                       // ddpfPixelFormat.dwBBitMask
      0                                 // ddpfPixelFormat.dwAlphaBitMask
    },
    DDSCAPS_TEXTURE,                    // ddscaps.dwCaps 
    },

    // 8:8:8:8 ARGB format
    {
    sizeof(DDSURFACEDESC),              // dwSize 
    DDSD_CAPS | DDSD_PIXELFORMAT,       // dwFlags 
    0,                                  // dwHeight 
    0,                                  // dwWidth 
    0,                                  // lPitch 
    0,                                  // dwBackBufferCount 
    0,                                  // dwZBufferBitDepth 
    0,                                  // dwAlphaBitDepth 
    0,                                  // dwReserved 
    NULL,                               // lpSurface 
    { 0, 0 },                           // ddckCKDestOverlay 
    { 0, 0 },                           // ddckCKDestBlt 
    { 0, 0 },                           // ddckCKSrcOverlay 
    { 0, 0 },                           // ddckCKSrcBlt 
    {
      sizeof(DDPIXELFORMAT),            // ddpfPixelFormat.dwSize 
      DDPF_RGB | DDPF_ALPHAPIXELS,      // ddpfPixelFormat.dwFlags 
      0,                                // ddpfPixelFormat.dwFourCC
      32,                               // ddpfPixelFormat.dwRGBBitCount
      0x00ff0000,                       // ddpfPixelFormat.dwRBitMask
      0x0000ff00,                       // ddpfPixelFormat.dwGBitMask
      0x000000ff,                       // ddpfPixelFormat.dwBBitMask
      0xff000000                        // ddpfPixelFormat.dwAlphaBitMask
    },
    DDSCAPS_TEXTURE,                    // ddscaps.dwCaps 
    },

    // 4 bit palettized format
    {
    sizeof(DDSURFACEDESC),              // dwSize 
    DDSD_CAPS | DDSD_PIXELFORMAT,       // dwFlags 
    0,                                  // dwHeight 
    0,                                  // dwWidth 
    0,                                  // lPitch 
    0,                                  // dwBackBufferCount 
    0,                                  // dwZBufferBitDepth 
    0,                                  // dwAlphaBitDepth 
    0,                                  // dwReserved 
    NULL,                               // lpSurface 
    { 0, 0 },                           // ddckCKDestOverlay 
    { 0, 0 },                           // ddckCKDestBlt 
    { 0, 0 },                           // ddckCKSrcOverlay 
    { 0, 0 },                           // ddckCKSrcBlt 
    {
      sizeof(DDPIXELFORMAT),            // ddpfPixelFormat.dwSize 
      DDPF_RGB | DDPF_PALETTEINDEXED4,  // ddpfPixelFormat.dwFlags 
      0,                                // ddpfPixelFormat.dwFourCC
      4,                                // ddpfPixelFormat.dwRGBBitCount
      0x00,                             // ddpfPixelFormat.dwRBitMask
      0x00,                             // ddpfPixelFormat.dwGBitMask
      0x00,                             // ddpfPixelFormat.dwBBitMask
      0x00                              // ddpfPixelFormat.dwAlphaBitMask
    },
    DDSCAPS_TEXTURE,                    // ddscaps.dwCaps
    },

    // 8 bit palettized format
    {
    sizeof(DDSURFACEDESC),              // dwSize
    DDSD_CAPS | DDSD_PIXELFORMAT,       // dwFlags
    0,                                  // dwHeight
    0,                                  // dwWidth
    0,                                  // lPitch
    0,                                  // dwBackBufferCount
    0,                                  // dwZBufferBitDepth
    0,                                  // dwAlphaBitDepth
    0,                                  // dwReserved
    NULL,                               // lpSurface
    { 0, 0 },                           // ddckCKDestOverlay
    { 0, 0 },                           // ddckCKDestBlt
    { 0, 0 },                           // ddckCKSrcOverlay
    { 0, 0 },                           // ddckCKSrcBlt
    {
      sizeof(DDPIXELFORMAT),            // ddpfPixelFormat.dwSize
      DDPF_RGB | DDPF_PALETTEINDEXED8,  // ddpfPixelFormat.dwFlags
      0,                                // ddpfPixelFormat.dwFourCC
      8,                                // ddpfPixelFormat.dwRGBBitCount
      0x00,                             // ddpfPixelFormat.dwRBitMask
      0x00,                             // ddpfPixelFormat.dwGBitMask
      0x00,                             // ddpfPixelFormat.dwBBitMask
      0x00                              // ddpfPixelFormat.dwAlphaBitMask
    },
    DDSCAPS_TEXTURE,                    // ddscaps.dwCaps
    },

};

ULONG gD3DNumberOfTextureFormats = 
                        sizeof(gD3DTextureFormats) / sizeof(DDSURFACEDESC);

//------------------------------------------------------------------------------
// D3D working structures for callbacks and global data
//------------------------------------------------------------------------------

// D3D callbacks and global data
D3DHAL_GLOBALDRIVERDATA gD3DGlobalDriverData;
D3DHAL_CALLBACKS        gD3DCallBacks;

// D3D contexts table
// each entry points to a valid PERMEDIA_D3DCONTEXT structure
UINT_PTR ContextSlots[MAX_CONTEXT_NUM] = {0};

// Handles table
// each entry is a DWLIST structure (*dwSurfaceList,*dwPaletteList;pDDLcl)
DWLIST  HandleList[MAX_CONTEXT_NUM] = {0}; 

//-----------------------------------------------------------------------------
//
// void D3DHALCreateDriver
//
// The main D3D Callback.  
//      Clears contexts 
//      Fills in entry points to D3D driver.  
//      Generates texture formats.
//
//-----------------------------------------------------------------------------
void CALLBACK 
D3DHALCreateDriver(PPDev ppdev, 
                   LPD3DHAL_GLOBALDRIVERDATA* lpD3DGlobalDriverData,
                   LPD3DHAL_CALLBACKS* lpD3DHALCallbacks,
                   LPDDHAL_D3DBUFCALLBACKS* lpDDExeBufCallbacks)
{
    D3DHAL_GLOBALDRIVERDATA deviceD3DGlobal;
    D3DHAL_CALLBACKS deviceD3DHALCallbacks;

    DBG_D3D((6,"Entering D3DHALCreateDriver"));

    // Contexts are cleared out. It is allright to use the D3DInitialised BOOL,
    // because it is global, and therefore forced into shared data segment by
    // the build.
    if (D3DInitialised == FALSE)
    {
        // Clear the contexts.
        memset(ContextSlots, 0, (sizeof(ContextSlots[0]) * MAX_CONTEXT_NUM) );
        memset(HandleList, 0, (sizeof(HandleList[0]) * MAX_CONTEXT_NUM) );

        D3DInitialised = TRUE;
    }

    // Here we fill in the supplied structures.
    // Can disable D3D HAL in registry if we are in the wrong mode
    if (ppdev->iBitmapFormat == BMF_8BPP )
    {
        *lpD3DGlobalDriverData = NULL;
        *lpD3DHALCallbacks = NULL;
        *lpDDExeBufCallbacks = NULL;
        DBG_D3D((0, "D3DHALCreateDriver: Disabled"));
        return;
    }


    // Set the pointers for D3D global data
    ppdev->pD3DDriverData32 = (UINT_PTR)&gD3DGlobalDriverData;
    ppdev->pD3DHALCallbacks32 = (UINT_PTR)&gD3DCallBacks;

    // Clear the global data
    memset(&deviceD3DGlobal, 0, sizeof(D3DHAL_GLOBALDRIVERDATA));
    deviceD3DGlobal.dwSize = sizeof(D3DHAL_GLOBALDRIVERDATA);
    
    // Clear the call-backs
    memset(&deviceD3DHALCallbacks, 0, sizeof(D3DHAL_CALLBACKS));
    deviceD3DHALCallbacks.dwSize = sizeof(D3DHAL_CALLBACKS);

    deviceD3DGlobal.dwNumVertices = 0;        // We don't parse execute buffers
    deviceD3DGlobal.dwNumClipVertices = 0;

#if D3D_MIPMAPPING
    // Add mipmapping cap bits to our texturing capabilities
    PermediaCaps.dpcTriCaps.dwTextureFilterCaps |= 
                                D3DPTFILTERCAPS_MIPNEAREST |
                                D3DPTFILTERCAPS_MIPLINEAR |
                                D3DPTFILTERCAPS_LINEARMIPNEAREST |
                                D3DPTFILTERCAPS_LINEARMIPLINEAR;

    PermediaCaps.dpcTriCaps.dwRasterCaps |= D3DPRASTERCAPS_MIPMAPLODBIAS;
#endif

    // Can do packed 24 bit on P2.
    PermediaCaps.dwDeviceRenderBitDepth |= DDBD_24;
    if (DD_P2AGPCAPABLE(ppdev))
        PermediaCaps.dwDevCaps |= D3DDEVCAPS_TEXTURENONLOCALVIDMEM;
    PermediaCaps.dwDevCaps |= D3DDEVCAPS_DRAWPRIMTLVERTEX;

    deviceD3DGlobal.hwCaps = PermediaCaps;
    deviceD3DGlobal.dwNumTextureFormats = gD3DNumberOfTextureFormats;
    deviceD3DGlobal.lpTextureFormats = &gD3DTextureFormats[0];

    // D3D Context callbacks
    deviceD3DHALCallbacks.ContextCreate = D3DContextCreate;
    deviceD3DHALCallbacks.ContextDestroy = D3DContextDestroy;

    //
    // Return the HAL table.
    //

    memcpy(&gD3DGlobalDriverData, &deviceD3DGlobal, sizeof(D3DHAL_GLOBALDRIVERDATA));
    memcpy(&gD3DCallBacks, &deviceD3DHALCallbacks, sizeof(D3DHAL_CALLBACKS));

    *lpD3DGlobalDriverData = &gD3DGlobalDriverData;
    *lpD3DHALCallbacks = &gD3DCallBacks;
    *lpDDExeBufCallbacks = NULL;

    DBG_D3D((6,"Exiting D3DHALCreateDriver"));

    return;
} // D3DHALCreateDriver

//-----------------------------------------------------------------------------
//
// void CleanDirect3DContext
//
// After it has been decided that a context is indeed still active
// and is being freed, this function walks along cleaning everything
// up.  Note it can be called either as a result of a D3DContextDestroy,
// or as a result of the app exiting without freeing the context, or
// as the result of an error whilst creating the context.
//
//-----------------------------------------------------------------------------
void 
CleanDirect3DContext(PERMEDIA_D3DCONTEXT* pContext, ULONG_PTR dwhContext)
{
    PERMEDIA_D3DTEXTURE* pTexture;
    DWORD dwSlotNum = 1;
    PPDev ppdev = pContext->ppdev;

    DBG_D3D((10,"Entering CleanDirect3DContext"));

    // free up Permedia register context id (resources)
    if (pContext->hPermediaContext)
    {
        P2FreeContext( ppdev, pContext->hPermediaContext);
    }

    // clean up texture manager stuff it is already allocated for this context
    if (pContext->pTextureManager)
    {
        pContext->pTextureManager = NULL;
        P2TMcount--;
        if (0 == P2TMcount)
        {
            if (0 != P2TextureManager.m_heap.m_data_p)
            {
                TextureCacheManagerEvictTextures(&P2TextureManager);
                ENGFREEMEM(P2TextureManager.m_heap.m_data_p);
                P2TextureManager.m_heap.m_data_p=NULL;
            }
        }
    }

#if D3D_STATEBLOCKS
    // Free up any remaining state sets
    __DeleteAllStateSets(pContext);
#endif //D3D_STATEBLOCKS

    // Finally, free up the rendering context structure itself
    ENGFREEMEM((PVOID)pContext->Hdr.pSelf);

    // Mark the context as now empty!
    ContextSlots[dwhContext] = 0;

    DBG_D3D((10,"Exiting CleanDirect3DContext, Context 0x%x deleted.",
                                                            dwhContext));

} // CleanDirect3DContext

//-----------------------------------------------------------------------------
//
// HRESULT InitPermediaContext
//
// Given a valid context, this sets up the rest of the chip, and
// enables the relevent units.  There is a software copy of most things.
//
//-----------------------------------------------------------------------------
HRESULT 
InitPermediaContext(PERMEDIA_D3DCONTEXT* pContext)
{
    PPDev ppdev = pContext->ppdev;

    DBG_D3D((10,"Entering InitPermediaContext"));

    SET_CURRENT_D3D_CONTEXT(pContext->hPermediaContext);

    // Initially turn off all.units
    __PermediaDisableUnits(pContext);

    // Setup initial state of Permedia 2 registers for this D3D context
    SetupDefaultsPermediaContext(pContext);

    DBG_D3D((10,"Exiting InitPermediaContext"));
    // Setup the correct surface (render & depth buffer) characteristics
    return SetupPermediaRenderTarget(pContext);

} // InitPermediaContext

//-----------------------------------------------------------------------------
//
// BOOL: SetupDefaultsPermediaContext
//
// Sets up the Permedia HW context(chip.registers) according to some D3D and
// some HW specific defaults. Done only when initializing the context
//
//-----------------------------------------------------------------------------
BOOL 
SetupDefaultsPermediaContext(PERMEDIA_D3DCONTEXT* pContext)
{
    __P2RegsSoftwareCopy* pSoftPermedia = &pContext->Hdr.SoftCopyP2Regs;
    PERMEDIA_DEFS(pContext->ppdev);

    DBG_D3D((10,"Entering SetupDefaultsPermediaContext"));

    //=========================================================================
    // Initialize our software copy of some registers for their default values 
    //=========================================================================

    // Setup the default & constant ( Z Buffer) LB settings
    //  this will be updated into the chip in SetupPermediaRenderTarget
    pSoftPermedia->LBReadMode.WindowOrigin = __PERMEDIA_TOP_LEFT_WINDOW_ORIGIN;
    pSoftPermedia->LBReadMode.DataType = __PERMEDIA_LBDEFAULT;     // default
    pSoftPermedia->LBReadMode.ReadSourceEnable = __PERMEDIA_DISABLE;
    pSoftPermedia->LBReadMode.ReadDestinationEnable = __PERMEDIA_DISABLE;
    pSoftPermedia->LBReadMode.PatchMode = 0;

    // Setup the default & constant FB settings
    //  this will be updated into the chip in SetupPermediaRenderTarget
    pSoftPermedia->FBReadMode.ReadSourceEnable = __PERMEDIA_DISABLE;
    pSoftPermedia->FBReadMode.ReadDestinationEnable = __PERMEDIA_DISABLE;
    pSoftPermedia->FBReadMode.DataType = __PERMEDIA_FBDATA;
                                                    // Top Left for D3D origin
    pSoftPermedia->FBReadMode.WindowOrigin = __PERMEDIA_TOP_LEFT_WINDOW_ORIGIN;
    pSoftPermedia->FBReadMode.PatchMode = 0;
    pSoftPermedia->FBReadMode.PackedData = 0;
    pSoftPermedia->FBReadMode.RelativeOffset = 0;

    // Setup the default & constant Alpha Blend Mode settings
    //  this will be updated into the chip in SetupPermediaRenderTarget
    pSoftPermedia->AlphaBlendMode.AlphaBlendEnable = 0;
    pSoftPermedia->AlphaBlendMode.SourceBlend = __PERMEDIA_BLEND_FUNC_ONE;
    pSoftPermedia->AlphaBlendMode.DestinationBlend = __PERMEDIA_BLEND_FUNC_ZERO;
    pSoftPermedia->AlphaBlendMode.NoAlphaBuffer = 0;
    pSoftPermedia->AlphaBlendMode.ColorOrder = COLOR_MODE;
    pSoftPermedia->AlphaBlendMode.BlendType = 0;
    pSoftPermedia->AlphaBlendMode.ColorConversion = 1;
    pSoftPermedia->AlphaBlendMode.AlphaConversion = 1;

    // Setup the default & constant  Dither Mode settings
    //  this will be updated into the chip in SetupPermediaRenderTarget
    pSoftPermedia->DitherMode.ColorOrder = COLOR_MODE;
    pSoftPermedia->DitherMode.XOffset = DITHER_XOFFSET;
    pSoftPermedia->DitherMode.YOffset = DITHER_YOFFSET;
    pSoftPermedia->DitherMode.UnitEnable = __PERMEDIA_ENABLE;
    pSoftPermedia->DitherMode.ForceAlpha = 0;

    //=========================================================================
    //  Find out info for memory widths
    //=========================================================================

    PPDev ppdev = pContext->ppdev;

    DBG_D3D((4, "ScreenWidth %d, ScreenHeight %d, Bytes/Pixel %d",
                ppdev->cxScreen, ppdev->cyScreen, 
                ppdev->ddpfDisplay.dwRGBBitCount >> 3));

    vCalcPackedPP( ppdev->cxMemory, NULL, &pContext->ulPackedPP);
    DBG_D3D((4, "PackedPP = %04x", pContext->ulPackedPP));

    //=========================================================================
    // Initialize hardware registers to their default values 
    //=========================================================================

    // Number of registers we are going to set up
    RESERVEDMAPTR(34);

    // ----------------- Render and Depth Buffer setup ----------------------

    // Setup default offset of render buffer in video memory
    SEND_PERMEDIA_DATA(FBWindowBase, 0x0);

    // Setup  offset from destination to source for copy operations
    SEND_PERMEDIA_DATA(FBSourceOffset, 0x0);

    // Render buffer Write Mode setup
    pSoftPermedia->FBWriteMode.UnitEnable = __PERMEDIA_ENABLE;
    COPY_PERMEDIA_DATA(FBWriteMode, pSoftPermedia->FBWriteMode);

    // Render buffer Write Masks (write to all bits in the pixel)
    SEND_PERMEDIA_DATA(FBSoftwareWriteMask, __PERMEDIA_ALL_WRITEMASKS_SET);
    SEND_PERMEDIA_DATA(FBHardwareWriteMask, __PERMEDIA_ALL_WRITEMASKS_SET);

    // Set block fill colour to black
    SEND_PERMEDIA_DATA(FBBlockColor, 0x0);

    // Set window origin offsets to (0,0)
    SEND_PERMEDIA_DATA(WindowOrigin, 0x0);

    // WindowSetup
    pSoftPermedia->Window.ForceLBUpdate = 0;
    pSoftPermedia->Window.LBUpdateSource = 0;
    pSoftPermedia->Window.DisableLBUpdate = 0;
    COPY_PERMEDIA_DATA(Window, pSoftPermedia->Window);

    // Disable Screen Scissor unit
    SEND_PERMEDIA_DATA(ScissorMode, __PERMEDIA_DISABLE);

    // Depth Buffer offset
    SEND_PERMEDIA_DATA(LBSourceOffset, 0);

    // Depth Buffer Write mode (initially allow LB Writes)
    pSoftPermedia->LBWriteMode.WriteEnable = __PERMEDIA_DISABLE;
    COPY_PERMEDIA_DATA(LBWriteMode, pSoftPermedia->LBWriteMode);

    // Depth comparisons
    pSoftPermedia->DepthMode.WriteMask = __PERMEDIA_ENABLE;
    pSoftPermedia->DepthMode.CompareMode =
                                __PERMEDIA_DEPTH_COMPARE_MODE_LESS_OR_EQUAL;
    pSoftPermedia->DepthMode.NewDepthSource = __PERMEDIA_DEPTH_SOURCE_DDA;
    pSoftPermedia->DepthMode.UnitEnable = __PERMEDIA_DISABLE;
    COPY_PERMEDIA_DATA(DepthMode, pSoftPermedia->DepthMode);


    // ----------------- Texture units setup -----------------------------

    // Enable texture address unit, disable perspective correction
    pSoftPermedia->TextureAddressMode.Enable = 1;
    pSoftPermedia->TextureAddressMode.PerspectiveCorrection = 0;
    pSoftPermedia->TextureAddressMode.DeltaFormat = 0;
    COPY_PERMEDIA_DATA(TextureAddressMode, pSoftPermedia->TextureAddressMode);

    // Enable texture color mode unit, set modulation blending, no specular
    // as defaults
    pSoftPermedia->TextureColorMode.TextureEnable = 1;
    pSoftPermedia->TextureColorMode.ApplicationMode = _P2_TEXTURE_MODULATE;
    pSoftPermedia->TextureColorMode.TextureType = 0;
    pSoftPermedia->TextureColorMode.KdDDA = 0;
    pSoftPermedia->TextureColorMode.KsDDA = 0;
    COPY_PERMEDIA_DATA(TextureColorMode, pSoftPermedia->TextureColorMode);

    // Enable texture mapping unit, set frame buffer size as default texture
    // map size (to be oevrriden in EnableTexturePermedia)
    pSoftPermedia->TextureMapFormat.PackedPP = pContext->ulPackedPP;
    pSoftPermedia->TextureMapFormat.WindowOrigin =
                                __PERMEDIA_TOP_LEFT_WINDOW_ORIGIN; //top left
    pSoftPermedia->TextureMapFormat.SubPatchMode = 0;
    pSoftPermedia->TextureMapFormat.TexelSize = 1;
    COPY_PERMEDIA_DATA(TextureMapFormat, pSoftPermedia->TextureMapFormat);

    // Setup Textura data format (to be oevrriden in EnableTexturePermedia)
    pSoftPermedia->TextureDataFormat.TextureFormat = 1;
    pSoftPermedia->TextureDataFormat.NoAlphaBuffer = 1;
    pSoftPermedia->TextureDataFormat.ColorOrder = COLOR_MODE;
    COPY_PERMEDIA_DATA(TextureDataFormat, pSoftPermedia->TextureDataFormat);

    // Setup default texture map base address (in video memory)
    SEND_PERMEDIA_DATA(TextureBaseAddress, 0);

    // Setup texture reading defaults: Repeat s,t wrapping, 256x256 texture
    // no texture filtering set up.
    pSoftPermedia->TextureReadMode.PackedData = 0;
    pSoftPermedia->TextureReadMode.FilterMode = 0;
    pSoftPermedia->TextureReadMode.Height = 8;
    pSoftPermedia->TextureReadMode.Width = 8;
    pSoftPermedia->TextureReadMode.pad1 = 0;
    pSoftPermedia->TextureReadMode.pad2 = 0;
    pSoftPermedia->TextureReadMode.TWrapMode = _P2_TEXTURE_REPEAT;
    pSoftPermedia->TextureReadMode.SWrapMode = _P2_TEXTURE_REPEAT;
    pSoftPermedia->TextureReadMode.Enable = 1;
    COPY_PERMEDIA_DATA(TextureReadMode, pSoftPermedia->TextureReadMode);

    // Disable Texture LUT unit for palettized textures
    SEND_PERMEDIA_DATA(TexelLUTMode, __PERMEDIA_DISABLE);

    // -------------- Other rendering units setup ----------------

    // Setup defaults of YUV units used for chromakey testing
    pSoftPermedia->YUVMode.Enable = __PERMEDIA_DISABLE;
    pSoftPermedia->YUVMode.TestMode = PM_YUVMODE_CHROMATEST_DISABLE;
    pSoftPermedia->YUVMode.TestData = PM_YUVMODE_TESTDATA_INPUT;
    pSoftPermedia->YUVMode.RejectTexel = FALSE;
    pSoftPermedia->YUVMode.TexelDisableUpdate = FALSE;
    COPY_PERMEDIA_DATA(YUVMode, pSoftPermedia->YUVMode);

    // Chromakey values initially black
    SEND_PERMEDIA_DATA(ChromaUpperBound, 0x00000000);
    SEND_PERMEDIA_DATA(ChromaLowerBound, 0x00000000);

    SEND_PERMEDIA_DATA(AlphaMapUpperBound, 0xFFFFFFFF);
    SEND_PERMEDIA_DATA(AlphaMapLowerBound, 0x11000000);

    // Default Fog color is white
    pSoftPermedia->FogColor = 0xFFFFFFFF;
    SEND_PERMEDIA_DATA(FogColor, pSoftPermedia->FogColor);

    // Fog setup
    pSoftPermedia->FogMode.FogEnable = 1;
    COPY_PERMEDIA_DATA(FogMode, pSoftPermedia->FogMode);

    // Stencil mode setup
    pSoftPermedia->StencilMode.DPFail = __PERMEDIA_STENCIL_METHOD_KEEP;
    pSoftPermedia->StencilMode.DPPass = __PERMEDIA_STENCIL_METHOD_KEEP;
    pSoftPermedia->StencilMode.UnitEnable = __PERMEDIA_DISABLE;
    pSoftPermedia->StencilMode.StencilSource =
                                        __PERMEDIA_STENCIL_SOURCE_TEST_LOGIC;
    COPY_PERMEDIA_DATA(StencilMode, pSoftPermedia->StencilMode);

    // Host out unit , disable read backs
    SEND_PERMEDIA_DATA(FilterMode, __PERMEDIA_DISABLE);

    // Disable statistics unit
    SEND_PERMEDIA_DATA(StatisticMode, __PERMEDIA_DISABLE);


    // ----------------- Rasterization setup -----------------------------

    // Setup Rasterizer units defaults
    SEND_PERMEDIA_DATA(RasterizerMode, 0);

    // Setup a step of -1, as this doesn't change very much
    SEND_PERMEDIA_DATA(dY, 0xFFFF0000);

    // Setup for Gourand shaded colour model, and enable unit
    pContext->Hdr.SoftCopyP2Regs.ColorDDAMode.UnitEnable = 1;
    pContext->Hdr.SoftCopyP2Regs.ColorDDAMode.ShadeMode = 1;
    COPY_PERMEDIA_DATA(ColorDDAMode, pContext->Hdr.SoftCopyP2Regs.ColorDDAMode);

    // Disable stippling unit
    SEND_PERMEDIA_DATA(AreaStippleMode, 0x0); //AZN

    // Setup the Delta setup chip for rasterization
    pSoftPermedia->DeltaMode.TargetChip = 2;
    pSoftPermedia->DeltaMode.SpecularTextureEnable = 0;
    // The below changes to normalize in the perspective case
    // It must not be on in the non-perspective case as the bad Q's will
    // get used in the normalisation.
    pSoftPermedia->DeltaMode.TextureParameterMode = 1;
    pSoftPermedia->DeltaMode.TextureEnable = 1;
    pSoftPermedia->DeltaMode.DiffuseTextureEnable = 0;

    pSoftPermedia->DeltaMode.FogEnable = 1;
    pSoftPermedia->DeltaMode.SmoothShadingEnable = 1;
    pSoftPermedia->DeltaMode.DepthEnable = 0;
    pSoftPermedia->DeltaMode.SubPixelCorrectionEnable = 1;
    pSoftPermedia->DeltaMode.DiamondExit = 1;
    pSoftPermedia->DeltaMode.NoDraw = 0;
    pSoftPermedia->DeltaMode.ClampEnable = 0;
    pSoftPermedia->DeltaMode.FillDirection = 0;
#ifndef P2_CHIP_CULLING
    pSoftPermedia->DeltaMode.BackfaceCull = 0;
#else
    pSoftPermedia->DeltaMode.BackfaceCull = 1;
#endif
    pSoftPermedia->DeltaMode.ColorOrder = COLOR_MODE;
    COPY_PERMEDIA_DATA(DeltaMode, pSoftPermedia->DeltaMode);

    // Send all this data to Permedia2
    COMMITDMAPTR();
    FLUSHDMA();

    DBG_D3D((10,"Exiting SetupDefaultsPermediaContext"));

    return TRUE;
} // SetupDefaultsPermediaContext

//-----------------------------------------------------------------------------
//
// void SetupPermediaRenderTarget
//
// Sets up the correct surface characteristics (format, stride, etc) of the 
// render buffer and the depth buffer in the Permedia registers
//
//-----------------------------------------------------------------------------
HRESULT 
SetupPermediaRenderTarget(PERMEDIA_D3DCONTEXT* pContext)
{
    __P2RegsSoftwareCopy*   pSoftPermedia = &pContext->Hdr.SoftCopyP2Regs;
    PPDev                   ppdev = pContext->ppdev;
    PPERMEDIA_D3DTEXTURE    pSurfRender,pSurfZBuffer;
    PermediaSurfaceData*    pPrivateRender;
    PERMEDIA_DEFS(pContext->ppdev);
    
    DBG_D3D((10,"Entering SetupPermediaRenderTarget"));

    pSurfRender = 
        TextureHandleToPtr(pContext->RenderSurfaceHandle, pContext);

    if (!CHECK_D3DSURFACE_VALIDITY(pSurfRender)) 
    {
        DBG_D3D((0,"ERROR: SetupPermediaRenderTarget"
            " Invalid pSurfRender handle=%08lx",
            pContext->RenderSurfaceHandle));
        return DDERR_INVALIDPARAMS;
    }

    if (DDSCAPS_SYSTEMMEMORY & pSurfRender->dwCaps)
    {
        DBG_D3D((0, "ERROR: SetupPermediaRenderTarget"
            " Render Surface in SYSTEMMEMORY handle=%08lx",
            pContext->RenderSurfaceHandle));
        return DDERR_INVALIDPARAMS;    
    }
    pPrivateRender=pSurfRender->pTextureSurface;
    if (!CHECK_P2_SURFACEDATA_VALIDITY(pPrivateRender))
    {
        DBG_D3D((0,"ERROR: SetupPermediaRenderTarget"
            " invalid pSurfRender->pTextureSurface handle=%08lx",
            pContext->RenderSurfaceHandle));
        return DDERR_INVALIDPARAMS;
    }
    if (0 != pContext->ZBufferHandle)
    {
        pSurfZBuffer = 
            TextureHandleToPtr(pContext->ZBufferHandle, pContext);

        if (!CHECK_D3DSURFACE_VALIDITY(pSurfZBuffer))
        {
            DBG_D3D((0,"ERROR: SetupPermediaRenderTarget"
                " invalid pSurfZBuffer handle=%08lx",
                pContext->ZBufferHandle));
            pContext->ZBufferHandle = 0;
        }
        else
        if (DDSCAPS_SYSTEMMEMORY & pSurfZBuffer->dwCaps)
        {
            DBG_D3D((0, "ERROR: SetupPermediaRenderTarget"
                " pSurfZBuffer in SYSTEMMEMORY  handle=%08lx",
                pContext->ZBufferHandle));
            pContext->ZBufferHandle = 0;
        }
        else
        if (!CHECK_P2_SURFACEDATA_VALIDITY(pSurfZBuffer->pTextureSurface))
        {
            DBG_D3D((0,"ERROR: SetupPermediaRenderTarget"
                " invalid pSurfZBuffer->pTextureSurface handle=%08lx",
                pContext->ZBufferHandle));
            pContext->ZBufferHandle = 0;
        }
    }

    // The default is linear surfaces...
    DBG_D3D((4,"Rendered surface Width: %d", pSurfRender->wWidth));
    pSoftPermedia->FBReadMode.PackedPP = pSurfRender->pTextureSurface->ulPackedPP;
    pContext->PixelOffset = 
        (DWORD)((UINT_PTR)pSurfRender->fpVidMem>>(pSurfRender->dwRGBBitCount>>4));

    DBG_D3D((4,"Setting FBReadMode: 0x%x",pSoftPermedia->FBReadMode));
    // Record the surface information
    RESERVEDMAPTR(10);
    // If there is a Z Buffer, then we must setup the Partial products to be
    // the same as those chosen when it was allocated.

    if (0 != pContext->ZBufferHandle)
    {
        PermediaSurfaceData* pPrivateZ = pSurfZBuffer->pTextureSurface;
        pSoftPermedia->LBReadMode.PackedPP = pPrivateZ->ulPackedPP;
        
        //actually check dwStencilBitMask 
        if (0==pPrivateZ->SurfaceFormat.BlueMask)
        {
            pSoftPermedia->LBReadFormat.DepthWidth = 0;                 // 16 bits
            pSoftPermedia->LBReadFormat.StencilWidth = 0;               // No Stencil
            pSoftPermedia->DeltaMode.DepthFormat = 1;   //PM_DELTAMODE_DEPTHWIDTH_16
        }
        else
        {
            pSoftPermedia->LBReadFormat.DepthWidth = 3;                 // 15 bits
            pSoftPermedia->LBReadFormat.StencilWidth = 3;               // 1 Stencil
            pSoftPermedia->DeltaMode.DepthFormat = 0;   //PM_DELTAMODE_DEPTHWIDTH_15
        }

        SEND_PERMEDIA_DATA(LBWindowBase, 
            (DWORD)((UINT_PTR)pSurfZBuffer->fpVidMem>>P2DEPTH16));
        COPY_PERMEDIA_DATA(LBReadFormat, pSoftPermedia->LBReadFormat);
        COPY_PERMEDIA_DATA(LBWriteFormat, pSoftPermedia->LBReadFormat);

        DBG_D3D((4,"Setting LBReadMode: 0x%x",pSoftPermedia->LBReadMode));
    }
    else
    {   // No Z Buffer, just stuff the same Partial products as the desktop.
        pSoftPermedia->LBReadMode.PackedPP = pContext->ulPackedPP;
    }

    COPY_PERMEDIA_DATA(FBReadMode, pSoftPermedia->FBReadMode);
    COPY_PERMEDIA_DATA(LBReadMode, pSoftPermedia->LBReadMode);

    // Set up the screen dimensions to be the same size as the surface.
    SEND_PERMEDIA_DATA(ScreenSize, 
        (pSurfRender->wWidth & 0xFFFF) | (pSurfRender->wHeight << 16));

    // DitherMode and AlphaBlendMode both depend on the surface pixel format
    // being correct.
    pSoftPermedia->DitherMode.ColorFormat =
    pSoftPermedia->AlphaBlendMode.ColorFormat=
        pPrivateRender->SurfaceFormat.Format;
    pSoftPermedia->DitherMode.ColorFormatExtension = 
    pSoftPermedia->AlphaBlendMode.ColorFormatExtension =
        pPrivateRender->SurfaceFormat.FormatExtension;
    pSoftPermedia->FBReadPixel = pPrivateRender->SurfaceFormat.FBReadPixel;
    SEND_PERMEDIA_DATA(FBReadPixel, pSoftPermedia->FBReadPixel);
    SEND_PERMEDIA_DATA(FBPixelOffset, pContext->PixelOffset);
    COPY_PERMEDIA_DATA(AlphaBlendMode, pSoftPermedia->AlphaBlendMode);
    COPY_PERMEDIA_DATA(DitherMode, pSoftPermedia->DitherMode);
    COMMITDMAPTR();

    DBG_D3D((10,"Exiting SetupPermediaRenderTarget"));
    return DD_OK;

} // SetupPermediaRenderTarget


//=============================================================================
//
// In the new DX7 DDI we don't have the Texture Create/Destroy/Swap calls
// anymore, so now we need a mechanism for generating texture handles. This
// is done by the runtime, which will associate a surface handle for each 
// surface created with the DD local object, and will get our D3DCreateSurfaceEx
// callback called. 
//
// Since this creation can very well happen before we create a D3D context, we
// need to keep track of this association, and when we do get called to create
// a D3D context, we will now be given the relevant DD local object pointer to
// resolve which handles are ours (and to which private texture structures we
// need to use).
//
// This mechanism is also used to associate a palette to a texture
//
//=============================================================================

//-----------------------------------------------------------------------------
//
// BOOL SetTextureSlot
//
// In the handle list element corresponding to this local DD object, store or
// update the pointer to the pTexture associated to the surface handle 
// from the lpDDSLcl surface.
//
//-----------------------------------------------------------------------------
BOOL
SetTextureSlot(LPVOID pDDLcl,
               LPDDRAWI_DDRAWSURFACE_LCL lpDDSLcl,
               PPERMEDIA_D3DTEXTURE pTexture)
{
    int   i,j= -1;
    DWORD dwSurfaceHandle;

    DBG_D3D((10,"Entering SetTextureSlot"));

    ASSERTDD(NULL != pDDLcl && NULL != lpDDSLcl && NULL != pTexture,
                                    "SetTextureSlot invalid input");
    dwSurfaceHandle = lpDDSLcl->lpSurfMore->dwSurfaceHandle;

    // Find the handle list element associated with the local DD object,
    // if there's none then select an empty one to be used
    for (i = 0; i < MAX_CONTEXT_NUM;i++)
    {
        if (pDDLcl == HandleList[i].pDDLcl)
        {
            break;  // found the right slot
        }
        else
        if (0 == HandleList[i].pDDLcl && -1 == j)
        {
            j=i;    // first empty slot !
        }
    }

    // If we overrun the existing handle list elements, we need to
    // initialize an existing empty slot or return an error.
    if (i >= MAX_CONTEXT_NUM)
    {
        if (-1 != j)
        {
            //has an empty slot for this process, so use it
            i = j;  
            HandleList[j].pDDLcl = pDDLcl;
            ASSERTDD(NULL == HandleList[j].dwSurfaceList,"in SetTextureSlot");
        }
        else
        {
            //all process slots has been used, fail
            DBG_D3D((0,"SetTextureSlot failed with pDDLcl=%x "
                       "dwSurfaceHandle=%08lx pTexture=%x",
                       pDDLcl,dwSurfaceHandle,pTexture));
            return false;
        }
    }

    ASSERTDD(i < MAX_CONTEXT_NUM, "in SetTextureSlot");

    if ( NULL == HandleList[i].dwSurfaceList ||
        dwSurfaceHandle >= PtrToUlong(HandleList[i].dwSurfaceList[0]))
    {
        // dwSurfaceHandle numbers are going to be ordinal numbers starting
        // at one, so we use this number to figure out a "good" size for
        // our new list.
        DWORD newsize = ((dwSurfaceHandle + LISTGROWSIZE) / LISTGROWSIZE)
                                                              * LISTGROWSIZE;
        PPERMEDIA_D3DTEXTURE *newlist= (PPERMEDIA_D3DTEXTURE *)
            ENGALLOCMEM( FL_ZERO_MEMORY,
                         sizeof(PPERMEDIA_D3DTEXTURE)*newsize,
                         ALLOC_TAG);
        DBG_D3D((4,"Growing pDDLcl=%x's SurfaceList[%x] size to %08lx",
                   pDDLcl,newlist,newsize));

        if (NULL == newlist)
        {
            DBG_D3D((0,"SetTextureSlot failed to increase "
                       "HandleList[%d].dwSurfaceList",i));
            return false;
        }

        memset(newlist,0,newsize);

        // we had a formerly valid surfacehandle list, so we now must 
        // copy it over and free the memory allocated for it
        if (NULL != HandleList[i].dwSurfaceList)
        {
            memcpy(newlist,HandleList[i].dwSurfaceList,
                PtrToUlong(HandleList[i].dwSurfaceList[0]) * 
                sizeof(PPERMEDIA_D3DTEXTURE));
            ENGFREEMEM(HandleList[i].dwSurfaceList);
            DBG_D3D((4,"Freeing pDDLcl=%x's old SurfaceList[%x]",
                       pDDLcl,HandleList[i].dwSurfaceList));
        }

        HandleList[i].dwSurfaceList = newlist;
         //store size in dwSurfaceList[0]
        *(DWORD*)HandleList[i].dwSurfaceList = newsize;
    }

    // Store a pointer to the pTexture associated to this surface handle
    HandleList[i].dwSurfaceList[dwSurfaceHandle] = pTexture;
    pTexture->HandleListIndex = i; //store index here to facilitate search
    DBG_D3D((4,"Set pDDLcl=%x Handle=%08lx pTexture = %x",
                pDDLcl, dwSurfaceHandle, pTexture));

    DBG_D3D((10,"Exiting SetTextureSlot"));

    return true;
} // SetTextureSlot

//-----------------------------------------------------------------------------
//
// PPERMEDIA_D3DTEXTURE GetTextureSlot
//
// Find the pointer to the PPERMEDIA_D3DTEXTURE associated to the 
// dwSurfaceHandle corresponding to the given local DD object
//
//-----------------------------------------------------------------------------
PPERMEDIA_D3DTEXTURE
GetTextureSlot(LPVOID pDDLcl, DWORD dwSurfaceHandle)
{
    DBG_D3D((10,"Entering GetTextureSlot"));

    DWORD   i;
    for (i = 0; i < MAX_CONTEXT_NUM; i++)
    {
        if (HandleList[i].pDDLcl == pDDLcl)
        {
            if (HandleList[i].dwSurfaceList &&
                PtrToUlong(HandleList[i].dwSurfaceList[0]) > dwSurfaceHandle )
            {
                return  HandleList[i].dwSurfaceList[dwSurfaceHandle];
            }
            else
                break;
        }
    }
    DBG_D3D((10,"Exiting GetTextureSlot"));

    return NULL;    //Not found
} // GetTextureSlot

//-----------------------------------------------------------------------------
//
// LPDWLIST GetSurfaceHandleList
//
// Get the handle list which is associated to a specific PDD_DIRECTDRAW_LOCAL
// pDDLcl. It is called from D3DContextCreate to get the handle list associated
// to the pDDLcl with which the context is being created.
//
//-----------------------------------------------------------------------------
LPDWLIST 
GetSurfaceHandleList(LPVOID pDDLcl)
{
    DWORD   i;

    DBG_D3D((10,"Entering GetSurfaceHandleList"));

    ASSERTDD(NULL != pDDLcl, "GetSurfaceHandleList get NULL==pDDLcl"); 
    for (i = 0; i < MAX_CONTEXT_NUM;i++)
    {
        if (HandleList[i].pDDLcl == pDDLcl)
        {
            DBG_D3D((4,"Getting pHandleList=%08lx for pDDLcl %x",
                &HandleList[i],pDDLcl));
            return &HandleList[i];
        }
    }

    DBG_D3D((10,"Exiting GetSurfaceHandleList"));

    return NULL;   //No surface handle available yet
} // GetSurfaceHandleList

//-----------------------------------------------------------------------------
//
// void ReleaseSurfaceHandleList
//
// Free all the associated surface handle and palette memory pools associated
// to a given DD local object.
//
//-----------------------------------------------------------------------------
void 
ReleaseSurfaceHandleList(LPVOID pDDLcl)
{
    DWORD   i;

    DBG_D3D((10,"Entering ReleaseSurfaceHandleList"));

    ASSERTDD(NULL != pDDLcl, "ReleaseSurfaceHandleList get NULL==pDDLcl"); 
    for (i = 0; i < MAX_CONTEXT_NUM; i++)
    {
        if (HandleList[i].pDDLcl == pDDLcl)
        {
            DWORD j;

            if (NULL != HandleList[i].dwSurfaceList)
            {
                DBG_D3D((4,"Releasing HandleList[%d].dwSurfaceList[%x] "
                           "for pDDLcl %x", i, HandleList[i].dwSurfaceList,
                           pDDLcl));

                for (j = 1; j < PtrToUlong(HandleList[i].dwSurfaceList[0]); j++)
                {
                    PERMEDIA_D3DTEXTURE* pTexture = 
                        (PERMEDIA_D3DTEXTURE*)HandleList[i].dwSurfaceList[j];
                    if (NULL != pTexture)
                    {
                        PermediaSurfaceData *pPrivateData=
                            pTexture->pTextureSurface;
                        if (CHECK_P2_SURFACEDATA_VALIDITY(pPrivateData) &&
                            (pPrivateData->fpVidMem))
                        {
                            TextureCacheManagerRemove(&P2TextureManager,
                                pTexture);
                        }
                        ENGFREEMEM(pTexture);
                    }
                }

                ENGFREEMEM(HandleList[i].dwSurfaceList);
                HandleList[i].dwSurfaceList = NULL;
            }

            HandleList[i].pDDLcl = NULL;

            if (NULL != HandleList[i].dwPaletteList)
            {
                DBG_D3D((4,"Releasing dwPaletteList %x for pDDLcl %x",
                    HandleList[i].dwPaletteList,pDDLcl));

                for (j = 1; j < PtrToUlong(HandleList[i].dwPaletteList[0]); j++)
                {
                    LPVOID pPalette = (LPVOID)HandleList[i].dwPaletteList[j];
                    if (NULL != pPalette)
                        ENGFREEMEM(pPalette);
                }

                ENGFREEMEM(HandleList[i].dwPaletteList);
                HandleList[i].dwPaletteList = NULL;
            }

            break;
        }
    }

    DBG_D3D((10,"Exiting ReleaseSurfaceHandleList"));
} // ReleaseSurfaceHandleList

//-----------------------------Public Routine----------------------------------
//
// DWORD D3DGetDriverState
//
// This callback is used by both the DirectDraw and Direct3D runtimes to obtain
// information from the driver about its current state.
//
// Parameters
//
//     lpgdsd   
//           pointer to GetDriverState data structure
//
//           dwFlags
//                   Flags to indicate the data required
//           dwhContext
//                   The ID of the context for which information 
//                   is being requested
//           lpdwStates
//                   Pointer to the state data to be filled in by the driver
//           dwLength
//                   Length of the state data buffer to be filled 
//                   in by the driver
//           ddRVal
//                   Return value
//
// Return Value
//
//      DDHAL_DRIVER_HANDLED
//      DDHAL_DRIVER_NOTHANDLED
//-----------------------------------------------------------------------------
DWORD CALLBACK  
D3DGetDriverState( LPDDHAL_GETDRIVERSTATEDATA lpgdsd )
{
    PERMEDIA_D3DCONTEXT *pContext;
    DBG_D3D((6,"Entering D3DGetDriverState"));
    if (lpgdsd->dwFlags != D3DDEVINFOID_TEXTUREMANAGER)
    {
        DBG_D3D((0,"D3DGetDriverState DEVICEINFOID=%08lx not supported",
            lpgdsd->dwFlags));
        return DDHAL_DRIVER_NOTHANDLED;
    }
    if (lpgdsd->dwLength < sizeof(D3DDEVINFO_TEXTUREMANAGER))
    {
        DBG_D3D((0,"D3DGetDriverState dwLength=%d is not sufficient",
            lpgdsd->dwLength));
        return DDHAL_DRIVER_NOTHANDLED;
    }
    pContext = (PERMEDIA_D3DCONTEXT *)ContextSlots[lpgdsd->dwhContext] ;
    // Check if we got a valid context handle.
    CHK_CONTEXT(pContext, lpgdsd->ddRVal, "D3DGetDriverState");

    TextureCacheManagerGetStats(pContext,
           (LPD3DDEVINFO_TEXTUREMANAGER)lpgdsd->lpdwStates);

    lpgdsd->ddRVal = DD_OK;

    DBG_D3D((6,"Exiitng D3DGetDriverState"));

    return DDHAL_DRIVER_HANDLED;
} // D3DGetDriverState

//-----------------------------------------------------------------------------
//
//  __CreateSurfaceHandle
//
//  allocate a new surface handle
//
//  return value
//
//      DD_OK   -- no error
//      DDERR_OUTOFMEMORY -- allocation of texture handle failed
//
//-----------------------------------------------------------------------------

DWORD __CreateSurfaceHandle( PPDev ppdev,
                             LPVOID pDDLcl,
                             LPDDRAWI_DDRAWSURFACE_LCL lpDDSLcl)
{
    PPERMEDIA_D3DTEXTURE pTexture;

    DUMPSURFACE(10, lpDDSLcl, NULL);

    if (0 == lpDDSLcl->lpSurfMore->dwSurfaceHandle)
    {
        DBG_D3D((0,"D3DCreateSurfaceEx got 0 surfacehandle dwCaps=%08lx",
            lpDDSLcl->ddsCaps.dwCaps));
        return DD_OK;
    }

    pTexture = 
        GetTextureSlot(pDDLcl,lpDDSLcl->lpSurfMore->dwSurfaceHandle);

    if ((0 == lpDDSLcl->lpGbl->fpVidMem) && 
        (DDSCAPS_SYSTEMMEMORY & lpDDSLcl->ddsCaps.dwCaps))
    {
        // this is a system memory destroy notification
        // so go ahead free the slot for this surface if we have it
        if (NULL != pTexture)
        {
            ASSERTDD(HandleList[pTexture->HandleListIndex].dwSurfaceList
                [lpDDSLcl->lpSurfMore->dwSurfaceHandle] == pTexture,
                "__CreateSurfaceHandle: mismatching pTexture in HandleList");
            HandleList[pTexture->HandleListIndex].dwSurfaceList
                [lpDDSLcl->lpSurfMore->dwSurfaceHandle]=0;
            ENGFREEMEM(pTexture);
            DBG_D3D((8,"D3DCreateSurfaceEx freeing handle=%08lx dwCaps=%08lx",
            lpDDSLcl->lpSurfMore->dwSurfaceHandle,lpDDSLcl->ddsCaps.dwCaps));
        }
        return DD_OK;
    }
    if (NULL == pTexture)
    {
        pTexture =
            (PERMEDIA_D3DTEXTURE*)ENGALLOCMEM( FL_ZERO_MEMORY,
                                               sizeof(PERMEDIA_D3DTEXTURE),
                                               ALLOC_TAG);

        if (NULL != pTexture) 
        {
            if (!SetTextureSlot(pDDLcl,lpDDSLcl,pTexture))
            {
                // Free texture structure since we won't be able to remember it
                // in order to later delete it. We must do it now.
                ENGFREEMEM(pTexture);
                return DDERR_OUTOFMEMORY;
            }
        }
        else
        {
            DBG_D3D((0,"ERROR: Couldn't allocate Texture data mem"));
            return DDERR_OUTOFMEMORY;
        } 
    }

    lpDDSLcl->dwReserved1=pTexture->HandleListIndex;    
    __InitD3DTextureWithDDSurfInfo(pTexture,lpDDSLcl,ppdev);

    if (pTexture->dwCaps & DDSCAPS_TEXTURE)
    {
        for (int i = 1; i < MAX_CONTEXT_NUM; i++)
        {
            PERMEDIA_D3DCONTEXT *pContext =
                                     (PERMEDIA_D3DCONTEXT *)ContextSlots[i];
            if (IS_D3DCONTEXT_VALID(pContext))
            {
                DBG_D3D((4,"   Context 0x%x, Pointer 0x%x",
                                                (DWORD)i, pContext));
                if ((pContext->pDDLcl == pDDLcl)
                    && (pContext->CurrentTextureHandle == 
                        lpDDSLcl->lpSurfMore->dwSurfaceHandle) 
                   )
                {
                    // If the texture being swapped is 
                    // currently being used then we need 
                    // to change the chip setup to reflect this.
                    DIRTY_TEXTURE;
                }
            }
        }
    }

    return DD_OK;
}


//-----------------------------------------------------------------------------
//
//  __CreateSurfaceHandleLoop
//
//  allocate a list of new surface handles by traversing AttachList
//      recursively and calling __CreateSurfaceHandle()
//      only exceptions are for MIPMAP and CUBMAP, which we only
//      use one handle to the root to represent the whole surface
//  return value
//
//      DD_OK   -- no error
//      DDERR_OUTOFMEMORY -- allocation of texture handle failed
//
//-----------------------------------------------------------------------------

DWORD __CreateSurfaceHandleLoop( PPDev ppdev,
                             LPVOID pDDLcl,
                             LPDDRAWI_DDRAWSURFACE_LCL lpDDSLclroot,
                             LPDDRAWI_DDRAWSURFACE_LCL lpDDSLcl)
{
    LPATTACHLIST    curr;
    DWORD ddRVal=DD_OK;
    // Now allocate the texture data space
    if (0 == lpDDSLcl->lpSurfMore->dwSurfaceHandle)
    {
        DBG_D3D((0,"__CreateSurfaceHandleLoop got 0 handle dwCaps=%08lx",
            lpDDSLcl->ddsCaps.dwCaps));
        return DD_OK;
    }

    if ((0 == lpDDSLcl->lpGbl->dwReserved1) && 
        (DDSCAPS_VIDEOMEMORY & lpDDSLcl->ddsCaps.dwCaps)
        )
    {
        DBG_D3D((4,"__CreateSurfaceHandleLoop got "
            "handle=%08lx dwCaps=%08lx not yet created",
            lpDDSLcl->lpSurfMore->dwSurfaceHandle,lpDDSLcl->ddsCaps.dwCaps));
        return DD_OK;
    }

    DBG_D3D((4,"** In __CreateSurfaceHandleLoop %08lx %08lx %08lx %08lx %x",
        lpDDSLcl->ddsCaps.dwCaps,lpDDSLcl->lpSurfMore->dwSurfaceHandle,
        lpDDSLcl->dwFlags,lpDDSLcl->lpGbl->dwReserved1,
        lpDDSLcl->lpGbl->fpVidMem));

    ddRVal=__CreateSurfaceHandle( ppdev, pDDLcl, lpDDSLcl);
    if (DD_OK != ddRVal)
    {
        return ddRVal;
    }

    // for some surfaces other than MIPMAP or CUBEMAP, such as
    // flipping chains, we make a slot for every surface, as
    // they are not as interleaved
    if ((lpDDSLcl->ddsCaps.dwCaps & DDSCAPS_MIPMAP) ||
        (lpDDSLcl->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP)
       )
    {
        return DD_OK;
    }
    curr = lpDDSLcl->lpAttachList;
    if (NULL == curr) 
        return DD_OK;

    // check if there is another surface attached!
    if (curr->lpLink)
    {
        lpDDSLcl=curr->lpLink->lpAttached; 
        if (NULL != lpDDSLcl && lpDDSLcl != lpDDSLclroot)
        {
            ddRVal=__CreateSurfaceHandleLoop( ppdev, pDDLcl, 
                 lpDDSLclroot, lpDDSLcl);
            if (DD_OK != ddRVal)
            {
                return ddRVal;
            }
        }
    }
    lpDDSLcl=curr->lpAttached;
    if (NULL != lpDDSLcl && lpDDSLcl != lpDDSLclroot)
        ddRVal=__CreateSurfaceHandleLoop( ppdev, pDDLcl, 
            lpDDSLclroot, lpDDSLcl);
    return ddRVal;
}
//-----------------------------Public Routine----------------------------------
//
// DWORD D3DCreateSurfaceEx
//
// D3dCreateSurfaceEx creates a Direct3D surface from a DirectDraw surface and 
// associates a requested handle value to it.
//
// All Direct3D drivers must support D3dCreateSurfaceEx.
//
// D3dCreateSurfaceEx creates an association between a DirectDraw surface and 
// a small integer surface handle. By creating these associations between a
// handle and a DirectDraw surface, D3dCreateSurfaceEx allows a surface handle
// to be imbedded in the Direct3D command stream. For example when the
// D3DDP2OP_TEXBLT command token is sent to D3dDrawPrimitives2 to load a texture
// map, it uses a source handle and destination handle which were associated
//  with a DirectDraw surface through D3dCreateSurfaceEx.
//
// For every DirectDraw surface created under the local DirectDraw object, the
// runtime generates a valid handle that uniquely identifies the surface and
// places it in pcsxd->lpDDSLcl->lpSurfMore->dwSurfaceHandle. This handle value
// is also used with the D3DRENDERSTATE_TEXTUREHANDLE render state to enable
// texturing, and with the D3DDP2OP_SETRENDERTARGET and D3DDP2OP_CLEAR commands
// to set and/or clear new rendering and depth buffers. The driver should fail
// the call and return DDHAL_DRIVER_HANDLE if it cannot create the Direct3D
// surface. 
//
// As appropriate, the driver should also store any surface-related information
// that it will subsequently need when using the surface. The driver must create
// a new surface table for each new lpDDLcl and implicitly grow the table when
// necessary to accommodate more surfaces. Typically this is done with an
// exponential growth algorithm so that you don't have to grow the table too
// often. Direct3D calls D3dCreateSurfaceEx after the surface is created by
// DirectDraw by request of the Direct3D runtime or the application.
//
// Parameters
//
//      lpcsxd
//           pointer to CreateSurfaceEx structure that contains the information
//           required for the driver to create the surface (described below). 
//
//           dwFlags
//                   Currently unused
//           lpDDLcl
//                   Handle to the DirectDraw object created by the application.
//                   This is the scope within which the lpDDSLcl handles exist.
//                   A DD_DIRECTDRAW_LOCAL structure describes the driver.
//           lpDDSLcl
//                   Handle to the DirectDraw surface we are being asked to
//                   create for Direct3D. These handles are unique within each
//                   different DD_DIRECTDRAW_LOCAL. A DD_SURFACE_LOCAL structure
//                   represents the created surface object.
//           ddRVal
//                   Specifies the location in which the driver writes the return
//                   value of the D3dCreateSurfaceEx callback. A return code of
//                   DD_OK indicates success.
//
// Return Value
//
//      DDHAL_DRIVER_HANDLE
//      DDHAL_DRIVER_NOTHANDLE
//
//-----------------------------------------------------------------------------
DWORD CALLBACK 
D3DCreateSurfaceEx( LPDDHAL_CREATESURFACEEXDATA lpcsxd )
{
    PPERMEDIA_D3DTEXTURE pTexture;
    LPVOID pDDLcl= (LPVOID)lpcsxd->lpDDLcl;
    LPDDRAWI_DDRAWSURFACE_LCL   lpDDSLcl=lpcsxd->lpDDSLcl;
    LPATTACHLIST    curr;

    DBG_D3D((6,"Entering D3DCreateSurfaceEx"));

    lpcsxd->ddRVal = DD_OK;

    if (NULL == lpDDSLcl || NULL == pDDLcl)
    {
        DBG_D3D((0,"D3DCreateSurfaceEx received 0 lpDDLcl or lpDDSLcl pointer"));
        return DDHAL_DRIVER_HANDLED;
    }


    // We check that what we are handling is a texture, zbuffer or a rendering
    // target buffer. We don't check if it is however stored in local video
    // memory since it might also be a system memory texture that we will later
    // blt with __TextureBlt.
    // also if your driver supports DDSCAPS_EXECUTEBUFFER create itself, it must 
    // process DDSCAPS_EXECUTEBUFFER here as well.
    if (!(lpDDSLcl->ddsCaps.dwCaps & 
             (DDSCAPS_TEXTURE       | 
              DDSCAPS_3DDEVICE      | 
              DDSCAPS_ZBUFFER))
       )
    {
        DBG_D3D((2,"D3DCreateSurfaceEx w/o "
             "DDSCAPS_TEXTURE/3DDEVICE/ZBUFFER Ignored"
             "dwCaps=%08lx dwSurfaceHandle=%08lx",
             lpDDSLcl->ddsCaps.dwCaps,
             lpDDSLcl->lpSurfMore->dwSurfaceHandle));
        return DDHAL_DRIVER_HANDLED;
    }

    DBG_D3D((4,"Entering D3DCreateSurfaceEx handle=%08lx",
        lpDDSLcl->lpSurfMore->dwSurfaceHandle));
    PPDev ppdev=(PPDev)lpcsxd->lpDDLcl->lpGbl->dhpdev;
    PERMEDIA_DEFS(ppdev);

    // Now allocate the texture data space
    lpcsxd->ddRVal = __CreateSurfaceHandleLoop( ppdev, pDDLcl, lpDDSLcl, lpDDSLcl);
    DBG_D3D((4,"Exiting D3DCreateSurfaceEx"));

    return DDHAL_DRIVER_HANDLED;
} // D3DCreateSurfaceEx

//-----------------------------Public Routine----------------------------------
//
// DWORD D3DDestroyDDLocal
//
// D3dDestroyDDLocal destroys all the Direct3D surfaces previously created by
// D3DCreateSurfaceEx that belong to the same given local DirectDraw object.
//
// All Direct3D drivers must support D3dDestroyDDLocal.
// Direct3D calls D3dDestroyDDLocal when the application indicates that the
// Direct3D context is no longer required and it will be destroyed along with
// all surfaces associated to it. The association comes through the pointer to
// the local DirectDraw object. The driver must free any memory that the
// driver's D3dCreateSurfaceExDDK_D3dCreateSurfaceEx_GG callback allocated for
// each surface if necessary. The driver should not destroy the DirectDraw
// surfaces associated with these Direct3D surfaces; this is the application's
// responsibility.
//
// Parameters
//
//      lpdddd
//            Pointer to the DestoryLocalDD structure that contains the
//            information required for the driver to destroy the surfaces.
//
//            dwFlags
//                  Currently unused
//            pDDLcl
//                  Pointer to the local Direct Draw object which serves as a
//                  reference for all the D3D surfaces that have to be destroyed.
//            ddRVal
//                  Specifies the location in which the driver writes the return
//                  value of D3dDestroyDDLocal. A return code of DD_OK indicates
//                   success.
//
// Return Value
//
//      DDHAL_DRIVER_HANDLED
//      DDHAL_DRIVER_NOTHANDLED
//-----------------------------------------------------------------------------
DWORD CALLBACK  
D3DDestroyDDLocal( LPDDHAL_DESTROYDDLOCALDATA lpdddd )
{
    DBG_D3D((6,"Entering D3DDestroyDDLocal"));

    ReleaseSurfaceHandleList(LPVOID(lpdddd->pDDLcl));
    lpdddd->ddRVal = DD_OK;

    DBG_D3D((6,"Exiting D3DDestroyDDLocal"));

    return DDHAL_DRIVER_HANDLED;
} // D3DDestroyDDLocal

//-----------------------------Public Routine----------------------------------
//
//  DdSetColorkey
//
//  DirectDraw SetColorkey callback
//
//  Parameters
//       lpSetColorKey
//             Pointer to the LPDDHAL_SETCOLORKEYDATA parameters structure 
//
//             lpDDSurface
//                  Surface struct
//             dwFlags
//                  Flags
//             ckNew
//                  New chroma key color values
//             ddRVal
//                  Return value
//             SetColorKey
//                  Unused: Win95 compatibility
//
//-----------------------------------------------------------------------------
DWORD CALLBACK 
DdSetColorKey(LPDDHAL_SETCOLORKEYDATA lpSetColorKey)
{
    DWORD dwSurfaceHandle =
                        lpSetColorKey->lpDDSurface->lpSurfMore->dwSurfaceHandle;
    DWORD index = (DWORD)lpSetColorKey->lpDDSurface->dwReserved1;

    DBG_D3D((6,"Entering DdSetColorKey dwSurfaceHandle=%d index=%d",
        dwSurfaceHandle, index));

    lpSetColorKey->ddRVal = DD_OK;
    // We don't have to do anything for normal blt source colour keys:
    if (!(DDSCAPS_TEXTURE & lpSetColorKey->lpDDSurface->ddsCaps.dwCaps) ||
        !(DDSCAPS_VIDEOMEMORY & lpSetColorKey->lpDDSurface->ddsCaps.dwCaps) 
       )
    {
        return(DDHAL_DRIVER_HANDLED);
    }

    if (0 != dwSurfaceHandle && NULL != HandleList[index].dwSurfaceList)
    {
        PERMEDIA_D3DTEXTURE *pTexture =
                                HandleList[index].dwSurfaceList[dwSurfaceHandle];

        ASSERTDD(PtrToUlong(HandleList[index].dwSurfaceList[0]) > dwSurfaceHandle,
            "SetColorKey: incorrect dwSurfaceHandle");

        if (NULL != pTexture)
        {
            DBG_D3D((4, "DdSetColorKey surface=%08lx KeyLow=%08lx",
                dwSurfaceHandle,pTexture->dwKeyLow));
            pTexture->dwFlags |= DDRAWISURF_HASCKEYSRCBLT;
            pTexture->dwKeyLow = lpSetColorKey->ckNew.dwColorSpaceLowValue;
            pTexture->dwKeyHigh = lpSetColorKey->ckNew.dwColorSpaceHighValue;
        }
    }
    else
    {
        lpSetColorKey->ddRVal = DDERR_INVALIDPARAMS;
    }
    DBG_D3D((6,"Exiting DdSetColorKey"));

    return DDHAL_DRIVER_HANDLED;
}   // DdSetColorKey

