Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

#define INITGUID

#include "DDInterface.h"
#include "DDImage.h"
#include "D3DInterface.h"
#include "D3DTester.h"
#include "SexyAppBase.h"
#include "AutoCrit.h"
#include "Graphics.h"
#include "PerfTimer.h"
#include "Debug.h"
#include "DirectXErrorString.h"
#include "MemoryImage.h"

using namespace Sexy;

typedef HRESULT (WINAPI *DirectDrawCreateFunc)(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter);
typedef HRESULT (WINAPI *DirectDrawCreateExFunc)(GUID FAR *lpGUID, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter);

extern HMODULE gDDrawDLL;
static DirectDrawCreateFunc gDirectDrawCreateFunc = NULL;
static DirectDrawCreateExFunc gDirectDrawCreateExFunc = NULL;

DDInterface::DDInterface(SexyAppBase* theApp)
{
        mApp = theApp;
        mPrimarySurface = NULL;
        mDrawSurface = NULL;
        mSecondarySurface = NULL;
        mScreenImage = NULL;
        mDD = NULL;
        mDD7 = NULL;
        mRedAddTable = NULL;
        mGreenAddTable = NULL;
        mBlueAddTable = NULL;
        mInitialized = false;
        mVideoOnlyDraw = false;
        mScanLineFailCount = 0;

        //TODO: Standards, anyone?
        mNextCursorX = 0;
        mNextCursorY = 0;
        mCursorX = 0;
        mCursorY = 0;
        mInRedraw = false;
        //mCursorWidth = 54;
        //mCursorHeight = 54;
        mCursorWidth = 64;
        mCursorHeight = 64;
        mCursorImage = NULL;
        mOldCursorArea = NULL;
        mNewCursorArea = NULL;
        mHasOldCursorArea = false;
        mOldCursorArea = NULL;
        mNewCursorArea = NULL;
        mNewCursorAreaImage = NULL;
        mOldCursorAreaImage = NULL;
        mInitCount = 0;
        mRefreshRate = 60;
        mMillisecondsPerFrame = 1000/mRefreshRate;

        mD3DInterface = new D3DInterface;
        mIs3D = false;

        mD3DTester = NULL;

        gDirectDrawCreateFunc = (DirectDrawCreateFunc)GetProcAddress(gDDrawDLL,"DirectDrawCreate");
        gDirectDrawCreateExFunc = (DirectDrawCreateExFunc)GetProcAddress(gDDrawDLL,"DirectDrawCreateEx");
}

DDInterface::~DDInterface()
{
        delete [] mRedAddTable;
        delete [] mGreenAddTable;
        delete [] mBlueAddTable;

        Cleanup();

        delete mD3DInterface;
        delete mD3DTester;
}

std::string DDInterface::ResultToString(int theResult)
{
        switch (theResult)
        {
        case RESULT_OK:
                return "RESULT_OK";
        case RESULT_FAIL:
                return "RESULT_FAIL";
        case RESULT_DD_CREATE_FAIL:
                return "RESULT_DD_CREATE_FAIL";
        case RESULT_SURFACE_FAIL:
                return "RESULT_SURFACE_FAIL";
        case RESULT_EXCLUSIVE_FAIL:
                return "RESULT_EXCLUSIVE_FAIL";
        case RESULT_DISPCHANGE_FAIL:
                return "RESULT_DISPCHANGE_FAIL";               
        case RESULT_INVALID_COLORDEPTH:
                return "RESULT_INVALID_COLORDEPTH";
        default:
                return "RESULT_UNKNOWN";
        }
}

bool DDInterface::GotDXError(HRESULT theResult, const char *theContext)
{
        if (!SUCCEEDED(theResult))
        {
                std::string anError = GetDirectXErrorString(theResult);
                mErrorString = StrFormat("%s: %s",theContext, anError.c_str());

                return true;
        }
        else
                return false;
}


DDImage* DDInterface::GetScreenImage()
{
        return mScreenImage;
}

HRESULT DDInterface::CreateSurface(DDSURFACEDESC2 *theDesc, LPDIRECTDRAWSURFACE *theSurface, void*)
{
        AutoCrit aCrit(mCritSect);
        HRESULT aResult;
        if (mDD7 != NULL)
        {
                LPDIRECTDRAWSURFACE7 aSurface;
                aResult = mDD7->CreateSurface(theDesc, &aSurface, NULL);
                if (!SUCCEEDED(aResult))
                        return aResult;

                aResult = aSurface->QueryInterface(IID_IDirectDrawSurface, (LPVOID*)theSurface);
                aSurface->Release();

                if (!SUCCEEDED(aResult))
                        return aResult;
        }
        else
        {
                DDSURFACEDESC aDesc;
                ZeroMemory(&aDesc,sizeof(aDesc));
                aDesc.dwSize = sizeof(aDesc);
                aDesc.dwFlags = theDesc->dwFlags;
                aDesc.dwHeight = theDesc->dwHeight;
                aDesc.dwWidth = theDesc->dwWidth;
                aDesc.lPitch = theDesc->lPitch;
                aDesc.dwBackBufferCount = theDesc->dwBackBufferCount;
                aDesc.dwRefreshRate = theDesc->dwRefreshRate;
                aDesc.dwAlphaBitDepth = theDesc->dwAlphaBitDepth;
                aDesc.dwReserved = theDesc->dwReserved;
                aDesc.lpSurface = theDesc->lpSurface;
            aDesc.ddpfPixelFormat = theDesc->ddpfPixelFormat;        
                aDesc.ddsCaps.dwCaps = theDesc->ddsCaps.dwCaps;                

                aResult = mDD->CreateSurface(&aDesc, theSurface, NULL);
                if (!SUCCEEDED(aResult))
                        return aResult;
        }

        // Make sure it's 32-bit or 16-bit
        DDSURFACEDESC aDesc;
        ZeroMemory(&aDesc, sizeof(aDesc));
        aDesc.dwSize = sizeof(aDesc);
        aDesc.dwFlags = DDSD_PIXELFORMAT;
        mPrimarySurface->GetSurfaceDesc(&aDesc);
#ifdef OPTIMIZE_SOFTWARE_DRAWING
        // If things are stored blue low, green middle, red high, we can optimize a lot of our software rendering based on bit patterns.
        // This of course does not matter for native data which is already in the correct order (and can be optimized similarly).
        gOptimizeSoftwareDrawing = aDesc.ddpfPixelFormat.dwBBitMask < aDesc.ddpfPixelFormat.dwGBitMask && aDesc.ddpfPixelFormat.dwGBitMask < aDesc.ddpfPixelFormat.dwRBitMask;
#endif
        int aNumBits = aDesc.ddpfPixelFormat.dwRGBBitCount;
        if (aNumBits!=16 && aNumBits!=32)
        {
                (*theSurface)->Release();
                *theSurface = NULL;
                return DDERR_INVALIDPIXELFORMAT;
        }

        return aResult;
}

