/*
**-----------------------------------------------------------------------------
**  Name:       D3DWin.cpp
**  Purpose:    
**
**	Basic Initialization proceeds as follows:
**
**	1.  Enumerate all Driver, modes, D3D devices (see DrvMgr.cpp for details)
**	2.  Choose a starting driver, mode, D3D device
**			- default driver = primary display driver (lpGuidDD = NULL)
**			- default mode   = current desktop
**			- default device = D3D device compatible with desktop mode
**	3.  Validate driver, mode, D3D device
**	4.  Create interfaces (from DD driver)
**  5.  Set window (from associated window handle)
**  6.  Create DD/D3D interfaces (lpDD, lpDD2, lpD3D)
**  7.  Create Primary surface (primary palette, if necessary)
**			- Attach a clipper to primary surface
**  8.  Create Render surface 
**			- Render surface (and associated Z-buffer)
**			- D3D Device 
**			- D3D Viewport
**
**  After initialization is complete, we have the
**	following objects necessary for rendering:
**
**		lpDD2 - DirectDraw interface, used for creating texture surfaces
**		lpD3D - Direct3D interface, used for creating materials, lights, viewports
**		lpD3DDevice - D3D device (current material, current viewport, etc.)
**		lpViewport - current viewport
**		lpPrimary  - front buffer
**		lpRender   - render target
**
**  Copyright (c) 1995 - 1997 by Microsoft, all rights reserved
**-----------------------------------------------------------------------------
*/

/*
**-----------------------------------------------------------------------------
** Includes
**-----------------------------------------------------------------------------
*/

#include "D3DWin.h"
#include "WinProc.h"
#include "D3DScene.h"
#include "Debug.h"



/*
**-----------------------------------------------------------------------------
**	D3DWindow Methods
**-----------------------------------------------------------------------------
*/

/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::D3DWindow
** Purpose: Default Constructor
**-----------------------------------------------------------------------------
*/

D3DWindow::D3DWindow (void)
{
    ZeroMemory (this, sizeof(D3DWindow));
    this->dwSize = sizeof(D3DWindow);

	// Default to creating a z-buffer
	createZBufferOn ();	
} // End D3DWindow::D3DWindow ()


  
/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::~D3DWindow
** Purpose: Destructor
**-----------------------------------------------------------------------------
*/

D3DWindow::~D3DWindow (void)
{
	// Destroy all objects
    Fini ();

	// Mark all other pointers as invalid
	// In case user tries to reuse this object
	lpCurrDriver = NULL;
	lpCurrMode	 = NULL;
	lpCurrDevice = NULL;
	hWindow		 = NULL;
	lpd3dScene	 = NULL;

} // End D3DWindow::~D3DWindow



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::Create
** Purpose: Creates a D3DWindow 
**
** Basic Algorithm:
**	- Validate parameters
**	- Choose (and validate choices) for driver, mode, device
**	- Create Interfaces
**	- Set Window
**	- Set Mode
**	- Create Primary surface (and palette)
**	- Create Render surface (and D3D device)
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::Create (
	HWND   hWnd,		/* In:  Window */
	LPGUID lpGuidDD,	/* In:  Requested DirectDraw Device */
	DWORD  dwW,			/* In:	Requested Mode */
	DWORD  dwH,			
	DWORD  dwBPP,
	DWORD  dwRefresh,
	LPGUID lpGuidD3D,	/* In:  Requested D3D device */
	BOOL   fUseZBuffer) /* In:  Create Z-Buffer */
{
    HRESULT         hResult;

    // Check parameters
	if ((! hWnd) || (! IsWindow (hWnd)))
	{
		// Error, Invalid parameters
		hResult = APPERR_INVALIDPARAMS;
		REPORTERR (hResult);
		return hResult;
	}

	// Set Current Window
	hWindow = hWnd;

    // Set Use Z-Buffer On/Off
    if (fUseZBuffer)
        createZBufferOn ();
    else
        createZBufferOff ();

    // Choose Default Driver, Mode, device 
    hResult = ChooseDriverDefaults (lpGuidDD, 
									dwW, dwH, dwBPP, dwRefresh,
									lpGuidD3D,
									TRUE,
									&lpCurrDriver,
									&lpCurrMode,
									&lpCurrDevice);
    if (FAILED (hResult))
        return hResult;
    
    // Create DD/D3D Interface objects
    hResult = InitInterfaces ();
    if (FAILED (hResult))
        return hResult;

	// Attach window to DD interface
	hResult = InitWindow ();
	if (FAILED (hResult))
		goto lblCLEANUP;

	// Set Fullscreen Mode
	hResult = InitFullscreenMode ();
	if (FAILED (hResult))
		goto lblCLEANUP;

    // Create Primary Surface (and palette)
    hResult = InitPrimary ();
    if (FAILED (hResult))
        goto lblCLEANUP;

	// Create the Render Surface (and D3D Device)
    hResult = InitRender ();
    if (FAILED (hResult))
        goto lblCLEANUP;

	// Notify the window of a successful initialization
	SendMessage (hWindow, D3DWIN_INIT, 0, (LPARAM)(void *)this);

    // Success
    return DD_OK;

lblCLEANUP:
	// Failure

    // Cleanup
    Fini ();

    return hResult;
} // End D3DWindow::Create



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::Init
** Purpose: 
**
** Basic Algorithm:
**	- Validate driver, mode, device
**	- Create Interfaces
**	- Attach Window
**	- Set Fullscreen Mode
**	- Create Primary surface (and palette)
**	- Create Render surface (and D3D device)
**
** Notes:
**		1.  Assumes that a valid window handle has already
**			been associated with this D3DWindow
**		2.  Assumes that driver, mode, device already choosen
**			- however if not, reasonable defaults will be choosen
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::Init (void)
{
    HRESULT         hResult;

    // Check parameters
	if ((! hWindow) || (! IsWindow (hWindow)))
	{
		// Error, Invalid Initialization
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
		return hResult;
	}

    // Validate Curr Driver, mode, device
	hResult = ValidateDefaults ();
	if (FAILED (hResult))
		return hResult;

    // Create DD/D3D Interface objects
    hResult = InitInterfaces ();
    if (FAILED (hResult))
		goto lblCLEANUP;

	// Attach the window to the DD interface
	hResult = InitWindow ();
	if (FAILED (hResult))
		goto lblCLEANUP;

	// Set the Mode
	hResult = InitFullscreenMode ();
	if (FAILED (hResult))
		goto lblCLEANUP;

    // Create Primary Surface (and palette)
    hResult = InitPrimary ();
    if (FAILED (hResult))
        goto lblCLEANUP;

	// Create Render surface (and D3D device)
    hResult = InitRender ();
    if (FAILED (hResult))
        goto lblCLEANUP;

	// Notify the window of a successful initialization
	SendMessage (hWindow, D3DWIN_INIT, 0, (LPARAM)(void *)this);

    // Success
    return DD_OK;

lblCLEANUP:
	// Failure 

    // Cleanup
    Fini ();
    return hResult;
} // End D3DWindow::Init



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::Fini
** Purpose: Destroys a D3DWindow
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::Fini (void)
{
	// Notify the window that we are cleaning up
	SendMessage (hWindow, D3DWIN_FINI, 0, (LPARAM)(void *)this);

	// Cleanup
    FiniRender ();
    FiniPrimary ();
	FiniFullscreenMode ();
//	FiniWindow ();
    FiniInterfaces ();

	// Success
	return DD_OK;
} // End D3DWindow::Fini


  
  
