/*==========================================================================;
 *
 *  Copyright (C) 1997, 1998 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       haldrv.cpp
 *  Content:    Direct3D HAL Driver
 *
 ***************************************************************************/

#include "pch.cpp"
#pragma hdrstop

#include "commdrv.hpp"
#include "d3dfei.h"
#include "tlhal.h"

#ifndef WIN95
#include <ntgdistr.h>
#endif

void Destroy(LPDIRECT3DDEVICEI lpDevI);
//---------------------------------------------------------------------
int
GenGetExtraVerticesNumber( LPDIRECT3DDEVICEI lpDevI )
{
    LPD3DHAL_GLOBALDRIVERDATA lpGlob = lpDevI->lpD3DHALGlobalDriverData;

    return (int)(lpGlob->dwNumVertices ?
        lpGlob->dwNumVertices : D3DHAL_DEFAULT_TL_NUM);

}
//---------------------------------------------------------------------
HRESULT CalcDDSurfInfo(LPDIRECT3DDEVICEI lpDevI, BOOL bUpdateZBufferFields)
{
    DDSURFACEDESC ddsd;
    HRESULT ddrval;
    DWORD dwWidth, dwHeight;
    unsigned long m;
    int s;

    // Get info from the surface

    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
    ddsd.dwSize = sizeof(DDSURFACEDESC);
    ddrval = lpDevI->lpDDSTarget->GetSurfaceDesc(&ddsd);
    if (ddrval != DD_OK) {
        return ddrval;
    }

    dwWidth = ddsd.dwWidth;
    dwHeight = ddsd.dwHeight;
    if ((ddsd.ddpfPixelFormat.dwFlags & (DDPF_PALETTEINDEXED4 | DDPF_PALETTEINDEXED8)) == 0) {
        // palettized pixfmts will not have valid RGB Bitmasks, so avoid computing this for them

        lpDevI->red_mask = ddsd.ddpfPixelFormat.dwRBitMask;
        lpDevI->green_mask = ddsd.ddpfPixelFormat.dwGBitMask;
        lpDevI->blue_mask = ddsd.ddpfPixelFormat.dwBBitMask;
        
        if ((lpDevI->red_mask == 0x0)  ||
            (lpDevI->green_mask == 0x0) ||
            (lpDevI->blue_mask == 0x0))
        {
            D3D_ERR("All the color masks in the Render target's pixel-format must be non-zero");
            return DDERR_INVALIDPIXELFORMAT;
        }

        // these are used by Clear
        for (s = 0, m = lpDevI->red_mask; !(m & 1); s++, m >>= 1) ;
        lpDevI->red_shift = s;
        lpDevI->red_scale = 255 / (lpDevI->red_mask >> s);
        for (s = 0, m = lpDevI->green_mask; !(m & 1); s++, m >>= 1) ;
        lpDevI->green_shift = s;
        lpDevI->green_scale = 255 / (lpDevI->green_mask >> s);
        for (s = 0, m = lpDevI->blue_mask; !(m & 1); s++, m >>= 1) ;
        lpDevI->blue_shift = s;
        lpDevI->blue_scale = 255 / (lpDevI->blue_mask >> s);

        if ( (lpDevI->red_scale==0) ||
             (lpDevI->green_scale==0) ||
             (lpDevI->blue_scale==0) )
            return DDERR_INVALIDPIXELFORMAT;

        // If there is Alpha in this format
        if (ddsd.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
        {
            lpDevI->alpha_mask = ddsd.ddpfPixelFormat.dwRGBAlphaBitMask;
            for (s = 0, m = lpDevI->alpha_mask; !(m & 1); s++, m >>= 1) ;
            lpDevI->alpha_shift = s;
            lpDevI->alpha_scale = 255 / (lpDevI->alpha_mask >> s);
        }
        else
        {
            lpDevI->alpha_shift = lpDevI->alpha_scale = lpDevI->alpha_mask = 0;
        }
        
        lpDevI->bDDSTargetIsPalettized=FALSE;
    } else
        lpDevI->bDDSTargetIsPalettized=TRUE;

    if (lpDevI->lpDDSZBuffer_DDS7 && bUpdateZBufferFields) {
        // Get info from the surface

        DDSURFACEDESC2 ddsd2;

        memset(&ddsd2, 0, sizeof(ddsd2));
        ddsd2.dwSize = sizeof(ddsd2);
        ddrval = lpDevI->lpDDSZBuffer_DDS7->GetSurfaceDesc(&ddsd2);
        if (ddrval != DD_OK) {
            return ddrval;
        }

        if( ddsd2.ddpfPixelFormat.dwZBitMask!=0x0) {
            for (s = 0, m = ddsd2.ddpfPixelFormat.dwZBitMask; !(m & 0x1); s++, m >>= 1) ;
            lpDevI->zmask_shift=s;
        } else {
            lpDevI->zmask_shift=0;     // if ZBitMask isn't being set, then Clear2 will never be used,
                                      // so zbuf_shift/stencil_shift wont be needed anyway
        }

        if( ddsd2.ddpfPixelFormat.dwStencilBitMask!=0x0) {
            for (s = 0, m = ddsd2.ddpfPixelFormat.dwStencilBitMask; !(m & 0x1); s++, m >>= 1) ;
            lpDevI->stencilmask_shift=s;
        } else {
            lpDevI->stencilmask_shift=0;
        }
    }

    return D3D_OK;
}

// called by DDRAW
extern "C" HRESULT __stdcall Direct3D_HALCleanUp(LPD3DHAL_CALLBACKS lpD3DHALCallbacks, DWORD dwPID)
{
    D3DHAL_CONTEXTDESTROYALLDATA data;
    HRESULT ret;

    DDASSERT(lpD3DHALCallbacks!=NULL);
    if (lpD3DHALCallbacks->ContextDestroyAll==NULL) {
        // no cleanup necessary (running on d3d hel)
    return D3D_OK;
    }

    memset(&data, 0, sizeof(D3DHAL_CONTEXTDESTROYALLDATA));
    data.dwPID = dwPID;

    // I'd prefer to use CALL_HALONLY() to do the locking (to avoid doing it for the SW rasterizers),
    // but that requires a pDevI which I can't get to from the caller, which is a ddraw cleanup routine

#ifdef WIN95
    _EnterSysLevel(lpWin16Lock);
#endif

    ret = (*lpD3DHALCallbacks->ContextDestroyAll)(&data);

#ifdef WIN95
    _LeaveSysLevel(lpWin16Lock);
#endif

    return ret;
}

// ATTENTION - These two functions should be combined into one as soon
// as ContextCreate has the new private data mechanism built in.
#ifdef WIN95
HRESULT DIRECT3DDEVICEI::halCreateContext()
{
    D3DHAL_CONTEXTCREATEDATA data;
    HRESULT ret;

    D3D_INFO(6, "in halCreateContext. Creating Context for driver = %08lx", this);

    memset(&data, 0, sizeof(D3DHAL_CONTEXTCREATEDATA));
    //
    // From DX7 onwards, drivers should be accepting
    // Surface Locals instead of the Surface interfaces
    // this future-proofs the drivers
    //
    if (IS_DX7HAL_DEVICE(this))
    {
        if (this->lpDD)
            data.lpDDLcl = ((LPDDRAWI_DIRECTDRAW_INT)(this->lpDD))->lpLcl;
        else
            data.lpDDLcl = NULL;

        if (lpDDSTarget)
            data.lpDDSLcl = ((LPDDRAWI_DDRAWSURFACE_INT)lpDDSTarget)->lpLcl;
        else
            data.lpDDSLcl = NULL;

        if (lpDDSZBuffer)
            data.lpDDSZLcl = ((LPDDRAWI_DDRAWSURFACE_INT)lpDDSZBuffer)->lpLcl;
        else
            data.lpDDSZLcl = NULL;

    }
    else
    {
        data.lpDDGbl = this->lpDDGbl;
        data.lpDDS = this->lpDDSTarget;
        data.lpDDSZ = this->lpDDSZBuffer;
    }

    // Hack Alert!! dwhContext is used to inform the driver which version
    // of the D3D interface is calling it.
    data.dwhContext = 3;
    data.dwPID  = GetCurrentProcessId();
    // Hack Alert!! ddrval is used to inform the driver which driver type
    // the runtime thinks it is (DriverStyle registry setting)
    data.ddrval = this->deviceType;

    if (!IS_HW_DEVICE(this))
    {
        // The new software rasterizers want to share IM's state vector so
        // we need to pass them the rstates pointer.  They don't
        // care about dwPID so stick the pointer in there.
        data.dwPID = (DWORD)this->rstates;
    }

    /* 0 for pre-DX5 devices.
     * 1 for DX5 devices.
     * 2 for DX6 devices.
     * 3 for DX7 devices.
     */

    CALL_HALONLY(ret, this, ContextCreate, &data);
    if (ret != DDHAL_DRIVER_HANDLED || data.ddrval != DD_OK) {
        D3D_ERR( "Driver did not handle ContextCreate" );
        return (DDERR_GENERIC);
    }
    this->dwhContext = data.dwhContext;

    if (D3DMalloc ((void**)&this->lpwDPBufferAlloced,
                   max(dwD3DTriBatchSize*4*sizeof(WORD),
                       dwHWBufferSize) +32) != DD_OK)
    {
        D3D_ERR( "Out of memory in DeviceCreate" );
        return (DDERR_OUTOFMEMORY);
    }
    this->lpwDPBuffer = (LPWORD) (((DWORD) this->lpwDPBufferAlloced+31) & (~31));

    // save the surface handle for later checks
    this->hSurfaceTarget = ((LPDDRAWI_DDRAWSURFACE_INT)this->lpDDSTarget)->lpLcl->lpSurfMore->dwSurfaceHandle;

    D3D_INFO(6, "in halCreateContext. Succeeded. dwhContext = %d", data.dwhContext);

    return (D3D_OK);
}
#else
    /*
     * On NT the kernel code creates the buffer to be used
     * for DrawPrim batching and returns it as extra data
     * in the ContextCreate request.
     */
HRESULT DIRECT3DDEVICEI::halCreateContext()
{
    D3DNTHAL_CONTEXTCREATEI ntData;
    D3DHAL_CONTEXTCREATEDATA *lpData =
        (D3DHAL_CONTEXTCREATEDATA *)&ntData;
    HRESULT ret;

    D3D_INFO(6, "in halCreateContext. Creating Context for driver = %08lx", this);

    /*
     * AnanKan: Assert here that the D3DNTHAL_CONTEXTCREATEI structure is
     * 2 DWORDS bigger than D3DHAL_CONTEXTCREATEDATA. This will be a good
     * consistency check for NT kernel updates.
     */
    memset(&ntData, 0, sizeof(ntData));
    if (IS_DX7HAL_DEVICE(this) || (dwFEFlags & D3DFE_REALHAL))
    {
        if (this->lpDD)
            lpData->lpDDLcl = ((LPDDRAWI_DIRECTDRAW_INT)(this->lpDD))->lpLcl;
        else
            lpData->lpDDLcl = NULL;

        if (lpDDSTarget)
            lpData->lpDDSLcl = ((LPDDRAWI_DDRAWSURFACE_INT)lpDDSTarget)->lpLcl;
        else
            lpData->lpDDSLcl = NULL;

        if (lpDDSZBuffer)
            lpData->lpDDSZLcl = ((LPDDRAWI_DDRAWSURFACE_INT)lpDDSZBuffer)->lpLcl;
        else
            lpData->lpDDSZLcl = NULL;

    }
    else
    {
        lpData->lpDDGbl = lpDDGbl;
        lpData->lpDDS = lpDDSTarget;
        lpData->lpDDSZ = lpDDSZBuffer;
    }

    // Hack Alert!! dwhContext is used to inform the driver which version
    // of the D3D interface is calling it.
    lpData->dwhContext = 3;
    lpData->dwPID = GetCurrentProcessId();
    // Hack Alert!! ddrval is used to inform the driver which driver type
    // the runtime thinks it is (DriverStyle registry setting)
    lpData->ddrval = this->deviceType;

    if (IS_HW_DEVICE(this))
    {
        // The new software rasterizers want to share IM's state vector so
        // we need to pass them the rstates pointer.  They don't
        // care about dwPID so stick the pointer in there.
        lpData->dwPID = (DWORD)((ULONG_PTR)this->rstates);
    }

    /* 0 for pre-DX5 devices.
     * 1 for DX5 devices.
     * 2 for DX6 devices.
     * 3 for DX7 devices.
     */
    ntData.cjBuffer = this->dwDPBufferSize;
    ntData.pvBuffer = NULL;

    CALL_HALONLY(ret, this, ContextCreate, lpData);
    if (ret != DDHAL_DRIVER_HANDLED || lpData->ddrval != DD_OK) {
        D3D_ERR( "Driver did not handle ContextCreate" );
        return (DDERR_GENERIC);
    }
    this->dwhContext = (DWORD)((ULONG_PTR)lpData->dwhContext);

    // If the this chose not to allocate a DrawPrim buffer do
    // it for them.
    if (ntData.pvBuffer == NULL)
    {
        this->dwDPBufferSize =
            dwD3DTriBatchSize * 4 * sizeof(WORD);
        if (this->dwDPBufferSize < dwHWBufferSize)
        {
            this->dwDPBufferSize = dwHWBufferSize;
        }

        ret = D3DMalloc((void**)&this->lpwDPBufferAlloced,
                        this->dwDPBufferSize + 32);
        if (ret != DD_OK)
        {
            return ret;
        }

        ntData.pvBuffer = (LPVOID)
            (((ULONG_PTR)this->lpwDPBufferAlloced + 31) & ~31);
        ntData.cjBuffer = this->dwDPBufferSize + 32 -
            (DWORD)((ULONG_PTR)ntData.pvBuffer -
                    (ULONG_PTR)this->lpwDPBufferAlloced);
    }
    else if( (this->dwDPBufferSize &&
              ntData.cjBuffer < this->dwDPBufferSize) ||
             ntData.cjBuffer < sizeof(D3DHAL_DRAWPRIMCOUNTS) )
    {
        D3D_ERR( "Driver did not correctly allocate DrawPrim buffer");
        return (DDERR_GENERIC);
    }

    // Need to save the buffer space provided and its size
    this->lpwDPBuffer = (LPWORD)ntData.pvBuffer;

    // save the surface handle for later checks
    this->hSurfaceTarget = (DWORD)(((LPDDRAWI_DDRAWSURFACE_INT)this->lpDDSTarget)->lpLcl->hDDSurface);

    D3D_INFO(6, "in halCreateContext. Succeeded. dwhContext = %d", lpData->dwhContext);

    return (D3D_OK);
}
#endif  //WIN95

void halDestroyContext(LPDIRECT3DDEVICEI lpDevI)
{
    D3DHAL_CONTEXTDESTROYDATA data;
    HRESULT ret;

    D3D_INFO(6, "in halCreateDestroy. Destroying Context for driver = %08lx", lpDevI);
    D3D_INFO(6, "                     dwhContext = %d", lpDevI->dwhContext);

    if(lpDevI->dwhContext!=NULL) {
        memset(&data, 0, sizeof(D3DHAL_CONTEXTDESTROYDATA));
        data.dwhContext = lpDevI->dwhContext;

        CALL_HALONLY(ret, lpDevI, ContextDestroy, &data);
        if (ret != DDHAL_DRIVER_HANDLED || data.ddrval != DD_OK) {
            D3D_WARN(0,"Failed ContextDestroy HAL call in halDestroyContext");
            return;
        }
    }
}
//---------------------------------------------------------------------
HRESULT D3DFE_Create(LPDIRECT3DDEVICEI lpDevI,
                     LPDIRECTDRAW lpDD,
                     LPDIRECTDRAW7 lpDD7,
                     LPDIRECTDRAWSURFACE lpDDS,
                     LPDIRECTDRAWSURFACE lpZ,
                     LPDIRECTDRAWPALETTE lpPal)
{
    DDSURFACEDESC ddsd;
    HRESULT hr;
    LPD3DHAL_GLOBALDRIVERDATA lpGlob;

    /*
     * Allocate and check validity of DirectDraw surfaces
     */

    lpDevI->lpDD = lpDD;
    lpDevI->lpDDGbl = ((LPDDRAWI_DIRECTDRAW_INT)lpDD)->lpLcl->lpGbl;
    lpDevI->lpDDSTarget = lpDDS;

    // Get DDS7 Interfaces for RenderTarget/ZBuffer

    HRESULT ret = lpDDS->QueryInterface(IID_IDirectDrawSurface7, (LPVOID*)&lpDevI->lpDDSTarget_DDS7);

    if(FAILED(ret)) {
          D3D_ERR("QI for RenderTarget DDS7 Interface failed ");
          return ret;
    }

    if(lpZ!=NULL) {
        ret = lpZ->QueryInterface(IID_IDirectDrawSurface7, (LPVOID*)&lpDevI->lpDDSZBuffer_DDS7);

        if(FAILED(ret)) {
              D3D_ERR("QI for ZBuffer DDS7 Interface failed ");

              return ret;
        }
        lpDevI->lpDDSZBuffer_DDS7->Release();
    }

    LPD3DHAL_D3DEXTENDEDCAPS lpCaps = lpDevI->lpD3DExtendedCaps;
    if (NULL == lpCaps || 0.0f == lpCaps->dvExtentsAdjust)
    {
        lpDevI->dvExtentsAdjust = 1.0f;
    }
    else
    {
        lpDevI->dvExtentsAdjust = lpCaps->dvExtentsAdjust;
    }
    lpDevI->dwClipMaskOffScreen = 0xFFFFFFFF;
    if (lpCaps != NULL)
    {
        if (lpCaps->dvGuardBandLeft   != 0.0f ||
            lpCaps->dvGuardBandRight  != 0.0f ||
            lpCaps->dvGuardBandTop    != 0.0f ||
            lpCaps->dvGuardBandBottom != 0.0f)
        {
            lpDevI->dwDeviceFlags |= D3DDEV_GUARDBAND;
            lpDevI->dwClipMaskOffScreen = ~__D3DCLIP_INGUARDBAND;
            DWORD v;
            if (GetD3DRegValue(REG_DWORD, "DisableGB", &v, 4) &&
                v != 0)
            {
                lpDevI->dwDeviceFlags &= ~D3DDEV_GUARDBAND;
                lpDevI->dwClipMaskOffScreen = 0xFFFFFFFF;
            }
#if DBG
            // Try to get test values for the guard band
            char value[80];
            if (GetD3DRegValue(REG_SZ, "GuardBandLeft", &value, 80) &&
                value[0] != 0)
                sscanf(value, "%f", &lpCaps->dvGuardBandLeft);
            if (GetD3DRegValue(REG_SZ, "GuardBandRight", &value, 80) &&
                value[0] != 0)
                sscanf(value, "%f", &lpCaps->dvGuardBandRight);
            if (GetD3DRegValue(REG_SZ, "GuardBandTop", &value, 80) &&
                value[0] != 0)
                sscanf(value, "%f", &lpCaps->dvGuardBandTop);
            if (GetD3DRegValue(REG_SZ, "GuardBandBottom", &value, 80) &&
                value[0] != 0)
                sscanf(value, "%f", &lpCaps->dvGuardBandBottom);
#endif // DBG
        }
    }

    if (!lpDevI->lpD3DHALCallbacks || ! lpDevI->lpD3DHALGlobalDriverData)
    {
        return DDERR_INVALIDOBJECT;
    }

    if (IS_HW_DEVICE(lpDevI))
    {
        // We do texture management (and hence clipped Blts) only for a real HAL.
        hr = lpDD7->CreateClipper(0, &lpDevI->lpClipper, NULL);
        if(hr != DD_OK)
        {
            D3D_ERR("Failed to create a clipper");
            return hr;
        }
    }
    else
    {
        lpDevI->lpClipper = 0;
    }

    lpGlob = lpDevI->lpD3DHALGlobalDriverData;

    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
    ddsd.dwSize = sizeof(DDSURFACEDESC);
    if (lpZ)
    {
        if ((hr = lpZ->GetSurfaceDesc(&ddsd)) != DD_OK)
        {
            D3D_ERR("Failed to getsurfacedesc on z");
            return hr;
        }
        if (ddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
        {
            D3D_INFO(1, "Z buffer is in system memory.");
        }
        else if (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)
        {
            D3D_INFO(1, "Z buffer is in video memory.");
        }
        else
        {
            D3D_ERR("Z buffer not in video or system?");
        }
    }
    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
    ddsd.dwSize = sizeof(DDSURFACEDESC);
    if (lpDDS)
    {
        if ((hr = lpDDS->GetSurfaceDesc(&ddsd)) != DD_OK)
        {
            D3D_ERR("Failed to getsurfacedesc on back buffer");
            return hr;
        }
        if (ddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
        {
            D3D_INFO(1, "back buffer is in system memory.");
        }
        else if (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)
        {
            D3D_INFO(1, "back buffer is in video memory.");
        }
        else
        {
            D3D_ERR("back buffer not in video or system?");
        }
        if (!(lpGlob->hwCaps.dwDeviceRenderBitDepth &
              BitDepthToDDBD(ddsd.ddpfPixelFormat.dwRGBBitCount)))
        {
            D3D_ERR("Rendering surface's RGB bit count not supported "
                    "by hardware device");
            return DDERR_INVALIDOBJECT;
        }
    }

    if (lpGlob->dwNumVertices
        && lpGlob->dwNumClipVertices < D3DHAL_NUMCLIPVERTICES)
    {
        D3D_ERR("In global driver data, dwNumClipVertices "
                "< D3DHAL_NUMCLIPVERTICES");
        lpGlob->dwNumClipVertices = D3DHAL_NUMCLIPVERTICES;
    }

    if ((hr = CalcDDSurfInfo(lpDevI,TRUE)) != DD_OK)
    {
        return hr;
    }

    RESET_HAL_CALLS(lpDevI);

    /*
     * Create our context in the HAL driver
     */
    if ((hr = lpDevI->halCreateContext()) != D3D_OK)
    {
        return hr;
    }
// Initialize the transform and lighting state
    D3DMATRIXI m;
    setIdentity(&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_VIEW, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_PROJECTION, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_WORLD,  (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_WORLD1, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_WORLD2, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_WORLD3, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_TEXTURE0, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_TEXTURE1, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_TEXTURE2, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_TEXTURE3, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_TEXTURE4, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_TEXTURE5, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_TEXTURE6, (D3DMATRIX*)&m);
    lpDevI->SetTransformI(D3DTRANSFORMSTATE_TEXTURE7, (D3DMATRIX*)&m);

    LIST_INITIALIZE(&lpDevI->specular_tables);
    lpDevI->specular_table = NULL;

    lpDevI->lightVertexFuncTable = &lightVertexTable;
    lpDevI->lighting.activeLights = NULL;

    lpDevI->iClipStatus = D3DSTATUS_DEFAULT;
    lpDevI->rExtents.x1 = D3DVAL(2048);
    lpDevI->rExtents.x2 = D3DVAL(0);
    lpDevI->rExtents.y1 = D3DVAL(2048);
    lpDevI->rExtents.y2 = D3DVAL(0);

    return S_OK;
}

void D3DFE_Destroy(LPDIRECT3DDEVICEI lpDevI)
{
// Destroy lighting data

    SpecularTable *spec;
    SpecularTable *spec_next;

    for (spec = LIST_FIRST(&lpDevI->specular_tables); spec; spec = spec_next)
    {
        spec_next = LIST_NEXT(spec,list);
        D3DFree(spec);
    }

    if(lpDevI->lpClipper)
    {
        lpDevI->lpClipper->Release();
    }

    delete [] lpDevI->m_pLights;

    if (lpDevI->lpD3DHALCallbacks) {
        halDestroyContext(lpDevI);
    }

#ifdef TRACK_HAL_CALLS
    D3D_INFO(0, "Made %d HAL calls", lpDevI->hal_calls);
#endif
}

void BltFillRects(LPDIRECT3DDEVICEI lpDevI, DWORD count, LPD3DRECT rect, D3DCOLOR dwFillColor)
{
    LPDIRECTDRAWSURFACE lpDDS = lpDevI->lpDDSTarget;
    HRESULT ddrval;
    DDBLTFX bltfx;
    RECT tr;
    DWORD i;
    DWORD r, g, b, a;

    // Fill with background color

    memset(&bltfx, 0, sizeof(bltfx));
    bltfx.dwSize = sizeof(bltfx);

// unlike clear callback, which just takes pure 32-bit ARGB word and forces the driver to scale it for
// the pixelformat, here we need to compute the exact fill word, depending on surface's R,G,B bitmasks

    if(lpDevI->bDDSTargetIsPalettized)
    {
         // map 24-bit color to 8-bit index used by 8bit RGB rasterizer
         CallRastService(lpDevI, RAST_SERVICE_RGB8COLORTOPIXEL, dwFillColor, &bltfx.dwFillColor);
    }
    else
    {
        DDASSERT((lpDevI->red_scale!=0)&&(lpDevI->green_scale!=0)&&(lpDevI->blue_scale!=0));
        r = RGB_GETRED(dwFillColor) / lpDevI->red_scale;
        g = RGB_GETGREEN(dwFillColor) / lpDevI->green_scale;
        b = RGB_GETBLUE(dwFillColor) / lpDevI->blue_scale;
        bltfx.dwFillColor = (r << lpDevI->red_shift) | (g << lpDevI->green_shift) | (b << lpDevI->blue_shift);
        if( lpDevI->alpha_scale!=0 )
        {
            a = RGBA_GETALPHA(dwFillColor) / lpDevI->alpha_scale;
            bltfx.dwFillColor |= (a << lpDevI->alpha_shift);
        }
    }

    for (i = 0; i < count; i++,rect++) {
        tr.left = rect->x1;
        tr.right = rect->x2;
        tr.top = rect->y1;
        tr.bottom = rect->y2;
        do {
            ddrval = lpDDS->Blt(&tr, NULL, NULL, DDBLT_COLORFILL, &bltfx);
        } while (ddrval == DDERR_WASSTILLDRAWING);
    }
}

void BltFillZRects(LPDIRECT3DDEVICEI lpDevI, unsigned long Zpixel,
                    DWORD count, LPD3DRECT rect, DWORD dwWriteMask)
{
    HRESULT ddrval;
    DDBLTFX bltfx;
    DWORD i;
    RECT tr;
    DWORD dwExtraFlags=0;

#if DBG
    if (lpDevI->lpDDSZBuffer == NULL)  // should be checked prior to call
        return;
#endif

    memset(&bltfx, 0, sizeof(DDBLTFX));
    bltfx.dwSize = sizeof(DDBLTFX);
    bltfx.dwFillDepth = Zpixel;

    // hack to pass DepthBlt WriteMask through ddraw/ddhel to blitlib
    if(dwWriteMask!=0) {
        bltfx.dwZDestConstBitDepth=dwWriteMask;
        dwExtraFlags = DDBLT_DEPTHFILLWRITEMASK;
    }

    for(i=0;i<count;i++,rect++) {
        D3D_INFO(4, "Z Clearing x1 = %d, y1 = %d, x2 = %d, y2 = %d, WriteMask %X", rect->x1, rect->y1, rect->x2, rect->y2, bltfx.dwReserved);
        tr.left = rect->x1;
        tr.right = rect->x2;
        tr.top = rect->y1;
        tr.bottom = rect->y2;
        do {
            ddrval = lpDevI->lpDDSZBuffer->Blt(&tr, NULL, NULL, DDBLT_DEPTHFILL | dwExtraFlags, &bltfx);
        } while (ddrval == DDERR_WASSTILLDRAWING);
    }
}

//---------------------------------------------------------------------
struct CHandle
{
    DWORD   m_Next;     // Used to make list of free handles
#if DBG
    DWORD   m_Tag;      // 1 - empty; 2 = taken
#endif
};

CHandleFactory::~CHandleFactory()
{
    if (m_Handles)
        delete m_Handles;
}

HRESULT CHandleFactory::Init(DWORD dwInitialSize, DWORD dwGrowSize)
{
    m_Handles = CreateHandleArray(dwInitialSize);
    if (m_Handles == NULL)
        return DDERR_OUTOFMEMORY;
    m_dwArraySize = dwInitialSize;
    m_dwGrowSize = dwGrowSize;
    m_Free = 0;
    return D3D_OK;
}

DWORD CHandleFactory::CreateNewHandle()
{
    DWORD handle = m_Free;
    if (m_Free != __INVALIDHANDLE)
    {
        m_Free = m_Handles[m_Free].m_Next;
    }
    else
    {
        handle = m_dwArraySize;
        m_Free = m_dwArraySize + 1;
        m_dwArraySize += m_dwGrowSize;
        CHandle * newHandles = CreateHandleArray(m_dwArraySize);
#if DBG
        memcpy(newHandles, m_Handles,
               (m_dwArraySize - m_dwGrowSize)*sizeof(CHandle));
#endif
        delete m_Handles;
        m_Handles = newHandles;
    }
    DDASSERT(m_Handles[handle].m_Tag == 1);
#if DBG
    m_Handles[handle].m_Tag = 2;    // Mark as taken
#endif
    return handle;
}

void CHandleFactory::ReleaseHandle(DWORD handle)
{
    DDASSERT(handle < m_dwArraySize);
    DDASSERT(m_Handles[handle].m_Tag == 2);
#if DBG
    m_Handles[handle].m_Tag = 1;    // Mark as empty
#endif

    m_Handles[handle].m_Next = m_Free;
    m_Free = handle;
}

CHandle* CHandleFactory::CreateHandleArray(DWORD dwSize)
{
    CHandle *handles = new CHandle[dwSize];
    DDASSERT(handles != NULL);
    if ( NULL == handles ) return NULL;
    for (DWORD i=0; i < dwSize; i++)
    {
        handles[i].m_Next = i+1;
#if DBG
        handles[i].m_Tag = 1;   // Mark as empty
#endif
    }
    handles[dwSize-1].m_Next = __INVALIDHANDLE;
    return handles;
}