void DDInterface::ClearSurface(LPDIRECTDRAWSURFACE theSurface)
{
        if (theSurface)
        {
                DDSURFACEDESC desc;
                memset(&desc, 0, sizeof desc);
                desc.dwSize = sizeof desc;
                HRESULT hr = theSurface->Lock(NULL, &desc, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL);
                if ( DD_OK == hr )
                {
                        DWORD pixelSize = desc.ddpfPixelFormat.dwRGBBitCount / 8;
                        unsigned char* p = (unsigned char*)desc.lpSurface;
                        for ( DWORD row = 0; row < desc.dwHeight; ++row )
                        {
                                memset(p, 0, pixelSize*desc.dwWidth);
                                p += desc.lPitch;
                        }
                        theSurface->Unlock(NULL);
                }
        }
}

bool DDInterface::Do3DTest(HWND theHWND)
{
        if (mD3DTester == NULL)
        {
                if (mApp->mTest3D || mApp->mAutoEnable3D)
                {
                        mD3DTester = new D3DTester;
                        mD3DTester->SetVidMemoryConstraints(mApp->mMinVidMemory3D, mApp->mRecommendedVidMemory3D);
                        mD3DTester->TestD3D(theHWND, mDD7);

                        if (mApp->mAutoEnable3D && mD3DTester->Is3DRecommended())
                                mIs3D = true;

                        if (!mD3DTester->Is3DSupported())
                                mIs3D = false;

                        if (mD3DTester->ResultsChanged() && !mD3DTester->Is3DRecommended())
                                mIs3D = false;

                        return true;
                }
        }

        return false;
}