/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::ValidateDefaults
** Purpose: Verify's current driver, mode, and device
** Notes:   
**
**	1.  Rather than fail completely, this will pick new defaults
**      if the current defaults don't work.
**
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::ValidateDefaults (void)
{
	LPGUID			lpGuidDD, lpGuidD3D;
	HRESULT			hResult;
    LPDDDrvInfo		lpDrvNew;
	LPDDModeInfo	lpModeNew;
	LPD3DDevInfo	lpDevNew;

	// Initialize Driver Manager, if necessary
	if (! DDDrvMgr::isInitialized ())
	{
		hResult = DDDrvMgr::Init ();
		if (FAILED (hResult))
			return hResult;
	}
    
    // Get DD Guid
	if (lpCurrDriver)
		lpGuidDD = lpCurrDriver->GetGuid ();
	else
		lpGuidDD = NULL;

	// Get D3D Guid
	if (lpCurrDevice)
		lpGuidD3D = &(lpCurrDevice->guid);
	else
		lpGuidD3D = NULL;

	// Get Driver corresponding to DD Guid
    lpDrvNew = ValidateDriver (lpGuidDD);
	if (! lpDrvNew)
    {
        // Error, invalid DD Guid
		hResult = APPERR_INVALIDPARAMS;
		REPORTERR (hResult);
        return hResult;
    }

	DWORD w, h, bpp, refresh;

	// Get Current mode info
	if (lpCurrMode)
		lpCurrMode->GetMode (w, h, bpp, refresh);
	else
	{
		w = h = bpp = refresh = 0;
	}

	// Get Fullscreen D3D device and compatible mode
	if (! GetFullscreenMode (lpDrvNew, lpGuidD3D,
							 w, h, bpp, refresh,
							 &lpModeNew, &lpDevNew))
	{
		// Couldn't find a valid mode and/or device
		hResult = APPERR_GENERIC;
		REPORTERR (hResult);
		return APPERR_GENERIC;
	}

	// Note:  Instead of complaining let's go ahead
	//		  and use the new defaults
	// Save new defaults
	lpCurrDriver = lpDrvNew;
	lpCurrMode	 = lpModeNew;
	lpCurrDevice = lpDevNew;

    // Success
    return DD_OK;
} // End D3DWindow::ValidateDefaults



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::CreateInterfaces
** Purpose: Creates DD/D3D interfaces from specified Guid
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::CreateInterfaces (LPGUID lpDDGuid)
{
	LPDDDrvInfo lpDrvNew;
	HRESULT		hResult;

	// Verify Guid
	lpDrvNew = ValidateDriver (lpDDGuid);
	if (! lpDrvNew)
	{
		// Invalid Params
		hResult = APPERR_INVALIDPARAMS;
		REPORTERR (hResult);
		return hResult;
	}

	lpCurrDriver = lpDrvNew;

	hResult = D3DWindow::InitInterfaces ();
	if (FAILED (hResult))
		return hResult;

	// Success
	return DD_OK;
} // End D3DWindow::CreateInterfaces

  

/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::InitInterfaces
** Purpose: Creates DD/D3D interfaces
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::InitInterfaces (void)
{
    HRESULT         hResult;
    LPGUID          lpGuid;

    // Do we have a current DD Driver
    if (! lpCurrDriver)
    {
		// So, Grab the Primary DD driver
		lpCurrDriver = ValidateDriver (NULL);
		if (! lpCurrDriver)
		{
			// Error, No current Driver
			hResult = APPERR_NOTINITIALIZED;
			REPORTERR (hResult);
			return hResult;
		}
    }

    // Get DD Guid
    lpGuid = lpCurrDriver->GetGuid ();
    
    // Create DD interface
    hResult = DirectDrawCreate (lpGuid, &lpDD, NULL);
    if (FAILED (hResult))
    {
        // Error
		REPORTERR (hResult);
		goto lblCLEANUP;
    }

    // Get DD2 interface
    hResult = lpDD->QueryInterface ((REFIID)IID_IDirectDraw2, (void **)&lpDD2);
    if (FAILED (hResult))
    {
        // Error
		REPORTERR (hResult);

		// Inform User that they Need DX 5.0 installed
        MessageBox (hWindow, TEXT ("This app requires DirectX 5.0"), TEXT("Error"), MB_OK);
        goto lblCLEANUP;
    }

    // Get D3D interface
    hResult = lpDD2->QueryInterface ((REFIID)IID_IDirect3D2, (void **)&lpD3D);
    if (FAILED (hResult))
    {
        // Error
		REPORTERR (hResult);
        goto lblCLEANUP;
    }

	// Mark this stage as done
	turnValidInterfaceOn ();

    // Success
    return DD_OK;

lblCLEANUP:
    // Failure
    FiniInterfaces ();

    return hResult;
} // End InitInterfaces



  
/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::FiniInterfaces
** Purpose: Destroys DD/D3D interfaces
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::FiniInterfaces (void)
{
	// Mark this stage as invalid
	turnValidInterfaceOff ();

    // Release Direct3D Interface
    if (lpD3D)
    {
        lpD3D->Release ();
        lpD3D = NULL;
    }

    // Release DirectDraw2 Interface
    if (lpDD2)
    {
        lpDD2->Release ();
        lpDD2 = NULL;
    }

    // Release DirectDraw Interface
    if (lpDD)
    {
        lpDD->Release ();
        lpDD = NULL;
    }

	// Success
	return DD_OK;
} // End D3DWindow::FiniInterfaces