int DDInterface::Init(HWND theWindow, bool IsWindowed)
{
        AutoCrit anAutoCrit(mCritSect);

        mInitialized = false;

        /*DDImageSet aReleasedImageSet;

        DDImageSet::iterator anItr = mDDImageSet.begin();
        while (anItr != mDDImageSet.end())
        {
                DDImage* aDDImage = *anItr;

                if ((aDDImage->mSurface != NULL) || aDDImage->mWantDDSurface || !aDDImage->mKeepBits)
                {
                        aDDImage->DeleteDDSurface();
                        aReleasedImageSet.insert(aDDImage);
                }

                ++anItr;
        }*/


        Cleanup();

        HRESULT aResult;
        DDSURFACEDESC2 aDesc;

        if (gDirectDrawCreateExFunc != NULL)
        {
                aResult = gDirectDrawCreateExFunc(NULL, (LPVOID*)&mDD7, IID_IDirectDraw7, NULL);
                if (GotDXError(aResult, "DirectDrawCreateEx"))
                        return RESULT_DD_CREATE_FAIL;

                aResult = mDD7->QueryInterface(IID_IDirectDraw, (LPVOID*)&mDD);
                if (GotDXError(aResult, "QueryrInterface IID_IDirectDraw"))
                        return RESULT_DD_CREATE_FAIL;          
        }
        else
        {
                aResult = gDirectDrawCreateFunc(NULL, &mDD, NULL);             
        }

        if (Do3DTest(theWindow))
        {
                if (mD3DTester->ResultsChanged())
                        mApp->Done3dTesting();
        }

        RECT aRect;
        GetClientRect(theWindow, &aRect);

        mHWnd = theWindow;
        mWidth = mApp->mWidth;
        mHeight = mApp->mHeight;
        mAspect.Set(mWidth, mHeight);
        mDesktopWidth = GetSystemMetrics(SM_CXSCREEN);
        mDesktopHeight = GetSystemMetrics(SM_CYSCREEN);
        mDesktopAspect.Set(mDesktopWidth, mDesktopHeight);
        mDisplayWidth = mWidth;
        mDisplayHeight = mHeight;
        mDisplayAspect = mAspect;
        mPresentationRect = Rect( 0, 0, mWidth, mHeight );
        mApp->mScreenBounds = mPresentationRect;
        mFullscreenBits = mApp->mFullscreenBits;
        mIsWindowed = IsWindowed;
        mHasOldCursorArea = false;

        OutputDebug(_S("Application requests %4lu x %4lu [%2d:%2d]\n"), mWidth, mHeight, mAspect.mNumerator, mAspect.mDenominator);

        if (GotDXError(aResult, "DirectDrawCreate"))
                return RESULT_DD_CREATE_FAIL;  
       
        if (IsWindowed)
        {
                OutputDebug(_S("Hack aspect is                   [%2d:%2d]\n"), mApp->mWindowAspect.mNumerator, mApp->mWindowAspect.mDenominator);

                if ( mApp->mEnableWindowAspect && mAspect < mApp->mWindowAspect )
                {
                        mIsWidescreen = true;

                        // Setup the window at the hack aspect,
                        // but with the height requested by the application.
                        mDisplayWidth = mHeight * mApp->mWindowAspect;
                        mDisplayHeight = mHeight;
                        mDisplayAspect = mApp->mWindowAspect;

                        // resize the window.
                        RECT rc;
                        POINT pt;
                        WINDOWINFO info;
                        ::GetWindowInfo(theWindow, &info);
                        ::GetClientRect(theWindow, &rc);
                        pt.x = rc.left;
                        pt.y = rc.top;
                        ::ClientToScreen(theWindow, &pt);
                        rc.left = pt.x - (mDisplayWidth - mWidth) / 2;
                        rc.top = pt.y - (mDisplayHeight - mHeight) / 2;
                        rc.right = rc.left + mDisplayWidth;
                        rc.bottom = rc.top + mDisplayHeight;
                        ::AdjustWindowRectEx(&rc, info.dwStyle, false, info.dwExStyle);
                        ::MoveWindow(theWindow, max(0, rc.left), max(0, rc.top), rc.right-rc.left, rc.bottom-rc.top, false);

                        if ( mApp->mWidescreenAware )
                        {
                                mWidth = mDisplayWidth;
                                mHeight = mDisplayHeight;
                                mAspect = mDisplayAspect;

                                mPresentationRect.mWidth = mDisplayWidth;
                                mPresentationRect.mHeight = mDisplayHeight;
                                mPresentationRect.mX = 0;
                                mPresentationRect.mY = 0;
                        }
                        else
                        {
                                // Set the dest rect for drawing the back buffer to the center of
                                // the wide display.
                                mPresentationRect.mWidth = mWidth;
                                mPresentationRect.mHeight = mHeight;
                                mPresentationRect.mX = ( mDisplayWidth - mPresentationRect.mWidth ) / 2;
                                mPresentationRect.mY = 0;
                        }
                }

                OutputDebug(_S("Window is            %4lu x %4lu [%2d:%2d]\n"), mDisplayWidth, mDisplayHeight, mDisplayAspect.mNumerator, mDisplayAspect.mDenominator);

                aResult = mDD->SetCooperativeLevel(theWindow, DDSCL_NORMAL);

                ZeroMemory(&aDesc, sizeof(aDesc));
                aDesc.dwSize = sizeof(aDesc);
                aDesc.dwFlags = DDSD_CAPS;
   
                aDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
                if (mIs3D)
                        aDesc.ddsCaps.dwCaps |= DDSCAPS_3DDEVICE;
               
                aDesc.dwBackBufferCount = 1;
                aResult = CreateSurface(&aDesc, &mPrimarySurface, NULL);
                if (aResult==DDERR_INVALIDPIXELFORMAT) // check for non 16 or 32 bit primary surface
                        return RESULT_INVALID_COLORDEPTH;
                else if (GotDXError(aResult, "CreateSurface Windowed Primary"))
                        return RESULT_SURFACE_FAIL;                            

                ZeroMemory(&aDesc, sizeof(aDesc));
                aDesc.dwSize = sizeof(aDesc);
                aDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
                aDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
                if (mIs3D)
                        aDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_3DDEVICE;

                aDesc.dwWidth = mWidth;
                aDesc.dwHeight = mHeight;

                aResult = CreateSurface(&aDesc, &mDrawSurface, NULL);
                if (GotDXError(aResult,"CreateSurface Windowed DrawSurface"))
                        return RESULT_SURFACE_FAIL;

                IDirectDrawClipper* aClipper;
                aResult = mDD->CreateClipper(0, &aClipper, NULL);
                if (GotDXError(aResult,"CreateClipper Windowed")) return RESULT_FAIL;

                // Associate the clipper with the window
                aResult = aClipper->SetHWnd(0, theWindow);
                if (SUCCEEDED(aResult))
                        aResult = mPrimarySurface->SetClipper(aClipper);

                aClipper->Release();
        }
        else
        {
                OutputDebug(_S("Desktop is           %4lu x %4lu [%2d:%2d]\n"), mDesktopWidth, mDesktopHeight, mDesktopAspect.mNumerator, mDesktopAspect.mDenominator);

                if ( mIs3D && mAspect < mDesktopAspect )
                {
                        mIsWidescreen = true;

                        // Set the display mode to the size of the desktop.
                        mDisplayWidth = mDesktopWidth;
                        mDisplayHeight = mDesktopHeight;
                        mDisplayAspect = mDesktopAspect;

                        if ( mApp->mWidescreenAware )
                        {
                                // Setup the draw buffer(s) at the same aspect ratio as the desktop,
                                // but with the height requested by the application.
                                mAspect = mDisplayAspect;
                                mWidth = mHeight * mAspect;

                                mPresentationRect.mWidth = mDisplayWidth;
                                mPresentationRect.mHeight = mDisplayHeight;
                                mPresentationRect.mX = 0;
                                mPresentationRect.mY = 0;
                        }
                        else
                        {
                                // Set the dest rect for drawing the back buffer to the center of
                                // the wide display.
                                mPresentationRect.mWidth = mWidth * mDisplayHeight / mHeight;
                                mPresentationRect.mHeight = mDisplayHeight;
                                mPresentationRect.mX = ( mDisplayWidth - mPresentationRect.mWidth ) / 2;
                                mPresentationRect.mY = 0;
                        }
                }

                OutputDebug(_S("Display is           %4lu x %4lu [%2d:%2d]\n"), mDisplayWidth, mDisplayHeight, mDisplayAspect.mNumerator, mDisplayAspect.mDenominator);

                aResult = mDD->SetCooperativeLevel(theWindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
                if (GotDXError(aResult,"SetCooperativeLevel FullScreen"))
                        return RESULT_EXCLUSIVE_FAIL;

                aResult = mDD->SetDisplayMode(mDisplayWidth, mDisplayHeight, mFullscreenBits);

                if (GotDXError(aResult,"SetDisplayMode FullScreen"))
                        return RESULT_DISPCHANGE_FAIL;

                ZeroMemory(&aDesc, sizeof(aDesc));
                aDesc.dwSize = sizeof(aDesc);
                aDesc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
                aDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
                                                          DDSCAPS_FLIP |
                                                          DDSCAPS_COMPLEX/* |
                                                          ,DDSCAPS_FRONTBUFFER*/
;
                aDesc.dwBackBufferCount = 1;

                if (mIs3D)
                        aDesc.ddsCaps.dwCaps |= DDSCAPS_3DDEVICE;


                aResult = CreateSurface(&aDesc, &mPrimarySurface, NULL);
                if (GotDXError(aResult,"CreateSurface FullScreen Primary"))
                        return RESULT_SURFACE_FAIL;

                DDSCAPS aCaps;         
                ZeroMemory(&aCaps,sizeof(aCaps));
                aCaps.dwCaps = DDSCAPS_BACKBUFFER;
                aResult = mPrimarySurface->GetAttachedSurface(&aCaps, &mSecondarySurface);

                if (GotDXError(aResult,"GetAttachedSurface"))
                        return RESULT_SURFACE_FAIL;

                if (mIsWidescreen)
                {
                        ClearSurface(mPrimarySurface);
                        ClearSurface(mSecondarySurface);
                }

                ZeroMemory(&aDesc, sizeof(aDesc));
                aDesc.dwSize = sizeof(aDesc);
                aDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
                aDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
                if (mIs3D)
                        aDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_3DDEVICE;

                aDesc.dwWidth = mWidth;
                aDesc.dwHeight = mHeight;              

                aResult = CreateSurface(&aDesc, &mDrawSurface, NULL);
                if (GotDXError(aResult, "CreateSurface FullScreen DrawSurface"))
                        return RESULT_SURFACE_FAIL;
               
                IDirectDrawClipper* aClipper;
                aResult = mDD->CreateClipper(0, &aClipper, NULL);
                if (GotDXError(aResult,"CreateClipper FullScreen")) return RESULT_FAIL;

                // Associate the clipper with the window
                aResult = aClipper->SetHWnd(0, theWindow);
                if (SUCCEEDED(aResult))
                        aResult = mPrimarySurface->SetClipper(aClipper);

                aClipper->Release();

                if (!mApp->mFullScreenPageFlip)
                        mDD->FlipToGDISurface();
        }
       
        OutputDebug(_S("Draw buffer is       %4lu x %4lu [%2d:%2d]\n"), mWidth, mHeight, mAspect.mNumerator, mAspect.mDenominator);

        if (FAILED(mDD->GetMonitorFrequency(&mRefreshRate)) || mRefreshRate<60)
        {
                mApp->mVSyncBroken = true;
                mRefreshRate = 60;
        }

        if (mRefreshRate < 60)
        {
                mApp->mVSyncBroken = true;
                mRefreshRate = 60;             
        }
        else if (mRefreshRate > 100) // We must have at least 1 Update per UpdateF for demo compatibility
        {
                mApp->mVSyncBroken = true;
                mRefreshRate = 100;
        }

        mMillisecondsPerFrame = 1000/mRefreshRate;

        // Create the images needed for cursor stuff
        ZeroMemory(&aDesc, sizeof(aDesc));
        aDesc.dwSize = sizeof(aDesc);
        aDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
        aDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
        aDesc.dwWidth = mCursorWidth;
        aDesc.dwHeight = mCursorHeight;

        aResult = CreateSurface(&aDesc, &mOldCursorArea, NULL);
        if (GotDXError(aResult, "CreateSurface OldCursorArea"))
                return RESULT_SURFACE_FAIL;
       
        aResult = CreateSurface(&aDesc, &mNewCursorArea, NULL);
        if (GotDXError(aResult, "CreateSurface NewCursorArea"))
                return RESULT_SURFACE_FAIL;

        mOldCursorAreaImage = new DDImage(this);
        mOldCursorAreaImage->SetSurface(mOldCursorArea);
        mOldCursorAreaImage->SetImageMode(false, false);

        mNewCursorAreaImage = new DDImage(this);
        mNewCursorAreaImage->SetSurface(mNewCursorArea);       
        mNewCursorAreaImage->SetImageMode(false, false);

        // Get data from the primary surface
        if (mPrimarySurface != NULL)
        {
                DDSURFACEDESC aDesc;

                ZeroMemory(&aDesc, sizeof(aDesc));
                aDesc.dwSize = sizeof(aDesc);
                aDesc.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
                HRESULT aResult = mPrimarySurface->GetSurfaceDesc(&aDesc);

                if ((aDesc.ddpfPixelFormat.dwRGBBitCount != 16) &&
                        (aDesc.ddpfPixelFormat.dwRGBBitCount != 32))
                        return RESULT_INVALID_COLORDEPTH;

                mRGBBits = aDesc.ddpfPixelFormat.dwRGBBitCount;
                mRedMask = aDesc.ddpfPixelFormat.dwRBitMask;
                mGreenMask = aDesc.ddpfPixelFormat.dwGBitMask;
                mBlueMask = aDesc.ddpfPixelFormat.dwBBitMask;

                int i;
                for (i = 32; i >= 0; i--)
                {
                        if (((mRedMask >> i) & 1) != 0)
                                mRedShift = i;
                        if (((mGreenMask >> i) & 1) != 0)
                                mGreenShift = i;
                        if (((mBlueMask >> i) & 1) != 0)
                                mBlueShift = i;                                        
                }

                for (i = 0; i < 32; i++)
                {      
                        if ((i+mRedShift < 32) && ((mRedMask >> (i+mRedShift)) != 0))
                                mRedBits = i+1;
                        if ((i+mGreenShift < 32) && ((mGreenMask >> (i+mGreenShift)) != 0))
                                mGreenBits = i+1;
                        if ((i+mBlueShift < 32) && ((mBlueMask >> (i+mBlueShift)) != 0))
                                mBlueBits = i+1;
                }

                delete [] mRedAddTable;
                delete [] mGreenAddTable;
                delete [] mBlueAddTable;               

                int aMaxR = (1<<mRedBits) - 1;
                int aMaxG = (1<<mGreenBits) - 1;
                int aMaxB = (1<<mBlueBits) - 1;

                mRedAddTable = new int[aMaxR*2+1];
                mGreenAddTable = new int[aMaxG*2+1];
                mBlueAddTable = new int[aMaxB*2+1];

                for (i = 0; i < aMaxR*2+1; i++)
                        mRedAddTable[i] = min(i, aMaxR);
                for (i = 0; i < aMaxG*2+1; i++)
                        mGreenAddTable[i] = min(i, aMaxG);
                for (i = 0; i < aMaxB*2+1; i++)
                        mBlueAddTable[i] = min(i, aMaxB);

                // Create the tables that we will use to convert from
                // internal color representation to surface representation
                for (i = 0; i < 256; i++)
                {
                        mRedConvTable[i] = ((i * mRedMask) / 255) & mRedMask;
                        mGreenConvTable[i] = ((i * mGreenMask) / 255) & mGreenMask;
                        mBlueConvTable[i] = ((i * mBlueMask) / 255) & mBlueMask;
                }
        }

        // Generate DDSurfaces for all our... image things
        /*anItr = aReleasedImageSet.begin();
        while (anItr != aReleasedImageSet.end())
        {
                DDImage* aDDImage = *anItr;

                DDImageSet::iterator aFindItr = mDDImageSet.find(aDDImage);
                if (aFindItr != mDDImageSet.end())
                {
                        if (mIs3D)
                        {
                                if (aDDImage->mWantPal)
                                        aDDImage->mWantPal = aDDImage->Palletize();
                        }
                        else
                        {
                                //if (aDDImage->mOnlyKeepDDSurface)
                                //      aDDImage->OnlyKeepDDSurface();
                                //else if (aDDImage->mWantDDSurface)
                                //      aDDImage->GenerateDDSurface();
                        }

                        aDDImage->GenerateExtraBuffers();
                       
                }

                ++anItr;
        }*/


        SetVideoOnlyDraw(mVideoOnlyDraw);
        if(mIs3D)
        {
                if(!mD3DInterface->InitFromDDInterface(this))
                {
                        mErrorString = "3D init error: ";
                        mErrorString += D3DInterface::mErrorString;
                        return RESULT_3D_FAIL;
                }
        }

        mInitCount++;
        mInitialized = true;

        return RESULT_OK;
}

void DDInterface::SetVideoOnlyDraw(bool videoOnlyDraw)
{
        AutoCrit anAutoCrit(mCritSect);

        mVideoOnlyDraw = videoOnlyDraw;

        if (mSecondarySurface == NULL)
        {
                DDSURFACEDESC2 aDesc;

                ZeroMemory(&aDesc, sizeof(aDesc));
                aDesc.dwSize = sizeof(aDesc);
                aDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
                aDesc.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
                aDesc.dwWidth = mWidth;
                aDesc.dwHeight = mHeight;

                HRESULT aResult = CreateSurface(&aDesc, &mSecondarySurface, NULL);

                if (FAILED(aResult))
                        mVideoOnlyDraw = false;
        }

        bool useSecondary = mVideoOnlyDraw;
        delete mScreenImage;
        mScreenImage = new DDImage(this);
        mScreenImage->SetSurface(useSecondary ? mSecondarySurface : mDrawSurface);             
        mScreenImage->mNoLock = mVideoOnlyDraw;
        mScreenImage->mVideoMemory = mVideoOnlyDraw;
        mScreenImage->SetImageMode(false, false);
}

void DDInterface::RemapMouse(int& theX, int& theY)
{
        if (mInitialized)
        {
                theX = ( theX - mPresentationRect.mX ) * mWidth / mPresentationRect.mWidth;
                theY = ( theY - mPresentationRect.mY ) * mHeight / mPresentationRect.mHeight;
        }
}

ulong DDInterface::GetColorRef(ulong theRGB)
{
        DDSURFACEDESC aDesc;

        ZeroMemory(&aDesc, sizeof(aDesc));
        aDesc.dwSize = sizeof(aDesc);
    aDesc.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
    HRESULT aResult = mPrimarySurface->GetSurfaceDesc(&aDesc);

        BYTE bRed   = (BYTE) ((theRGB >> 16) & 0xFF);
    BYTE bGreen = (BYTE) ((theRGB >>  8) & 0xFF);
    BYTE bBlue  = (BYTE) ((theRGB      ) & 0xFF);

        ulong aColor;
        aColor = ((DWORD(((LONGLONG)bRed * (LONGLONG)aDesc.ddpfPixelFormat.dwRBitMask) / 255) & aDesc.ddpfPixelFormat.dwRBitMask) |
                        (DWORD(((LONGLONG)bGreen * (LONGLONG)aDesc.ddpfPixelFormat.dwGBitMask) / 255) & aDesc.ddpfPixelFormat.dwGBitMask) |
                        (DWORD(((LONGLONG)bBlue * (LONGLONG)aDesc.ddpfPixelFormat.dwBBitMask) / 255) & aDesc.ddpfPixelFormat.dwBBitMask));

        return aColor;
}

void DDInterface::AddDDImage(DDImage* theDDImage)
{
        AutoCrit anAutoCrit(mCritSect);

        mDDImageSet.insert(theDDImage);
}

void DDInterface::RemoveDDImage(DDImage* theDDImage)
{
        AutoCrit anAutoCrit(mCritSect);

        DDImageSet::iterator anItr = mDDImageSet.find(theDDImage);
        if (anItr != mDDImageSet.end())
                mDDImageSet.erase(anItr);
}

void DDInterface::Remove3DData(MemoryImage* theImage) // for 3d texture cleanup
{
        mD3DInterface->RemoveMemoryImage(theImage);
}

void DDInterface::Cleanup()
{
        AutoCrit anAutoCrit(mCritSect);

        mInitialized = false;  
        mD3DInterface->Cleanup();

        if (mOldCursorAreaImage != NULL)
        {
                delete mOldCursorAreaImage;
                mOldCursorAreaImage = NULL;
        }

        if (mNewCursorAreaImage != NULL)
        {
                delete mNewCursorAreaImage;
                mNewCursorAreaImage = NULL;
        }

        if (mOldCursorArea != NULL)
        {
                mOldCursorArea->Release();
                mOldCursorArea = NULL;
        }

        if (mNewCursorArea != NULL)
        {
                mNewCursorArea->Release();
                mNewCursorArea = NULL;
        }      

        if (mScreenImage != NULL)      
        {
                delete mScreenImage;
                mScreenImage = NULL;
        }

        if (mDrawSurface != NULL)
        {
                mDrawSurface->Release();
                mDrawSurface = NULL;
        }

        if (mSecondarySurface != NULL)
        {
                mSecondarySurface->Release();
                mSecondarySurface = NULL;
        }

        if (mPrimarySurface != NULL)   
        {
                mPrimarySurface->Release();    
                mPrimarySurface = NULL;
        }
       
        if (mDD != NULL)
        {
                mDD->SetCooperativeLevel(mHWnd, DDSCL_NORMAL);         
                mDD->Release();
                mDD = NULL;
        }

        if (mDD7 != NULL)
        {
                mDD7->Release();
                mDD7 = NULL;
        }
}

bool DDInterface::CopyBitmap(LPDIRECTDRAWSURFACE theSurface, HBITMAP theBitmap, int theX, int theY, int theWidth, int theHeight)
{
        AutoCrit anAutoCrit(mCritSect);

    HRESULT hr;

    if (theBitmap == NULL || theSurface == NULL) return false;

    // Make sure this surface is restored.
    theSurface->Restore();

    // Get size of the bitmap
    BITMAP bmBitmap;
    GetObject(theBitmap, sizeof(bmBitmap), &bmBitmap);
    theWidth  = (theWidth  == 0) ? bmBitmap.bmWidth : theWidth;
    theHeight = (theHeight == 0) ? bmBitmap.bmHeight : theHeight;

    DDSURFACEDESC aDesc;
    ZeroMemory(&aDesc, sizeof(aDesc));
        aDesc.dwSize = sizeof(aDesc);
    aDesc.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
    hr = theSurface->GetSurfaceDesc(&aDesc);
    if (FAILED(hr)) return false;

    // Create memory DC
    HDC hdcImage = CreateCompatibleDC(NULL);
    if (hdcImage != NULL)
    {
        // Select bitmap into memory DC
        HBITMAP anOldBitmap = (HBITMAP)SelectObject(hdcImage, theBitmap);

        // Get surface DC
        HDC hdc;
        hr = theSurface->GetDC(&hdc);
        if (SUCCEEDED(hr))
        {
            // Copy the bitmap. Use BitBlt, if possible, otherwise use
            // StretchBlt
            if (theWidth == aDesc.dwWidth && theHeight == aDesc.dwHeight)
            {
                BitBlt(hdc, 0, 0, theWidth, theHeight, hdcImage, theX, theY, SRCCOPY);
            }
            else
            {
                StretchBlt(hdc, 0, 0, aDesc.dwWidth, aDesc.dwHeight, hdcImage,
                           theX, theY, theWidth, theHeight, SRCCOPY);
            }

            // Release surface DC
            theSurface->ReleaseDC(hdc);
        }

        // Select old bitmap into the memory DC and delete the DC
        SelectObject(hdcImage, anOldBitmap);
        DeleteDC(hdcImage);
    }
    else return false;

    return true;
}

extern std::fstream gStreamThing;

bool DDInterface::Redraw(Rect* theClipRect)
{
        AutoCrit anAutoCrit(mCritSect);
               
        if (!mInitialized)
                return false;  

        DDBLTFX aBltFX;
        ZeroMemory(&aBltFX, sizeof(aBltFX));
        aBltFX.dwSize = sizeof(aBltFX);        

        mInRedraw = true;

        if(mIs3D)
        {
                if (!mD3DInterface->mErrorString.empty())
                {
                        mInRedraw = false;
                        mIs3D = false;
                        return false;
                }

                mD3DInterface->Flush();
        }      

        RECT aDestRect;
        RECT aSrcRect;
        if (NULL == theClipRect || mIsWidescreen)
        {
                // ClipRect cannot be supported when the draw surface and
                // primary surface are not the same size in widescreen mode.
                aDestRect = mPresentationRect.ToRECT();
                aSrcRect = Rect(0, 0, mWidth, mHeight).ToRECT();
        }
        else
        {
                aDestRect = theClipRect->ToRECT();
                aSrcRect = theClipRect->ToRECT();
        }
       
        if ( mIsWidescreen )
        {
                aBltFX.dwDDFX = DDBLTFX_ARITHSTRETCHY;
        }

        POINT aPoint = {0, 0};         

        ClientToScreen(mHWnd, &aPoint);
        OffsetRect(&aDestRect, aPoint.x, aPoint.y);
       
        DDSURFACEDESC aDesc;
        ZeroMemory(&aDesc,sizeof(&aDesc));
        aDesc.dwSize=sizeof(aDesc);
        mDrawSurface->Lock(NULL,&aDesc,DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_READONLY ,0);
        mDrawSurface->Unlock(NULL);

        if (mIsWindowed)
        {
                HRESULT aResult;
               
                //DWORD aScanLine;
                //mDD->GetScanLine(&aScanLine);

                int aScreenHeight = GetSystemMetrics(SM_CYSCREEN);

                if (!mVideoOnlyDraw)
                {
                        mCursorX = mNextCursorX;
                        mCursorY = mNextCursorY;

                        DrawCursorTo(mDrawSurface, false);
                       
                        if ((mApp->mWaitForVSync) && (!mApp->mSoftVSyncWait))
                        {
                                bool scanLineFail = false;

                                if (mScanLineFailCount >= 3)
                                        scanLineFail = true;

                                if (!scanLineFail)
                                {
                                        int aHalfMarkClient = mApp->mHeight / 2;
                                        int aHalfMarkScreen = aDestRect.top + aHalfMarkClient;                                 
                                        int aBotMarkScreen = aDestRect.top + mHeight;

                                        DWORD aScanLine = 0x7FFFFFFF;
                                       
                                        bool wasLess = false;

                                        DWORD aTopStartTick = GetTickCount();
                                       
                                        for (;;)
                                        {                                      
                                                aResult = mDD->GetScanLine(&aScanLine);

                                                if (aResult == DD_OK)
                                                {
                                                        // Wait until we scan below half way on the window
                                                        int aHalfMarkDist = aHalfMarkScreen - aScanLine;
                                                       
                                                        if ((aHalfMarkDist <= 0) || ((int) aScanLine >= aScreenHeight))
                                                        {
                                                                if (wasLess)
                                                                        break;
                                                        }
                                                        else
                                                        {
                                                                wasLess = true;
                                                        }
                                                }
                                                else
                                                {
                                                        if (aResult == DDERR_VERTICALBLANKINPROGRESS )
                                                        {
                                                                if (wasLess)
                                                                        break;
                                                        }
                                                        else
                                                        {
                                                                scanLineFail = true;
                                                                break;
                                                        }
                                                }

                                                DWORD aTickNow = GetTickCount();
                                                if (aTickNow - aTopStartTick >= 200)
                                                {
                                                        // It shouldn't take this long
                                                        scanLineFail = true;
                                                        break;
                                                }
                                        }

                                        if (!scanLineFail)
                                        {
                                                mScanLineFailCount = 0;                                                                                

                                                RECT aTopDestRect = {aDestRect.left, aDestRect.top, aDestRect.right, aHalfMarkScreen};
                                                RECT aTopSrcRect = {aSrcRect.left, aSrcRect.top, aSrcRect.right, aHalfMarkClient};
                                                aResult = mPrimarySurface->Blt(&aTopDestRect, mDrawSurface, &aTopSrcRect, DDBLT_WAIT, &aBltFX);

                                                DWORD aLastScanLine = aScanLine;                                               

                                                for (;;)
                                                {                      
                                                        if (SUCCEEDED(mDD->GetScanLine(&aScanLine)))
                                                        {      
                                                                // Wait until we scan below the bottom of the window
                                                                int aHalfMarkDist = aBotMarkScreen - aScanLine;
                                                                if ((aScanLine < aLastScanLine) || (aHalfMarkDist <= 0))
                                                                        break;
                                                        }
                                                }

                                                RECT aBotDestRect = {aDestRect.left, aHalfMarkScreen, aDestRect.right, aDestRect.bottom};
                                                RECT aBotSrcRect = {aSrcRect.left, aHalfMarkClient, aSrcRect.right, aSrcRect.bottom};
                                                aResult = mPrimarySurface->Blt(&aBotDestRect, mDrawSurface, &aBotSrcRect, DDBLT_WAIT, &aBltFX);
                                        }
                                        else
                                        {
                                                mScanLineFailCount++;
                                        }                                                                              
                                }
                               
                                if (scanLineFail)
                                {
                                        mDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0);
                                        aResult = mPrimarySurface->Blt(&aDestRect, mDrawSurface, &aSrcRect, DDBLT_WAIT, &aBltFX);
                                }                                                                                              
                        }
                        else
                        {
                                aResult = mPrimarySurface->Blt(&aDestRect, mDrawSurface, &aSrcRect, DDBLT_WAIT, &aBltFX);
                        }

                        if (mHasOldCursorArea)
                        {
                                // Restore from the drawn surface, incase we don't do a redraw
                                //  of the drawn surface by next Redraw
                                RestoreOldCursorAreaFrom(mDrawSurface, false);
                               
                                // Set it back to true so it gets removed from the primary
                                //  surface when we move the mouse
                                mHasOldCursorArea = true;
                        }
                }
                else
                {
                        if ((mApp->mWaitForVSync) && (!mApp->mSoftVSyncWait))
                                mDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0);

                        mCursorX = mNextCursorX;
                        mCursorY = mNextCursorY;

                        DrawCursorTo(mSecondarySurface, true);
                        aResult = mPrimarySurface->Blt(&aDestRect, mSecondarySurface, &aSrcRect, DDBLT_WAIT, &aBltFX);
                }

                mInRedraw = false;
                return !GotDXError(aResult,"Redraw Windowed");
        }
        else
        {
                // Don't flip away from the GDI surface during the TryMedia purchasing process
                if (!mApp->mNoDefer && mApp->mFullScreenPageFlip)
                {
                        if (!mVideoOnlyDraw)
                                HRESULT aResult = mSecondarySurface->Blt(&aDestRect, mDrawSurface, &aSrcRect, DDBLT_WAIT, &aBltFX);

                        mCursorX = mNextCursorX;
                        mCursorY = mNextCursorY;

                        DrawCursorTo(mSecondarySurface, true);
                                               
                        HRESULT aResult = mPrimarySurface->Flip(NULL, DDFLIP_WAIT);                    

                        mInRedraw = false;
                        return !GotDXError(aResult,"Redraw FullScreen Flip");
                }
                else
                {
                        HRESULT aResult = mPrimarySurface->Blt(&aDestRect, mDrawSurface, &aSrcRect, DDBLT_WAIT, &aBltFX);

                        mInRedraw = false;
                        return !GotDXError(aResult,"Redraw FullScreen Blt");
                }
        }
}