/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::InitWindow
** Purpose: Attaches Window to Direct Draw Interface
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::InitWindow (void)
{
	HRESULT hResult;
	DWORD	dwFlags;

	// Check Initialization
	if ((! hWindow) || (! IsWindow (hWindow)))
	{
		// Error, we need a valid window to continue
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
		return hResult;
	}

    // Get Cooperative Flags
    dwFlags = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;

    // Set Cooperative Level
    hResult = lpDD2->SetCooperativeLevel (hWindow, dwFlags);
    if (FAILED (hResult))
    {
        // Error
        REPORTERR (hResult);
		return hResult;
    }

	// Success
	return DD_OK;
} // End D3DWindow::InitWindow


  
/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::FiniWindow
** Purpose: Cleanups window
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::FiniWindow (void)
{
	// Currently does nothing

	// Success
	return DD_OK;
} // End D3DWindow::FiniWindow


  
/*
**-----------------------------------------------------------------------------
**	Name:		D3DWindow::InitFullscreenMode
**	Purpose:	Switches to requested fullscreen mode
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::InitFullscreenMode (void)
{
	HRESULT hResult;
	DWORD   dwFlags = 0;

	// Check Initialization
	if ((! lpCurrMode) || (! lpDD2))
	{
		// Error, we need a valid mode and DirectDraw 2 interface to proceed
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
		return hResult;
	}
        
	// Calculate Mode info
	DWORD w, h, bpp, refresh;
	lpCurrMode->GetMode (w, h, bpp, refresh);

	// Special check for mode 320 x 200 x 8
	if ((w == 320) && (h == 200) && (bpp == 8))
    {
		// Make sure we use Mode 13 instead of Mode X
		dwFlags = DDSDM_STANDARDVGAMODE;
    } 
		
    // Set Requested Fullscreen mode
    hResult = lpDD2->SetDisplayMode (w, h, bpp, refresh, dwFlags);
    if (SUCCEEDED (hResult))
	{
		// Save Surface Rectangle
		rSurf.left   = 0;
		rSurf.top    = 0;
		rSurf.right  = w;
		rSurf.bottom = h;

		// Success
		turnValidFullscreenOn ();
		return hResult;
	}

	DPF (DEBUG_ERROR, "SetDisplayMode failed (%d x %d x %d), trying (640 x 480 x %d)", w, h, bpp, bpp);

	// Don't give up!
	// Try 640 x 480 x bpp mode instead
	if ((w != 640 || h != 480))
    {
		w = 640;
		h = 480;

		lpCurrMode = ValidateMode (lpCurrDriver, w, h, bpp, 0, lpCurrDevice);
		if (lpCurrMode)
		{
			hResult = lpDD2->SetDisplayMode (w, h, bpp, 0, 0);
			if (SUCCEEDED (hResult))
			{
				// Save Surface Rectangle
				rSurf.left   = 0;
				rSurf.top    = 0;
				rSurf.right  = w;
				rSurf.bottom = h;

				// Success
				turnValidFullscreenOn ();
				return hResult;
			}
		}
	}

	// Keep trying
	// Try 640 x 480 x 16 mode instead
	if (bpp != 16)
    {
		DPF (DEBUG_ERROR, "SetDisplayMode failed (640 x 480 x %d), trying (640 x 480 x 16)", bpp);
		bpp = 16;

		lpCurrMode = ValidateMode (lpCurrDriver, w, h, bpp, 0, lpCurrDevice);
		if (lpCurrMode)
		{
			hResult = lpDD2->SetDisplayMode (w, h, bpp, 0, 0);
			if (SUCCEEDED (hResult))
			{
				// Save Surface Rectangle
				rSurf.left   = 0;
				rSurf.top    = 0;
				rSurf.right  = w;
				rSurf.bottom = h;

				// Success
				turnValidFullscreenOn ();
				return hResult;
			}
		}
	}

	// Failure
	REPORTERR (hResult);
	return hResult;
} // End D3DWindow::InitFullscreenMode



/*
**-----------------------------------------------------------------------------
**	Name:		D3DWindow::FiniFullscreenMode
**	Purpose:	Restores mode to original desktop
**	Notes:		This does nothing if we are windowed
**				I.E. the user already is restored
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::FiniFullscreenMode (void)
{
	turnValidFullscreenOff ();

	// Restore original desktop mode
	if (lpDD2)
		lpDD2->RestoreDisplayMode();

	// Success
	return DD_OK;
} // End D3DWindow::FiniFullscreenMode



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::InitPrimary
** Purpose: Creates a primary surface (desktop surface)
** Notes:   Also creates and attaches palette, if necessary
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::InitPrimary (void)
{
    HRESULT			hResult;
    DDSURFACEDESC	ddsd;

    // Check Initialization
    if ((! lpCurrMode) || (! lpDD2))
    {
        // Error, Need a valid mode and DD interface to proceed
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
        return hResult;
    }

	// Note:  There is no need to fill in width, height, bpp, etc.
	//        This was taken care of in the SetDisplayMode call.

	// Setup Surfaces caps for a front buffer and back buffer
    ddsd.dwSize				= sizeof(ddsd);
    ddsd.dwFlags			= DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps		= DDSCAPS_PRIMARYSURFACE | 
							  DDSCAPS_FLIP | 
							  DDSCAPS_COMPLEX |
							  DDSCAPS_3DDEVICE;		// Create a D3D compatible surface
    ddsd.dwBackBufferCount	= 1;			

	// Create Primary surface
    hResult = lpDD2->CreateSurface (&ddsd, &lpddsPrimary, NULL);
    if (FAILED (hResult))
    {
        // Error
        REPORTERR (hResult);
        return hResult;
    }

	// Create and attach palette, if necessary
	hResult = InitPalette ();
	if (FAILED (hResult))
		return hResult;

	// Mark as Valid
	turnValidPrimaryOn ();

    // Success
    return DD_OK;
} // End D3DWindow::InitPrimary



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::FiniPrimary
** Purpose: Destroys the Primary Surface
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::FiniPrimary (void)
{
	// Mark as Invalid
	turnValidPrimaryOff ();

	// Cleanup palette
	FiniPalette ();

    // Release Primary Surface Object
    if (lpddsPrimary)
    {
        lpddsPrimary->Release ();
        lpddsPrimary = NULL;
    }

	// Success
	return DD_OK;
} // End D3DWindow::FiniPrimary



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::InitPalette
** Purpose: Creates a primary palette if necessary
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::InitPalette ()
{
    HRESULT             hResult;
    HDC                 hdc;
    DWORD               ii;
    DWORD               cbSize;
    DWORD               dwFlags;
    DDSURFACEDESC       ddsd;

    // Destroy old palette
    FiniPalette ();

    // Make sure we are properly intialized 
    // for this to work
    if ((! lpDD2) || (! lpddsPrimary))
    {
        // Error, need a valid DD interfac and a primary surface to continue
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
        return hResult;
    }

    // Get primary surface caps
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    hResult = lpddsPrimary->GetSurfaceDesc(&ddsd);
    if (FAILED (hResult))
    {
        // Error
        REPORTERR (hResult);
        return hResult;
    }

    // Make sure it is a palettized surface
    if (! isPalettized (&(ddsd.ddpfPixelFormat)))
    {
        // Success, primary isn't palettized
		// So we don't need to create a palette
        return DD_OK;
    }

    // Create and save System palette
    hdc = GetDC (NULL);
    cPalette = GetDeviceCaps (hdc, SIZEPALETTE);
    if (cPalette)
    {
        if (cPalette > 256)
            cPalette = 256;

        // Get memory for system palette
        lppeSystem = new PALETTEENTRY[cPalette];
        if (! lppeSystem)
        {
			ReleaseDC (NULL, hdc);

            // Error, not enough memory
			hResult = APPERR_OUTOFMEMORY;
			REPORTERR (hResult);
			goto lblCLEANUP;
        }

		// Get Memory for current palette
		lppeCurr = new PALETTEENTRY[cPalette];
		if (! lppeCurr)
		{
			ReleaseDC (NULL, hdc);

            // Error, not enough memory
			hResult = APPERR_OUTOFMEMORY;
			REPORTERR (hResult);
			goto lblCLEANUP;

		}

        // Save system palette
        GetSystemPaletteEntries (hdc, 0, cPalette, lppeSystem);

        // Copy system palette to temporary values
        cbSize = cPalette * sizeof (PALETTEENTRY);
        CopyMemory (lppeCurr, lppeSystem, cbSize);
    }
	ReleaseDC (NULL, hdc);

    if (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED1)
    {
        dwFlags = DDPCAPS_1BIT;

        // Only 2 palette entries, we need them all
        for (ii = 0; ii < 2; ii++)
            lppeCurr[ii].peFlags = D3DPAL_FREE | PC_RESERVED;

    }
    else if (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED2)
    {
        // Only 4 palette entries, we need them all
        for (ii = 0; ii < 4; ii++)
            lppeCurr[ii].peFlags = D3DPAL_FREE | PC_RESERVED;

        dwFlags = DDPCAPS_2BIT;
    }
    else if (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED4)
    {
        // Only 16 palette entries, we will save black and white
        // and keep the rest for ourselves.

        lppeCurr[0].peFlags = D3DPAL_READONLY;
        lppeCurr[15].peFlags = D3DPAL_READONLY;

        for (ii = 1; ii < 15; ii++)
            lppeCurr[ii].peFlags = D3DPAL_FREE | PC_RESERVED;

        dwFlags = DDPCAPS_4BIT;
    }
    else if (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8)
    {
        // 256 palette entries, we can afford to be nice
        // and save the first 10 and last 10 palette entries
        // for system use 
        for (ii = 0; ii < 10; ii++)
        {
            lppeCurr[ii].peFlags = D3DPAL_READONLY;
            lppeCurr[246+ii].peFlags = D3DPAL_READONLY;
        }

        for (ii = 10; ii < 246; ii++)
            lppeCurr[ii].peFlags = D3DPAL_FREE | PC_RESERVED;

        dwFlags = DDPCAPS_8BIT;        
    }
    else
    {
        // Error, programming (unknown palette type)
		hResult = APPERR_GENERIC;
		REPORTERR (hResult);
		goto lblCLEANUP;
    }

    // Create Primary Palette
    hResult = lpDD2->CreatePalette (dwFlags,
                                    lppeCurr,
                                    &lpddpPalette,
                                    NULL);
    if (FAILED (hResult))
    {
        REPORTERR (hResult);
        goto lblCLEANUP;
    }

    // Attach palette to primary surface
    hResult = lpddsPrimary->SetPalette (lpddpPalette);
    if (FAILED (hResult))
    {
        // Error
        REPORTERR (hResult);
        goto lblCLEANUP;
    }

    // Success
    return DD_OK;

lblCLEANUP:
	// Failure

	// Cleanup
	FiniPalette ();
	return hResult;
} // D3DWindow::InitPalette



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::FiniPalette
** Purpose: Destroys primary palette
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::FiniPalette (void)
{
    // Note:  Should we Detach Palette object from surfaces
    // No way to do this that I know of...
    
    // Cleanup up DD Palette object
    if (lpddpPalette)
    {
        lpddpPalette->Release ();
        lpddpPalette = NULL;
    }

	// Cleanup Current Palette
	if (lppeCurr)
	{
		delete [] lppeCurr;
		lppeCurr = NULL;
	}

    // Cleanup System Palette
    if (lppeSystem)
    {
        // Note:  Should we try and restore system palette here ?!?

        // Destroy system palette
        delete [] lppeSystem;
        lppeSystem = NULL;
    }

	cPalette = 0;

	// Success
	return DD_OK;
} // End FiniPalette


    
/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::CreateRender
** Purpose: Creates the rendering surface and D3D device
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::CreateRender (LPGUID lpD3DGuid)
{
	HRESULT		 hResult;
	LPD3DDevInfo lpDevNew;
	LPDDModeInfo lpModeNew;

	// Check Initialization
	if ((! lpCurrDriver) || (! lpCurrMode))
	{
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
		return hResult;
	}

	// Validate D3D Device
	lpDevNew = ValidateDevice (lpCurrDriver, lpD3DGuid, NULL);
	if (! lpDevNew)
	{
		hResult = APPERR_INVALIDPARAMS;
		REPORTERR (hResult);
		return hResult;
	}

	DWORD w, h, bpp, refresh;
	lpCurrMode->GetMode (w, h, bpp, refresh);

	// Validate Mode with this D3D Device
	lpModeNew = ValidateMode (lpCurrDriver, w, h, bpp, 0, lpDevNew);
	if (! lpModeNew)
	{
		hResult = APPERR_INVALIDPARAMS;
		REPORTERR (hResult);
		return hResult;
	}

	// Do we need to Change the Mode as well
	// to stay in synch with the new D3D device ?!?
	if (lpModeNew != lpCurrMode)
	{
		// Save old mode
		LPDDModeInfo lpModeOld = lpCurrMode;

		// Try to switch modes
		lpCurrMode = lpModeNew;
		hResult = InitPrimary ();
		if (FAILED (hResult))
		{
			// Try to restore old mode and exit with error
			lpCurrMode = lpModeOld;
			InitPrimary ();
			return hResult;
		}

		// Successfully switched modes
	}
	
	// Save new D3D device
	lpCurrDevice = lpDevNew;

	// Create new Render surface (using new Device)
	hResult = InitRender ();
	if (FAILED (hResult))
		return hResult;

	// Success
	return DD_OK;
}



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::InitRender
** Purpose: Creates the rendering surface and D3D device
** Notes:
**
**	1.  Catch 22:  To creating a D3D device you need it's render surface
**		First.  But in order to create a render surface properly you need
**      to know the D3D device desc characteristics first.   Fortunately we 
**		already have this information.  Because we store it awy in the 
**		Driver Manager.
**
**  2.  This same catch 22 prevented us from enumerating Texture formats
**      easily in the Driver Manager.  So, we do it here once we have a 
**		valid D3D device.
**
** Basic Algorithm:
**	1. Get pointer to back buffer and use as render surface
**  2. Create Z-buffer (optional)
**	3. Create D3D Device (enumerate Texture Formats)
**	4. Create Viewport
**	5. InitScene (optional)
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::InitRender (void)
{
    HRESULT         hResult;
    DWORD           dwMemType;
    LPD3DDEVICEDESC lpDeviceDesc;
    DDSURFACEDESC   ddsd;
    DDSCAPS			ddscaps;
    DWORD           dwWidth, dwHeight, dwBPP;

	// Check Initialization
    if ((! hWindow) || (! IsWindow (hWindow)) ||
		(! lpCurrDevice) || (! lpCurrMode) || 
		(! lpDD2) || (! lpD3D) || (! lpddsPrimary))
    {
        // Error, Not initialized properly before calling this method
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
        return hResult;
    }

	// 
	// Step 1.  Grab the Render surface (back buffer) 
	//			from the Primary surface (front buffer)
	//
   	ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
	hResult = lpddsPrimary->GetAttachedSurface (&ddscaps, &lpddsRender);
   	if (FAILED (hResult))
	{
		REPORTERR (hResult);
		return hResult;
	}

	
	//
	// Step 2.	Create and attach Z-buffer (optional)
	//
	
	// Get D3D Device description
	if (lpCurrDevice->isHardware ())
	{
		// Hardware device
		// Z-buffer has to be in Video memory
		lpDeviceDesc = &(lpCurrDevice->d3dHalDesc);
		dwMemType = DDSCAPS_VIDEOMEMORY;
	}
	else
	{
		// Software device 
		// Z-Buffer has to be in System memory
		lpDeviceDesc = &(lpCurrDevice->d3dHelDesc);
		dwMemType = DDSCAPS_SYSTEMMEMORY;
	}

	// Should we create a Z-buffer ?!?
    if ((isCreateZBuffer ()) && (lpDeviceDesc) &&
        (0L != lpDeviceDesc->dwDeviceZBufferBitDepth))
    {
		// Get Z-buffer surface info (w, h, bpp, video vs. system memory)
		ddsd.dwSize = sizeof(ddsd);
		hResult = lpddsPrimary->GetSurfaceDesc (&ddsd);
		if (FAILED (hResult))
		{
			REPORTERR (hResult);
			return hResult;
		}
		dwWidth   = ddsd.dwWidth;
		dwHeight  = ddsd.dwHeight;
		dwBPP	  = FlagsToBitDepth (lpDeviceDesc->dwDeviceZBufferBitDepth);

        // Create the z-buffer.
        ZeroMemory (&ddsd, sizeof(ddsd));
        ddsd.dwSize            = sizeof(ddsd);
        ddsd.dwFlags           = DDSD_CAPS   |
                                 DDSD_WIDTH  |
                                 DDSD_HEIGHT |
                                 DDSD_ZBUFFERBITDEPTH;
        ddsd.ddsCaps.dwCaps    = DDSCAPS_ZBUFFER | dwMemType;
        ddsd.dwWidth           = dwWidth;
        ddsd.dwHeight          = dwHeight;
        ddsd.dwZBufferBitDepth = dwBPP;
        hResult = lpDD2->CreateSurface (&ddsd, &lpddsZBuff, NULL);
        if (FAILED(hResult))
        {
            REPORTERR (hResult);

            // Note: we may be able to continue without a z buffer
			// So don't exit
        }
		else
		{
			// Attach Z-buffer to rendering surface
			hResult = lpddsRender->AddAttachedSurface (lpddsZBuff);
			if (FAILED (hResult))
			{
				REPORTERR (hResult);

				if (lpddsZBuff)
				{
					lpddsZBuff->Release ();
					lpddsZBuff = NULL;
				}

				// Note: we may be able to continue without a z buffer
				// So don't exit
			}
			//else
			//{
			// Note:  We could actually release the reference to the
			//        Z-Buffer here, if we don't need a pointer to it anymore.
			//        The back-buffer has an attachment to it now and it
			//        won't go away until the Back-buffer does.
			// lpddsZBuff->Release ();
			// lpddsZBuff = NULL;
			//}
		}
    }


    //
    // Step 4.  Create the D3D device interface
	//
	hResult = lpD3D->CreateDevice (lpCurrDevice->guid,
				 				   lpddsRender, 
							 	   &lpd3dDevice);
    if (FAILED (hResult))
    {
        REPORTERR (hResult);
        return hResult;
    }

	// Enumerate all Texture formats associated with this D3D device
	hResult = lpCurrDevice->LoadFormats (lpd3dDevice);
	if (FAILED (hResult))
	{
		// Error, no texture formats
		// Hope we can run OK without textures
	}

	// Mark as valid
	turnValidRenderOn ();


	//
	// Step 5.  Create the viewport
	//
	hResult = InitViewport ();
	if (FAILED (hResult))
		return hResult;
	

	// 
	// Step 6.	Allow Scene to create all objects dependent on us
	//
	if (lpd3dScene)
		hResult = lpd3dScene->Attach ();

    // Success
    return DD_OK;
} // End D3DWindow::InitRender



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::FiniRender
** Purpose: Destroys the Rendering surface
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::FiniRender (void)
{
	// Allow Scene to cleanup all objects dependent on us
	if (lpd3dScene)
		lpd3dScene->Detach ();

	// Cleanup viewport
	FiniViewport ();

	// Mark as invalid
	turnValidRenderOff ();

    // Release D3D Device
    if (lpd3dDevice)
    {
        lpd3dDevice->Release ();
        lpd3dDevice = NULL;
    }

    // Release Z Buffer
    if (lpddsZBuff)
    {
		// Detach Z-Buffer from back buffer
		if (lpddsRender)
			lpddsRender->DeleteAttachedSurface (0L, lpddsZBuff);

		// Release Z-Buffer
        lpddsZBuff->Release ();
        lpddsZBuff = NULL;
    }

    // Release rendering surface
    if (lpddsRender)
    {
        lpddsRender->Release ();
        lpddsRender = NULL;
    }

	// Success
	return DD_OK;
} // End D3DWindow::FiniRender



/*
**-----------------------------------------------------------------------------
**  Name:       D3DWindow::InitViewport
**  Purpose:    
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::InitViewport (void)
{
	HRESULT hResult;

	// Check Initialization
	if ((! lpD3D) || (! lpd3dDevice))
	{
		// Error, Not properly initialized before calling this method
		hResult = APPERR_NOTINITIALIZED;
        REPORTERR (hResult);
        return hResult;
	}

	// Create Viewport
    hResult = lpD3D->CreateViewport (&lpd3dViewport, NULL);
    if (FAILED (hResult))
    {
        REPORTERR (hResult);
        return hResult;
    }

	// Attach viewport to D3D device
    hResult = lpd3dDevice->AddViewport (lpd3dViewport);
    if (FAILED (hResult))
    {
		lpd3dViewport->Release ();
		lpd3dViewport = NULL;

        REPORTERR (hResult);
        return hResult;
    }

	// Set up Initial Viewport parameters
	hResult = UpdateViewport ();
    if (FAILED (hResult))
	{
		lpd3dDevice->DeleteViewport (lpd3dViewport);
		lpd3dViewport->Release ();
		lpd3dViewport = NULL;

        return hResult;
	}

	// Mark as valid
	turnValidViewportOn ();

	/// Success
	return DD_OK;
} // End D3DWindow::InitViewport
  


/*
**-----------------------------------------------------------------------------
**  Name:       D3DWindow::FiniViewport
**  Purpose:    Cleanup viewport
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::FiniViewport (void)
{
	// Mark as invalid
	turnValidViewportOn ();

	// Release D3D viewport
    if (lpd3dViewport)
    {
		lpd3dDevice->DeleteViewport (lpd3dViewport);
        lpd3dViewport->Release ();
        lpd3dViewport = NULL;
    }

	// Success
	return DD_OK;
} // End D3DWindow::FiniViewport

  

/*
**-----------------------------------------------------------------------------
**  Name:       D3DWindow::UpdateViewport
**  Purpose:    Keeps viewport updated with current window size
**  Notes:		
**
**	1. The viewport construction here assumes that you are rendering 
**		Triangles using the D3DVERTEX and D3DIM is doing Transform,
**		lighting, and rasterization for you.
**
**	2. If you are rendering triangles using D3DTLVERTEX and doing your
**	   own transform and lighting then you need to setup the viewport
**	   differently.   As follows:
**
**      // Replace the following values below:
**		dvClipX			= 0.0f;
**		dvClipY			= 0.0f;
**		dvClipWidth		= dwSurfW;
**		dvClipHeight	= dvSurfH;
**
**  3. This perserves the aspect ratio.  If you don't need or want to
**     perserve the aspect ratio then set inv_aspect = 1.0 below and
**     work this constant through the rest of the viewport setup.
**
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::UpdateViewport (void)
{
    HRESULT			hResult;
    D3DVIEWPORT2	d3dViewport;
	DWORD			dwSurfW, dwSurfH;

    // Check Parameters
	if ((! lpd3dDevice) || (! lpd3dViewport))
	{
		// Not properly initialized before calling this method
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
		return hResult;
	}

	// Get Surface Width and Height
	dwSurfW = abs (rSurf.right - rSurf.left);
	dwSurfH = abs (rSurf.bottom - rSurf.top);

	float inv_aspect;
	
	if (dwSurfW)
		inv_aspect = (float)dwSurfH/(float)dwSurfW;
	else
                inv_aspect = 1.0f;

    // Update Viewport
    ZeroMemory (&d3dViewport, sizeof(d3dViewport));
    d3dViewport.dwSize		= sizeof(d3dViewport);     // Always set size of structure!!!
    d3dViewport.dwX			= 0UL;
    d3dViewport.dwY			= 0UL;
    d3dViewport.dwWidth		= dwSurfW;
    d3dViewport.dwHeight	= dwSurfH;
    d3dViewport.dvClipX		= -1.0f;
    d3dViewport.dvClipY		= inv_aspect;
    d3dViewport.dvClipWidth	= 2.0f;
    d3dViewport.dvClipHeight = 2.0f * inv_aspect;
    d3dViewport.dvMinZ		= 0.0f;
    d3dViewport.dvMaxZ		= 1.0f;

	// Update Viewport
    hResult = lpd3dViewport->SetViewport2 (&d3dViewport);
    if (FAILED (hResult))
    {
        REPORTERR (hResult);
        return hResult;
    }

	// Update D3D device to use this viewport
    hResult = lpd3dDevice->SetCurrentViewport (lpd3dViewport);
    if (FAILED (hResult))
    {
        REPORTERR (hResult);
        return hResult;
    }

    // Success
    return DD_OK;
} // End D3DWindow::UpdateViewport

  

/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::DrawFrame
** Purpose: Paints current surface to window
** Notes:   
**
**	1. Full screen mode - we render to the back buffer
**	   and then flip the front and back buffers to make the
**	   changes visible
**
**  2. Windowed mode - We blt the render surface onto the primary surface
**
**  3. This routine is not written for optimal performance
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::DrawFrame (void)
{
    HRESULT hResult = DD_OK;

	// Check Initialization
    if (! isValid ())
    {
        // Error, not properly initialized
		hResult = APPERR_NOTINITIALIZED;
		//REPORTERR (hResult);
        return hResult;
    }

    if (isPaused ())
    {
        // Don't draw, if paused
        return DD_OK;
    }
    
    // Paint until we truly succeed or error out
    while (TRUE)
    {
		// Render D3D Scene
		if (lpd3dScene)
			hResult = lpd3dScene->Render ();

		if (SUCCEEDED (hResult))
		{
			// Flip Front and Back buffers (Primary, Render)
		    hResult =lpddsPrimary->Flip (lpddsRender, 1);

			// Did it work ?!?  
			if (SUCCEEDED (hResult))
			{
				// Success, exit
				return hResult;
			}
		}

        // Check if busy or drawing
        if ((DDERR_SURFACEBUSY == hResult) ||
            (DDERR_WASSTILLDRAWING == hResult))
        {
            // Try again
            continue;
        }

        // Check for lost surfaces
        while (DDERR_SURFACELOST == hResult)
        {
            // Restore surfaces
            hResult = Restore ();
        }

        // Check for real error
        if (FAILED (hResult))
        {
            // Error,
            REPORTERR (hResult);
            return hResult;
        }
    }

    // Success
    return DD_OK;
} // End D3DWindow::DrawFrame



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::Move
** Purpose: Moving full-screen windows not supported
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::Move (long x, long y)
{
	// Do nothing

    // Success
    return DD_OK;
} // D3DWindow::Move



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::Resize
** Purpose: Resizes window
** Notes:	Resizing Fullscreen windows not supported.
**			I.E. The Back buffer needs to stay the same size as the front
**			Buffer.
**
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::Resize (DWORD dwWidth, DWORD dwHeight)
{
	// Do nothing

	// Success
	return DD_OK;
} // End D3DWindow::Resize 



/*
**-----------------------------------------------------------------------------
**  Name:       D3DWindow::RealizePalette
**  Purpose:    
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::RealizePalette (void)
{
	HRESULT hResult;

	//
    // Realizing the palette using DirectDraw is quite different
    // from GDI. To realize the palette we call SetPalette()
    // each time our application is activated.
    //
    // NOTE: DirectDraw spots the fact that the new palette is the
    // same as the old one and so does not increase the reference
    // count of the palette.
    //

	if ((lpddsPrimary) && (lpddpPalette))
	{
		hResult = lpddsPrimary->SetPalette (lpddpPalette);
		if (FAILED (hResult))
        {
            REPORTERR (hResult);
            return hResult;
        }
	}

	// Success
	return DD_OK;
} // End D3DWindow::RealizePalette



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::toGDI
** Purpose: Setups for drawing to GDI surface
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::toGDI (void)
{
	HRESULT hResult;

	// Step 1. Restore system palette (optional)
	if (lpddpPalette) 
	{
		// Save the current palette
		hResult = lpddpPalette->GetEntries (0, 0, cPalette, lppeCurr);
		if (FAILED (hResult))
		{
			REPORTERR (hResult);
			return hResult;
		}

		// Restore the system palette into our device
		hResult = lpddpPalette->SetEntries (0, 0, cPalette, lppeSystem);
		if (FAILED (hResult))
		{
			REPORTERR (hResult);
			return hResult;
		}
	}

	// Step 2. Flip to GDI Surface
	if (lpDD2) 
	{
		hResult = lpDD2->FlipToGDISurface ();
		if (FAILED (hResult))
		{
			REPORTERR (hResult);
			return hResult;
		}
	}

	// Step 3.  Force window to redraw itself (on GDI surface)
	if ((hWindow) && (IsWindow (hWindow)))
	{
		DrawMenuBar (hWindow);
		RedrawWindow (hWindow, NULL, NULL, RDW_FRAME);
	}

	// Success
	return DD_OK;
} // End D3DWindow::toGDI



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::fromGDI
** Purpose: Restores from GDI
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::fromGDI (void)
{
	HRESULT hResult;

	// Restore current palette
	if (lpddpPalette)
	{
		hResult = lpddpPalette->SetEntries (0, 0, cPalette, lppeCurr);
		if (FAILED (hResult)) 
			return hResult;
	}

	// Success
	return DD_OK;
} // End D3DWindow::fromGDI



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::Pause
** Purpose: Pause any work on DD/D3D resources
** Notes:	
**
** For Fullscreen apps we need to setup the 
**
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::Pause (BOOL fPause)
{
	HRESULT hResult;

    // Turning pausing on/off ?!?
    if (fPause)
    {
		// Increment pause semaphore
        dwPaused++;

		// Are we pausing for the first time
        if (dwPaused == 1L)
        {
			// Flip to GDI surface
			hResult = toGDI ();
			if (FAILED (hResult))
				return hResult;
        }

    }
    else
    {
        if (dwPaused == 0L)
        {
            // Programmer Error, already unpaused
			hResult = APPERR_GENERIC;
			REPORTERR (hResult);
            return hResult;
        }

		// Are we unpausing for the last time ?!?
        if (dwPaused == 1L)
        {
			// Restore from GDI surface
			hResult = fromGDI ();
			if (FAILED (hResult))
				return hResult;
        }

        // Decrement pause semaphore
        dwPaused--;
    }

    // Success
    return DD_OK;
} // D3DWindow::Pause
  


/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::AttachScene
** Purpose: 
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::AttachScene (LPD3DScene lpNewScene)
{
	// Check parameters
	if (! lpNewScene)
	{
		return DDERR_INVALIDPARAMS;
	}

	// Save Scene pointer
	lpd3dScene = lpNewScene;

	// Inform scene of our existence
	lpd3dScene->Init (this);

	// Allow scene to create any objects dependent on us
	if (isValid ())
	{
		lpd3dScene->Attach ();
	}

	// Success
	return DD_OK;
} // End D3DWindow::AttachScene



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::DetachScene
** Purpose: 
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::DetachScene (void)
{
	// Cleanup Scene
	if (lpd3dScene)
	{
		lpd3dScene->Detach ();
		lpd3dScene->Fini ();
		lpd3dScene = NULL;
	}

	// Success
	return DD_OK;
} // End D3DWindow::DetachScene


  
/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::Restore
** Purpose: Restores lost surfaces
** Note:    Eventually we should inform the user somehow that
**          they need to redraw the surface but for now punt
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::Restore (void)
{
    HRESULT hResult;

	// Check Initialization
    if (! isValid ())
	{
		// Error, not properly initialized before calling this method
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
        return hResult;
	}

    // Restore Primary Surface
    if (lpddsPrimary)
    {
		hResult = lpddsPrimary->IsLost ();
		if (hResult != DD_OK)
		{
			hResult = lpddsPrimary->Restore ();
			if (FAILED (hResult))
				return hResult;
		}
    }

    // Restore Z Buffer
    if (lpddsZBuff)
    {
		hResult = lpddsZBuff->IsLost ();
		if (hResult != DD_OK)
		{
	        hResult = lpddsZBuff->Restore ();
		    if (FAILED (hResult))
			    return hResult;
		}
    }

    // Restore Rendering surface
    if (lpddsRender)
    {
		hResult = lpddsRender->IsLost ();
		if (hResult != DD_OK)
		{
	        hResult = lpddsRender->Restore ();
		    if (FAILED (hResult))
			    return hResult;
		}
    }

	// Allow D3D Scene to restore any surfaces
	if (lpd3dScene)
	{
		hResult = lpd3dScene->Restore ();
		if (FAILED (hResult))
			return hResult;
	}

    // Success
    return DD_OK;
} // End D3DWindow::Restore


  
/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::GetSurfaceRect
** Purpose: Get bounding rectangle of surface
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::GetSurfaceRect (RECT & rSurface)
{
	HRESULT hResult;

    if (! isValid ())
    {
		// Error, not properly initialized before calling this method
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
        return hResult;
    }

    // Return Surface rectangle
    rSurface = rSurf;

    // Success
    return DD_OK;
} // D3DWindow::GetSurfaceRect



/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::GetPrimaryRect
** Purpose: Get bounding rectangle of primary surface
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::GetPrimaryRect (RECT & rPrimary)
{
	HRESULT hResult;

    if (! isValid ())
    {
		// Error, not properly initialized before calling this method
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
        return hResult;
    }

    // Return Primary rectangle
    rPrimary = rPrim;

    // Success
    return DD_OK;
} // GetPrimaryRect
  

  
/*
**-----------------------------------------------------------------------------
**  Name:       D3DWindow::ChangeDesktop
**  Purpose:    The Primary Desktop has changed Modes
**	Notes:		
**
**	1.  Since we are a fullscreen app, this shouldn't effect us
**		until we restore the desktop mode.  Since DirectDraw should
**		be tracking this anyway.  We can just ignore doing anything
**
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::ChangeDesktop (void)
{
	// Do nothing

	// Success 
	return DD_OK;
} // D3DInfo::ChangeDesktop
  


  
/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::ChangeDriver
** Purpose: 
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::ChangeDriver (
	LPGUID		 lpGuidDD,
	LPD3DDevInfo lpDevHint,
	LPDDModeInfo lpModeHint)
{
	HRESULT		 hResult;
	LPGUID		 lpGuidD3D;
	LPDDDrvInfo  lpDrvNew,  lpDrvOld;
	LPDDModeInfo lpModeNew, lpModeOld;
	LPD3DDevInfo lpDevNew, lpDevOld;
	DWORD		 w, h, bpp, refresh;

	// Get New Driver
	lpDrvNew = ValidateDriver (lpGuidDD);
	if (! lpDrvNew)
	{
		// Error, invalid DD Guid
		hResult = APPERR_INVALIDPARAMS;
		REPORTERR (hResult);
        return hResult;
	}

	// Get requested D3D device
	if (lpDevHint)
		lpGuidD3D = &(lpDevHint->guid);
	else if (lpCurrDevice)
		lpGuidD3D = &(lpCurrDevice->guid);
	else
		lpGuidD3D = NULL;

	// Get requested mode
	if (lpModeHint)
		lpModeHint->GetMode (w, h, bpp, refresh);
	else
	{
		// Default to 640 x 480 x 16
		w		= 640;
		h		= 480;
		bpp		= 16;
		refresh = 0;
	}

	// Get new device and mode compatible with this driver
	if (! GetFullscreenMode (lpDrvNew, lpGuidD3D, w, h, bpp, refresh,
							 &lpModeNew, &lpDevNew))
	{
		// Error, unable to find a valid D3D Device
		// and Mode that work with this driver.
		hResult = APPERR_GENERIC;
		REPORTERR (hResult);
		return hResult;
	}
	
	// Save old defaults
	lpDrvOld	= lpCurrDriver;
	lpModeOld	= lpCurrMode;
	lpDevOld	= lpCurrDevice;

	// Destroy almost everything
	Fini ();

	// Set new defaults
	lpCurrDriver = lpDrvNew;
	lpCurrMode   = lpModeNew;
	lpCurrDevice = lpDevNew;

	// Re-create almost everything based on new driver, device, and mode
	hResult = Init ();
	if (FAILED (hResult))
	{
		// Try to restore old defaults
		Fini ();

		lpCurrDriver = lpDrvOld;
		lpCurrMode   = lpModeOld;
		lpCurrDevice = lpDevOld;

		Init ();
		return hResult;
	}

	// Notify the window of a successful change in Driver
	SendMessage (hWindow, D3DWIN_CHANGED_DRIVER, 0, 0);

	// Success
    return DD_OK;
} // End D3DWindow::ChangeDriver


  
/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::ChangeMode
** Purpose: Change the mode
**
**	Basic Algorithm:
**
**  1.  Validate new mode request
**  2.  Validate that the D3D device is compatible
**  3.  Destroy old mode
**  4.  Create new mode
**
**	- This can be complicated by the fact that the new mode is not
**    compatible with the current D3D device, in which case we need
**    to choose a new D3D device that will work with this mode.
**
**  - We don't normally allow mode changes for windowed mode.
**		- However the desktop mode could have changed underneath us,
**		  So make sure the desktop mode and our current mode match.
**		  If not, then change to the current desktop mode.
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::ChangeMode (
	DWORD w,			// Mode Width
	DWORD h,			// Mode Height
	DWORD bpp,			// Mode Bits Per Pixel
	DWORD refresh)		// Optional:  Mode refresh rate
{
	HRESULT		 hResult;
	LPDDDrvInfo  lpOldDrv;
	LPDDModeInfo lpOldMode, lpNewMode;
	LPD3DDevInfo lpOldDev, lpNewDev;

	// Check Initialization
	if ((! hWindow) || (! IsWindow (hWindow)))
	{
		// Error, Not properly initialized
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
		return hResult;
	}

	lpOldDrv  = lpCurrDriver;
	lpOldMode = lpCurrMode;
	lpOldDev  = lpCurrDevice;


	//
	// Step 1. Get New Mode
	//

	// Find new mode corresponding to w, h, bpp
	lpNewMode = lpOldDrv->FindMode (w, h, bpp, 0, NULL);
	if (! lpNewMode)
	{
		// Error, Invalid Mode parameters
		hResult = APPERR_INVALIDPARAMS;
		REPORTERR (hResult);
		return hResult;
	}

	// 
	// Step 2.   Check if Device needs to be changed as well
	//
	if (lpNewMode->ModeSupported (lpOldDev))
	{
		lpNewDev = NULL;
	}
	else
	{
		LPD3DDevInfo lpNextBest;
		lpNewDev = lpOldDrv->FindDeviceSupportsMode (&lpOldDev->guid,
													 lpNewMode,
													 &lpNextBest);
		if (! lpNewDev)
		{
			if (! lpNextBest)
			{
				// No D3D device is compatible with this new mode
				hResult = APPERR_GENERIC;
				REPORTERR (hResult);
				return hResult;
			}
			lpNewDev = lpNextBest;
		}
	}

	// 
	// Step 3.	Destroy current Mode
	//
	FiniRender ();
	FiniPrimary ();
//  FiniFullscreenMode ();		// Don't do this => unnecessary mode switch

	//
	// Step 4.  Create new mode
	//
	lpCurrMode = lpNewMode;
	if (lpNewDev)
		lpCurrDevice = lpNewDev;

	// Change Mode
	hResult = InitFullscreenMode ();
	if (FAILED (hResult))
		return hResult;

	// Create Primary Surface
	hResult = InitPrimary ();
	if (FAILED (hResult))
	{
		// Try to restore old mode
		lpCurrMode	 = lpOldMode;
		lpCurrDevice = lpOldDev;

		InitFullscreenMode ();
		InitPrimary ();
		InitRender ();

		return hResult;
	}

	// Create Render surface
	hResult = InitRender ();
	if (FAILED (hResult))
	{
		FiniPrimary ();
	//  FiniFullscreenMode ();		// Unnecessary mode switch

		// Try to restore old mode
		lpCurrMode	 = lpOldMode;
		lpCurrDevice = lpOldDev;

		InitFullscreenMode ();
		InitPrimary ();
		InitRender ();

		return hResult;
	}

	// Notify the window of a successful change in Mode
	SendMessage (hWindow, D3DWIN_CHANGED_MODE, 0, 0);

	// Success
    return DD_OK;
} // End D3DWindow::ChangeMode

  

/*
**-----------------------------------------------------------------------------
** Name:    D3DWindow::ChangeDevice
** Purpose: Change to a new D3D device (RAMP, RGB, Hardware, etc.)
** Notes:
**
**  Algorithm:
**		- Destroy the current D3D Device (and associated surfaces)
**		- Recreate a new D3D device from the new GUID
**
**  1.	The new D3D Device may not be supported by the current DD Device.
**  2.  The new D3D Device may not be compatible with the current Mode
**		- Since we are fullscreen, just pick a new mode that is compatible
**
**-----------------------------------------------------------------------------
*/