void DDInterface::RestoreOldCursorAreaFrom(LPDIRECTDRAWSURFACE theSurface, bool adjust)
{
        if ((mHasOldCursorArea) && (mPrimarySurface != NULL))
        {
                Rect aSexyScreenRect(
                        mCursorX - (mCursorWidth / 2),
                        mCursorY - (mCursorHeight / 2),
                        mCursorWidth,
                        mCursorHeight);

                Rect aClippedScreenRect = aSexyScreenRect.Intersection(Rect(0, 0, mWidth, mHeight));                                           
               
                Rect aSexyLocalRect(
                        aClippedScreenRect.mX - aSexyScreenRect.mX,
                        aClippedScreenRect.mY - aSexyScreenRect.mY,
                        aClippedScreenRect.mWidth,
                        aClippedScreenRect.mHeight);
               
                if (adjust)
                {
                        POINT aPoint = {0, 0};         
                        ClientToScreen(mHWnd, &aPoint);
                        aClippedScreenRect.Offset(aPoint.x, aPoint.y);
                }

                RECT aLocalRect = aSexyLocalRect.ToRECT();
                RECT aScreenRect = aClippedScreenRect.ToRECT();

                DDBLTFX aBltFX;
                ZeroMemory(&aBltFX, sizeof(aBltFX));
                aBltFX.dwSize = sizeof(aBltFX);

                // From mNewCursorArea to theSurface
                HRESULT aResult = theSurface->Blt(&aScreenRect, mOldCursorArea, &aLocalRect, DDBLT_WAIT, &aBltFX);

                mHasOldCursorArea = false;
        }
}