HRESULT D3DWindow::ChangeDevice (
	LPGUID		 lpD3DGuid,
	LPDDModeInfo lpModeHint)
{
    HRESULT         hResult;
	LPDDDrvInfo		lpDrvOld;
	LPDDModeInfo    lpModeNew, lpModeOld;
	LPD3DDevInfo	lpDevNew, lpDevOld;

	// Check Parameters
	if (! lpD3DGuid)
	{
		hResult = APPERR_INVALIDPARAMS;
		REPORTERR (hResult);
        return hResult;
	}
	
	// Check Initialization
    if (! isValid () || (! lpddsRender))
	{
		hResult = APPERR_NOTINITIALIZED;
		REPORTERR (hResult);
        return hResult;
	}

	// Save Original State
	lpDrvOld	= lpCurrDriver;
	lpModeOld   = lpCurrMode;
	lpDevOld	= lpCurrDevice;

	// Verify new D3D device belongs to current DD driver
	lpDevNew = lpDrvOld->FindDevice (lpD3DGuid, NULL);
	if (! lpDevNew)
	{
		hResult = APPERR_INVALIDPARAMS;
		REPORTERR (hResult);
        return hResult;
	}

	//
	//	Step 1. Verify new D3D device is supported with current mode
	//
	if (lpModeHint)
		lpModeNew = lpModeHint;
	else
		lpModeNew = lpModeOld;
	if (! lpModeNew->ModeSupported (lpDevNew))
	{
		// We are a full screen app, so we can do what we want
		// Pick a new mode that is compatible with this device
		LPDDModeInfo lpNextBest;
		DWORD w, h, bpp, refresh;

		lpModeNew->GetMode (w, h, bpp, refresh);

		lpModeNew = lpDrvOld->FindModeSupportsDevice (w, h, bpp, 0,
													  lpDevNew,
													  &lpNextBest);
		if (! lpModeNew)
		{
			if (! lpNextBest)
			{
				// Error , no compatible mode found!!!
				hResult = APPERR_GENERIC;
				REPORTERR (hResult);
				return hResult;
			}
			lpModeNew = lpNextBest;
		}
	}
	if (lpModeNew == lpModeOld)
		lpModeNew = NULL;


	//
	//	Step 2.  Destroy Old D3D Device (and mode)
	//
	FiniRender ();
	if (lpModeNew)
	{
		FiniPrimary ();
		// FiniFullscreenMode ();	// Unnecessary mode switch
	}


	//
	//	Step 3. Create new D3D Device (new mode optional)
	//

	// Set new D3D device (and mode)
	if (lpModeNew)
		lpCurrMode = lpModeNew;
	lpCurrDevice = lpDevNew;

	// Create new mode, if necessary
	if (lpModeNew)
	{
		// Change Mode
		hResult = InitFullscreenMode ();
		if (FAILED (hResult))
		{
			// Try to restore original mode and device
			lpCurrDevice = lpDevOld;
			lpCurrMode   = lpModeOld;

			InitFullscreenMode ();
			InitPrimary ();
			InitRender ();

			return hResult;
		}

		// Create Primary
		hResult = InitPrimary ();
		if (FAILED (hResult))
		{
			// Try to restore original mode and device
			lpCurrDevice = lpDevOld;
			lpCurrMode   = lpModeOld;

			InitFullscreenMode ();
			InitPrimary ();
			InitRender ();

			return hResult;
		}
	}

	// Create new D3D Device
	hResult = InitRender ();
	if (FAILED (hResult))
	{
		// Try to restore original mode and device
		if (lpModeNew)
			lpCurrMode = lpModeOld;
		lpCurrDevice = lpDevOld;

		if (lpModeNew)
		{
			FiniPrimary ();
			InitFullscreenMode ();
			InitPrimary ();
		}
		
		InitRender ();

		// Return Error
		REPORTERR (hResult);
		return hResult;
	}

	// Notify the window of a successful change in device
	SendMessage (hWindow, D3DWIN_CHANGED_DEVICE, 0, 0);

    // Success
    return DD_OK;
} // End D3DWindow::ChangeDevice



/*
**-----------------------------------------------------------------------------
** End of File
**-----------------------------------------------------------------------------
*/