void DDInterface::DrawCursorTo(LPDIRECTDRAWSURFACE theSurface, bool adjust)
{
        if ((mCursorImage != NULL) && (mPrimarySurface != NULL))
        {
                DDSURFACEDESC aDesc;
                ZeroMemory(&aDesc, sizeof(aDesc));
                aDesc.dwSize = sizeof(aDesc);
                aDesc.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
                HRESULT aResult = mPrimarySurface->GetSurfaceDesc(&aDesc);
                Rect aDestSurfaceRect(0, 0, aDesc.dwWidth, aDesc.dwHeight);

                Rect aSexyScreenRect(
                        mCursorX - (mCursorWidth / 2),
                        mCursorY - (mCursorHeight / 2),
                        mCursorWidth,
                        mCursorHeight);

                Rect aClippedScreenRect = aSexyScreenRect.Intersection(mPresentationRect);
                                               
                Rect aSexyLocalRect(
                        aClippedScreenRect.mX - aSexyScreenRect.mX,
                        aClippedScreenRect.mY - aSexyScreenRect.mY,
                        aClippedScreenRect.mWidth,
                        aClippedScreenRect.mHeight);

                if (adjust)
                {
                        POINT aPoint = {0, 0};         
                        ClientToScreen(mHWnd, &aPoint);
                        aClippedScreenRect.Offset(aPoint.x, aPoint.y);
                }

                RECT aLocalRect = aSexyLocalRect.ToRECT();
                RECT aScreenRect = aClippedScreenRect.ToRECT();

                DDBLTFX aBltFX;
                ZeroMemory(&aBltFX, sizeof(aBltFX));
                aBltFX.dwSize = sizeof(aBltFX);
               
                // From theSurface to mOldCursorArea
                aResult = mOldCursorArea->Blt(&aLocalRect, theSurface, &aScreenRect, DDBLT_WAIT, &aBltFX);
                if (aResult != DD_OK)                  
                {
                        // Try to clip it now.  We don't ALWAYS want to clip it, though
                        Rect aPrevRect = aClippedScreenRect;
                        aClippedScreenRect = aClippedScreenRect.Intersection(aDestSurfaceRect);
                       
                        aSexyLocalRect.mX += (aClippedScreenRect.mX - aPrevRect.mX);
                        aSexyLocalRect.mY += (aClippedScreenRect.mY - aPrevRect.mY);
                        aSexyLocalRect.mWidth = aClippedScreenRect.mWidth;
                        aSexyLocalRect.mHeight = aClippedScreenRect.mHeight;

                        aLocalRect = aSexyLocalRect.ToRECT();
                        aScreenRect = aClippedScreenRect.ToRECT();                     

                        aResult = mOldCursorArea->Blt(&aLocalRect, theSurface, &aScreenRect, DDBLT_WAIT, &aBltFX);                     
                        //DBG_ASSERT(aResult == DD_OK);
                }
               
                mHasOldCursorArea = aResult == DD_OK;

                // Kindof a hack, since we only use this for mDrawSurace
                if (theSurface == mDrawSurface && !mIs3D)
                {
                        // Draw directly to the screen image, instead of indirectly through mNewCursorArea
                        Graphics g(mScreenImage);
                        g.DrawImage(mCursorImage,
                                mCursorX - (mCursorWidth / 2) + (mCursorWidth - mCursorImage->mWidth)/2,
                                mCursorY - (mCursorHeight / 2) + (mCursorHeight - mCursorImage->mHeight)/2);                           
                }
                else
                {
                        // From mOldCursorArea to mNewCursorArea
                        aResult = mNewCursorArea->Blt(&aLocalRect, mOldCursorArea, &aLocalRect, DDBLT_WAIT, &aBltFX);

                        // Draw image to mNewCursorAreaImage
                        Graphics aNewCursorAreaG(mNewCursorAreaImage);
                        aNewCursorAreaG.DrawImage(mCursorImage,
                                (mCursorWidth - mCursorImage->mWidth)/2,
                                (mCursorHeight - mCursorImage->mHeight)/2);

                        // From mNewCursorArea to theSurface
                        aResult = theSurface->Blt(&aScreenRect, mNewCursorArea, &aLocalRect, DDBLT_WAIT, &aBltFX);
                }
        }
        else
                mHasOldCursorArea = false;
}

void DDInterface::MoveCursorTo(LPDIRECTDRAWSURFACE theSurface, bool adjust, int theNewCursorX, int theNewCursorY)
{
        DBG_ASSERT(mHasOldCursorArea); 

        if ((mCursorImage != NULL) && (mPrimarySurface != NULL))
        {
                DDSURFACEDESC aDesc;
                ZeroMemory(&aDesc, sizeof(aDesc));
                aDesc.dwSize = sizeof(aDesc);
                aDesc.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
                HRESULT aResult = mPrimarySurface->GetSurfaceDesc(&aDesc);
                Rect aDestSurfaceRect(0, 0, aDesc.dwWidth, aDesc.dwHeight);

                // OLD RECTANGLES
                Rect aSexyScreenRect(
                        mCursorX - (mCursorWidth / 2),
                        mCursorY - (mCursorHeight / 2),
                        mCursorWidth,
                        mCursorHeight);

                Rect aClippedScreenRect = aSexyScreenRect.Intersection(mPresentationRect);
               
                Rect aSexyLocalRect(
                        aClippedScreenRect.mX - aSexyScreenRect.mX,
                        aClippedScreenRect.mY - aSexyScreenRect.mY,
                        aClippedScreenRect.mWidth,
                        aClippedScreenRect.mHeight);
                if (adjust)
                {
                        POINT aPoint = {0, 0};         
                        ClientToScreen(mHWnd, &aPoint);
                        aClippedScreenRect.Offset(aPoint.x, aPoint.y);
                        //aClippedScreenRect = aClippedScreenRect.Intersection(aDestSurfaceRect);      
                }

                aSexyLocalRect.mWidth = aClippedScreenRect.mWidth;
                aSexyLocalRect.mHeight = aClippedScreenRect.mHeight;
               
                RECT aLocalRect = aSexyLocalRect.ToRECT();
                RECT aScreenRect = aClippedScreenRect.ToRECT();

                // NEW RECTANGLES
                Rect aNewSexyScreenRect(
                        theNewCursorX - (mCursorWidth / 2),
                        theNewCursorY - (mCursorHeight / 2),
                        mCursorWidth,
                        mCursorHeight);
                               
                Rect aNewClippedScreenRect = aNewSexyScreenRect.Intersection(mPresentationRect);
                Rect aNewSexyLocalRect(
                        aNewClippedScreenRect.mX - aNewSexyScreenRect.mX,
                        aNewClippedScreenRect.mY - aNewSexyScreenRect.mY,
                        aNewClippedScreenRect.mWidth,
                        aNewClippedScreenRect.mHeight);
                if (adjust)
                {
                        POINT aPoint = {0, 0};
                        ClientToScreen(mHWnd, &aPoint);
                        aNewClippedScreenRect.Offset(aPoint.x, aPoint.y);
                        //aNewClippedScreenRect = aNewClippedScreenRect.Intersection(aDestSurfaceRect);
                }

                aNewSexyLocalRect.mWidth = aNewClippedScreenRect.mWidth;
                aNewSexyLocalRect.mHeight = aNewClippedScreenRect.mHeight;

                RECT aNewLocalRect = aNewSexyLocalRect.ToRECT();
                RECT aNewScreenRect = aNewClippedScreenRect.ToRECT();

                // Do drawing stuff now

                DDBLTFX aBltFX;
                ZeroMemory(&aBltFX, sizeof(aBltFX));
                aBltFX.dwSize = sizeof(aBltFX);

                // From theSurface to mNewCursorArea
                //  It may still have some of the old cursor on it though, since we haven't restored
                //  that area yet
               
                aResult = mNewCursorArea->Blt(&aNewLocalRect, theSurface, &aNewScreenRect, DDBLT_WAIT, &aBltFX);
                if (aResult != DD_OK)                  
                {
                        // Try to clip it now.  We don't ALWAYS want to clip it, though
                        Rect aPrevRect = aNewClippedScreenRect;
                        aNewClippedScreenRect = aNewClippedScreenRect.Intersection(aDestSurfaceRect);
                       
                        aNewSexyLocalRect.mX += (aNewClippedScreenRect.mX - aPrevRect.mX);
                        aNewSexyLocalRect.mY += (aNewClippedScreenRect.mY - aPrevRect.mY);
                        aNewSexyLocalRect.mWidth = aNewClippedScreenRect.mWidth;
                        aNewSexyLocalRect.mHeight = aNewClippedScreenRect.mHeight;

                        aNewLocalRect = aNewSexyLocalRect.ToRECT();
                        aNewScreenRect = aNewClippedScreenRect.ToRECT();                                       

                        aResult = mNewCursorArea->Blt(&aNewLocalRect, theSurface, &aNewScreenRect, DDBLT_WAIT, &aBltFX);                       
                        //DBG_ASSERT(aResult == DD_OK);
                }
                //DBG_ASSERT(aResult == DD_OK);

                // Figure out the overlapping area from the source
                Rect aCursorAreaRect(0, 0, mCursorWidth, mCursorHeight);
                Rect aSexyOrigSrcAreaRect(aCursorAreaRect);
                aSexyOrigSrcAreaRect.Offset(theNewCursorX - mCursorX, theNewCursorY - mCursorY);
                Rect aSexySrcAreaRect = aSexyOrigSrcAreaRect.Intersection(aCursorAreaRect);

                // Does the new cursor area overlap with the old one?
                if ((aSexySrcAreaRect.mWidth > 0) || (aSexySrcAreaRect.mHeight > 0))
                {
                        Rect aSexyDestAreaRect(
                                aSexySrcAreaRect.mX - aSexyOrigSrcAreaRect.mX,
                                aSexySrcAreaRect.mY - aSexyOrigSrcAreaRect.mY,
                                aSexySrcAreaRect.mWidth, aSexySrcAreaRect.mHeight);
                       
                        RECT aSrcAreaRect = aSexySrcAreaRect.ToRECT();
                        RECT aDestAreaRect = aSexyDestAreaRect.ToRECT();
                       
                        // Restore old area from new area
                        //  This will give us our new pure OLD buffer
                        mNewCursorArea->Blt(&aDestAreaRect, mOldCursorArea, &aSrcAreaRect, DDBLT_WAIT, &aBltFX);
                        //DBG_ASSERT(aResult == DD_OK);
                       
                        // Draw offset image to mOldCursorAreaImage.  This is to avoid flicker
                        Graphics aOldCursorAreaG(mOldCursorAreaImage);
                        aOldCursorAreaG.DrawImage(mCursorImage,
                                theNewCursorX - mCursorX + (mCursorWidth - mCursorImage->mWidth)/2,
                                theNewCursorY - mCursorY + (mCursorHeight - mCursorImage->mHeight)/2);
                }
               
                // Restore the old cursor area
                aResult = theSurface->Blt(&aScreenRect, mOldCursorArea, &aLocalRect, DDBLT_WAIT, &aBltFX);
                //DBG_ASSERT(aResult == DD_OK);
               
                // The screen is now PURE and restored

                // Move the new cursor area to the old one, since this is what we will have to
                //  use to redraw the old area
                RECT aFullAreaRect = {0, 0, mCursorWidth, mCursorHeight};
                aResult = mOldCursorArea->Blt(&aFullAreaRect, mNewCursorArea, &aFullAreaRect, DDBLT_WAIT, &aBltFX);
                //DBG_ASSERT(aResult == DD_OK);

                // Draw image to mNewCursorAreaImage, preparing to draw it to the screen
                Graphics aNewCursorAreaG(mNewCursorAreaImage);
                aNewCursorAreaG.DrawImage(mCursorImage,
                        (mCursorWidth - mCursorImage->mWidth)/2,
                        (mCursorHeight - mCursorImage->mHeight)/2);

                // From mNewCursorArea to theSurface
                aResult = theSurface->Blt(&aNewScreenRect, mNewCursorArea, &aNewLocalRect, DDBLT_WAIT, &aBltFX);
                //DBG_ASSERT(aResult == DD_OK);

                // The cursor is now fully moved
                mCursorX = theNewCursorX;
                mCursorY = theNewCursorY;
        }
        else
                mHasOldCursorArea = false;
}

bool DDInterface::SetCursorImage(Image* theImage)
{
        AutoCrit anAutoCrit(mCritSect);
       
        if (mCursorImage != theImage)
        {
                // Wait until next Redraw or cursor move to draw new cursor
                mCursorImage = theImage;               
                return true;
        }
        else
                return false;
}

void DDInterface::SetCursorPos(int theCursorX, int theCursorY)
{
        mNextCursorX = theCursorX;
        mNextCursorY = theCursorY;

        if (mInRedraw)
                return;

        AutoCrit anAutoCrit(mCritSect);
       
        if (mHasOldCursorArea)
        {
                MoveCursorTo(mPrimarySurface, true, theCursorX, theCursorY);
        }
        else
        {
                mCursorX = theCursorX;
                mCursorY = theCursorY;
                DrawCursorTo(mPrimarySurface, true);           
        }
}