/******************************************************************************
 * ATI 3D RAGE PRO SDK sample code                                            *
 *                                                                            *
 * R3expl3.c - RAGE PRO 16 Bit Specular Lighting Example.					  *
 *                                                                            *
 * Copyright (c) 1997 ATI Technologies Inc. All rights reserved.              *
 *																			  *
 * This application requires a 2MB RAGE PRO and DirectX 3.0 or later, as	  *
 * well as the latest drivers that contain support for specular color and	  *
 * the C3D_TLVERTEX vertex type. This example runs in a 16-bit mode but       *
 * specular color can also be used in 32-bit modes.						      *
 *																			  *
 * This example displays a red and blue textured triangle on a black 		  *
 * background. White specular color is set for the lower left vertex on the	  *
 * triangle. Vertex descriptions use the newly implemented C3D_TLVERTEX		  *
 * vertex type.																  *
 *																			  *
 ******************************************************************************/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ddraw.h>

#include "ati3dcif.h"

//Convenient texture info structure, we package a 
//surface description, surface pointer and texture
//handle for any textures that we'll be using.
typedef struct _Texture
{
    LPDIRECTDRAWSURFACE lpDDSTex;
    DDSURFACEDESC       ddsd;
    C3D_HTX             hTX;
} Texture;


static LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static BOOL InitApp (void);
static void CloseApp (void);
static BOOL InitDirectDraw (void);
static void CloseDirectDraw (void);
static BOOL InitATI3DCIF (void);
static void CloseATI3DCIF (void);
static BOOL SetATI3DCIFInitialState (void);
static BOOL LoadTexture (Texture *pTex);
static void UnloadTexture (Texture *pTex);
static BOOL CopyTextureData (Texture *pTex);
static void InitVertexLists (void);
static void DrawFrame (void);
static void RenderScene (void);
static BOOL IsDisplay16Bit (void);	
static void ErrBox(const char *errStr, ... );
static void _Assert (const char *file, int line, const char *msg);

#define ASSERT(x)   if( !(x) )  _Assert( __FILE__, __LINE__, #x)

#define RELEASE(x)	{												\
						if (x == NULL) 								\
							ErrBox ("Tried to free Null pointer");	\
						else										\
						{											\
							x->Release(); 							\
							x = NULL;								\
						}											\
					}

#define OUR_WIDTH			640			//main window client area width     
#define OUR_HEIGHT			480			//main window client area height

#define TEXTURE_WIDTH		128			//texture dimensions
#define TEXTURE_HEIGHT		128			
#define TEXTURE_LEVEL		7			//texture width as a power of 2

#define COLORDEPTH_IN_BYTES	2			//16 bit video mode has 2 bytes per pixel
#define FILL_COLOR			0			//use black for background fill color

Texture gTex = {NULL};		

LPDIRECTDRAW        glpDD = NULL;			//DirectDraw object
LPDIRECTDRAWSURFACE glpDDSPrimary = NULL;	//DirectDraw primary surface
LPDIRECTDRAWSURFACE glpDDSBack = NULL;		//DirectDraw back surface
LPDIRECTDRAWCLIPPER glpDDClip = NULL;		//DirectDraw clipper	
C3D_HRC             ghRC = FALSE;			//ATI3DCIF rendering context
BOOL                gbCIFInit = FALSE;		//ATI3DCIF driver interface state

char                gszErrMsg[64];			//global error message text
HWND				ghWnd;					//global window handle 


//These arrays hold our 3D primitive description. The primitive is described 
//using the C3D_TLVERTEX vertex type which is made up of x,y,z,w, diffuse color 
//(rgba), specular color (rgba) and s and t. Color values are specified as
//unsigned ints, all others are floats. This vertex type is portable across 
//many APIs (including Direct3D and OpenGL). In addition, it is faster on the 
//RAGE PRO than the older C3D_VTCF vertex. We will map a texture to this 
//primitive and then apply white specular lighting to it.
C3D_TLVERTEX gvtl[3];
C3D_TLVERTEX *gpvtl[3];
  

//WinMain is standard Windows creation code. PeekMessage is used for the
//main event loop. When there are no messages pending, control drops into 
//RenderScene. 
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
										LPSTR lpCmdLine, int nCmdShow)
{
	MSG msg;
    WNDCLASSEX wndclass;
	RECT rc;
	DWORD dwStyle;
	const char *pszClassName = "RAGE PRO Specular Example";			    
	const char *pszWindowCaption = "RAGE PRO Specular Example";

	memset (&wndclass, 0, sizeof(WNDCLASSEX));
	wndclass.cbSize = sizeof(WNDCLASSEX);
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon (hInstance, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH) GetStockObject (GRAY_BRUSH);
    wndclass.lpszMenuName = pszClassName;
    wndclass.lpszClassName = pszClassName;
	wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);

    RegisterClassEx (&wndclass);

	dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;

   	rc.top = rc.left = 0;
	rc.bottom = OUR_HEIGHT;
	rc.right = OUR_WIDTH;

	//Get the window extents for a window whose client
	//area is OUR_WIDTH x OUR_HEIGHT.
	AdjustWindowRectEx (&rc, dwStyle, FALSE, WS_EX_APPWINDOW);

	ghWnd = CreateWindowEx (WS_EX_APPWINDOW,
							pszClassName,
   							pszWindowCaption,
							dwStyle,
							50,
							50,
							rc.right - rc.left,
							rc.bottom - rc.top,
							NULL,
							NULL,
							hInstance,
							NULL);

    if (!ghWnd) return FALSE;

    if (!InitApp ()) return FALSE;

	ShowWindow (ghWnd, nCmdShow);
    UpdateWindow (ghWnd);
	
    while (TRUE)
    {
        if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
				break;
             
            TranslateMessage (&msg);
            DispatchMessage (&msg);
        } 
		else
		{
			RenderScene ();
		}
    } 

    return msg.wParam;
}
 

//Main window message handling procedure. Since most of 
//the work is done in DrawFrame and RenderScene, we only
//handle window exiting and destruction here. If the user
//tries to change focus we exit. 
LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_KEYDOWN:
            switch (wParam)
            {
                case VK_ESCAPE:
                    DestroyWindow (hWnd);
                    return 0;
            } 
            return 0;

		case WM_KILLFOCUS:	 	  
			PostQuitMessage (0);
			return 0;
			
		case WM_DESTROY:
            CloseApp ();
            PostQuitMessage (0);
            return 0;
    } 

    return DefWindowProc (hWnd, message, wParam, lParam);
}
 

//Function wrapper for handling all setup and initialization
//of the application's global, object and texture data. 
static BOOL InitApp (void)
{
    //Create and initialize the main DirectDraw object.
	if (InitDirectDraw ())
	{
		//Initialize ATI3DCIF and set up a rendering context.
		if (InitATI3DCIF ())
		{
			//Initialize global rendering state.
			if(SetATI3DCIFInitialState ())
			{
				//Load all our texture data.
				if (LoadTexture (&gTex))
				{
					//Initialize the primitive list.
					InitVertexLists ();
					
					return TRUE;

					//Reminder for future expansion...
					//UnloadTexture (&gTex);
				}
				else
					ErrBox ("Could not load texture data"); 
			}
			else
				ErrBox ("Could not set ATI3DCIF initial state"); 
			CloseATI3DCIF ();
		}
		else
			ErrBox ("ATI3DCIF not initialized");
		CloseDirectDraw ();
	}
	else
		ErrBox ("DirectDraw not initialized"); 
	return FALSE;
}


//Unload the texture data and the ATI3DCIF driver interface and 
//release the DirectDraw objects.
static void CloseApp (void)
{
	UnloadTexture (&gTex);
	CloseATI3DCIF ();
	CloseDirectDraw ();
} 


//Initialize DirectDraw for a windowed application and set up surfaces:
// Set up a DirectDraw object (DirectDrawCreate).
// Make sure we are currently in a 16-bit mode.
// Let Windows know that we will be sharing windows resources (such as the 
//	 GDI) for our application  (SetCooperativeLevel using DDSCL_NORMAL flag).
// Create the primary and back surfaces in video memory.
//	 In order to do this we fill out a surface description and pass it to  
//   CreateSurface. We do this once for the front and once for the back. The
//	 primary surface will automatically be allocated to the size of the screen, 
//	 so it is not necessary to set up the .dwWidth and .dwHeight fields in 
//	 the surface description (DDSURFACEDESC). 
// Since we are running in a windowed mode, we must set up a clipper for our
//	 window. In order to do this we create the clipper object (CreateClipper),
//	 associate the clipper to our window (SetHWnd), and attach the clipper to 
//	 the primary surface (SetClipper).
static BOOL InitDirectDraw (void)
{
	if (DirectDrawCreate (NULL, &glpDD, NULL) == DD_OK)
	{
		if (IsDisplay16Bit ())
		{
			if (glpDD->SetCooperativeLevel (ghWnd,DDSCL_NORMAL) == DD_OK)
			{
				//Create the primary surface in video memory.
				DDSURFACEDESC ddsd;
				memset (&ddsd, 0, sizeof (ddsd));
				ddsd.dwSize = sizeof (ddsd);
				ddsd.dwFlags = DDSD_CAPS;
				ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY;

				if (glpDD->CreateSurface (&ddsd, &glpDDSPrimary, NULL) == DD_OK)
				{
					//Create a plain offscreen surface in video memory 
					//for the back buffer.
					memset (&ddsd, 0, sizeof (ddsd));
					ddsd.dwSize = sizeof (ddsd);
					ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
					ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
					ddsd.dwHeight = OUR_HEIGHT;
					ddsd.dwWidth = OUR_WIDTH;

					if (glpDD->CreateSurface (&ddsd, &glpDDSBack, NULL) == DD_OK)
					{
						if (glpDD->CreateClipper (0, &glpDDClip, NULL) == DD_OK)
						{
							if (glpDDClip->SetHWnd (0, ghWnd) == DD_OK)
							{
								if (glpDDSPrimary->SetClipper (glpDDClip) == DD_OK)
									return TRUE;
								else
									ErrBox ("Could not set clipper to front buffer");
							}
							else
								ErrBox ("Could not set clipper to window");
							RELEASE (glpDDClip);
						}
						else
							ErrBox ("Could not create clipper");
						RELEASE (glpDDSBack);
					}
					else
						ErrBox ("Could not create back buffer");
					RELEASE (glpDDSPrimary);
				}
				else
					ErrBox ("Could not create the primary surface");
				glpDD->SetCooperativeLevel (NULL, DDSCL_NORMAL);
			}
			else
				ErrBox ("Could not set the cooperative level");
		}
		else
			ErrBox ("Please reset to 16 bit display mode");
		RELEASE (glpDD);
	}
	else
        ErrBox ("Could not create DirectDraw object");
    return FALSE;
}
     

//Free DirectDraw surfaces and destroy main DirectDraw object.
static void CloseDirectDraw (void)
{
	RELEASE (glpDDClip);
	RELEASE (glpDDSBack);
	RELEASE (glpDDSPrimary);
	glpDD->SetCooperativeLevel (NULL, DDSCL_NORMAL);
	RELEASE (glpDD);
} 


//Load the ATI3DCIF driver interface module and create a rendering context. 
//We set a global flag here (gbCIFInit) indicating that the module is loaded. 
//This flag will get checked before we attempt to unload the module in 
//CloseATI3DCIF.
static BOOL InitATI3DCIF (void)
{
	gbCIFInit = FALSE;

	if (ATI3DCIF_Init () == C3D_EC_OK)					
    {
		if ( (ghRC = ATI3DCIF_ContextCreate ()) != NULL)
		{
			gbCIFInit = TRUE;		//set flag indicating driver is loaded
			
			return TRUE;

			//Reminder for future expansion...
			//ATI3DCIF_ContextDestroy (ghRC);
		}
		else
	        ErrBox ("Could not create 3D rendering context");
		ATI3DCIF_Term ();
	}
	else
        ErrBox ("Could not initialize ATI3DCIF driver interface");

	return FALSE;
}


//Destroy the rendering context and unload the ATI3DCIF module.
static void CloseATI3DCIF (void)
{
    ASSERT (ghRC);
	ASSERT (gbCIFInit);

	ATI3DCIF_ContextDestroy (ghRC);
    ghRC = NULL;
    
    ATI3DCIF_Term ();
    gbCIFInit = FALSE;
} 
 

//Set up global rendering state. Once a state is set it will remain
//active for the life of the application unless it is explicitly 
//modified by a call to ATI3DCIF_ContextSetState. Generallly states that
//need to be modified are not set here, but are set/reset in the frame
//display loop (e.g. DrawFrame). We will set our initial state up for 
//specular enabled texture mapping where we use the C3D_TLVERTEX vertex
//type.
static BOOL SetATI3DCIFInitialState (void)
{
	BOOL bTMap = TRUE;							//enable texture mapping
	BOOL bSpec = TRUE;							//enable specular color
	C3D_EVERTEX vtype = C3D_EV_TLVERTEX;		//use TL vertex type

	if (ATI3DCIF_ContextSetState(ghRC, C3D_ERS_VERTEX_TYPE, &vtype) == C3D_EC_OK)
	{
		if (ATI3DCIF_ContextSetState (ghRC, C3D_ERS_TMAP_EN, &bTMap) == C3D_EC_OK)
		{
			if (ATI3DCIF_ContextSetState(ghRC, C3D_ERS_SPECULAR_EN, &bSpec) == C3D_EC_OK)
				return TRUE;
			else
				ErrBox ("Couldn't enable specular color.");
		}
		else
			ErrBox ("Couldn't enable texture mapping.");
	}
	else
		ErrBox ("Couldn't change vertex type to TL vertex");

	return FALSE;
}


//Perform the blit. Remember that DirectDraw defines the width and height
//of the front (primary) buffer to be that of the screen.
void RenderScene (void)
{
	//Prepare the offscreen buffer to be displayed.
	DrawFrame ();

	//Get rectangle that describes client area of main window.
	RECT rcFront;
	GetClientRect (ghWnd, &rcFront);

	//Get upper left point in client area in screen coords.
	POINT pt = {0,0};				
	ClientToScreen (ghWnd, &pt);	
	OffsetRect (&rcFront, pt.x, pt.y); 	 

   	//Now blit the back buffer to the correct area on the front buffer,
	//where it will be displayed.  
	glpDDSPrimary->Blt (&rcFront, glpDDSBack, NULL, DDBLT_WAIT, NULL);
}


//Draw the animation frame.
// Fill background with black. We do this by filling out a DDBLTFX structure
//	 and blitting the color to the back buffer.
// Obtain a valid surface pointer to the back (rendering) buffer and pass
//	 it to the ATI3DCIF driver interface module.
// Set drawing surface pitch. The pitch value is specified in PIXELS, not in 
//	 bytes per scanline.
// Select a texture map to be displayed. We need to tell the ATI3DCIF what
//	 texture map (bitmap) we want to render. We do this by selecting the
//	 texture into the rendering context. This call could conceivably
//	 have been placed in SetATI3DCIFInitialState, since this simple example 
//	 only uses one texture. However, since most applications will probably 
//	 want to switch textures between frames, the call was placed here.
// Perform 3D rendering. To do this, we switch the hardware into it's 3D 
//	 rendering state (ATI3DCIF_RenderBegin), render the primitive list using 
//	 the current rendering context information (set up by all those calls to 
//	 ContextSetState) to the back buffer (ATI3DCIF_RenderPrimList), and 
//	 switch back (ATI3DCIF_RenderEnd).
static void DrawFrame (void)
{
    DDSURFACEDESC ddsd;
	DDBLTFX ddbltfx;
	DWORD pitch;
    
    //Color fill the back surface.
    memset (&ddbltfx, 0, sizeof (ddbltfx));
    ddbltfx.dwSize = sizeof (ddbltfx);
    ddbltfx.dwFillColor = FILL_COLOR; 
    glpDDSBack->Blt (NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);

    //Lock/Unlock the back surface to get a valid surface pointer.
    memset (&ddsd, 0, sizeof (ddsd));
    ddsd.dwSize = sizeof (ddsd);
    if (glpDDSBack->Lock (NULL, &ddsd, DDLOCK_WAIT, NULL) != DD_OK) return;
	
	glpDDSBack->Unlock (ddsd.lpSurface);

	//Set the pointer to the frame buffer address of the back surface.
	ATI3DCIF_ContextSetState (ghRC, C3D_ERS_SURF_DRAW_PTR, (C3D_PRSDATA) &(ddsd.lpSurface));

	//Set the pitch of the drawing surface. Note that pitch is in PIXELS.
	pitch = ddsd.lPitch / COLORDEPTH_IN_BYTES;
	ATI3DCIF_ContextSetState (ghRC, C3D_ERS_SURF_DRAW_PITCH, (C3D_PRSDATA) &pitch);

	//Select our texture into the rendering context.
	ASSERT (gTex.hTX);		//make sure handle is valid before passing its address
	ATI3DCIF_ContextSetState (ghRC, C3D_ERS_TMAP_SELECT, &gTex.hTX);
		
	//Switch to 3D mode.
	if (ATI3DCIF_RenderBegin (ghRC) != C3D_EC_OK) return;

	//Draw the list.
	ATI3DCIF_RenderPrimList ((C3D_VSTRIP *) gpvtl, 3);

	//Switch back to 2D mode.
	ATI3DCIF_RenderEnd ();
} 


//Load a texture into a video memory surface and register it. The
//process is as follows:
//	Copy the texture data to the video memory surface.
//	Fill out a C3D_TMAP struct with the required texture information.
//	Register the texture with the ATI3DCIF using the C3D_TMAP struct.  
static BOOL LoadTexture (Texture *pTex)
{
	ASSERT (pTex);

	//Set up a texture surface in video memory and copy the texture
	//data to it.
	if (CopyTextureData (pTex) == TRUE)
	{
		//Fill out a C3D_TMAP struct.
		C3D_TMAP TMap;
		memset (&TMap, 0, sizeof (TMap));
		TMap.u32Size = sizeof (TMap);

		TMap.apvLevels[0] = pTex->ddsd.lpSurface;
		TMap.bMipMap = FALSE;
		TMap.u32MaxMapXSizeLg2 = TEXTURE_LEVEL;
		TMap.u32MaxMapYSizeLg2 = TEXTURE_LEVEL;
		TMap.eTexFormat = C3D_ETF_RGB565;

		//Register the texture using the C3D_TMAP struct. 
		if (ATI3DCIF_TextureReg (&TMap, &(pTex->hTX)) == C3D_EC_OK)
	  		return TRUE;

			//Reminder for future expansion...
			//ATI3DCIF_TextureUnreg(pTex->hTX);
		else
			ErrBox ("Error registering texture");
	}
	else
		ErrBox ("Error copying texture data to video memory surface");

    return FALSE;
} 


//Unregister a texture and release it's surface pointer.
static void UnloadTexture (Texture *pTex)
{
	ASSERT (pTex);
    ASSERT (pTex->hTX);
    
    ATI3DCIF_TextureUnreg (pTex->hTX);
	RELEASE (pTex->lpDDSTex);
}


//Create an offscreen surface in video memory and copy the texture data 
//to it. The surface color depth will default to that of the current video 
//mode, in this case 16 bit. Note that the format of the surface as created
//by DirectDraw is not used by the ATI3DCIF. The ATI3DCIF uses the raw data 
//off the surface and interprets it according to how the texture format is 
//specified in the .eTexFormat member of the C3D_TMAP struct. Therefore,
//it is not strictly necessary to explicitly set up the color format of 
//the DirectDraw surface used for the texture.
static BOOL CopyTextureData (Texture *pTex)
{
    HRESULT ddrval;

	memset (&(pTex->ddsd), 0, sizeof (pTex->ddsd));
	pTex->ddsd.dwSize = sizeof (pTex->ddsd);
	pTex->ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	pTex->ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
	pTex->ddsd.dwWidth =  TEXTURE_WIDTH;
	pTex->ddsd.dwHeight =  TEXTURE_HEIGHT;

	ddrval = glpDD->CreateSurface (&(pTex->ddsd), &pTex->lpDDSTex, NULL);
	if (ddrval == DD_OK)
	{
	    //Get a valid pointer to the texture surface.
		memset (&(pTex->ddsd), 0, sizeof (pTex->ddsd));
		pTex->ddsd.dwSize = sizeof (pTex->ddsd);

		if (pTex->lpDDSTex->Lock (NULL, &(pTex->ddsd),	DDLOCK_WAIT, NULL) == DD_OK)
		{
			pTex->lpDDSTex->Unlock (pTex->ddsd.lpSurface);

			unsigned short *texSurfBase = (unsigned short *)pTex->ddsd.lpSurface;

			//Copy image to DirectDraw texture surface in video memory.
			//The image will consist of alternating red and blue vertical
			//stripes every 16 texels.
			for (int y=0; y<TEXTURE_HEIGHT; y++)
				for (int x=0; x<TEXTURE_WIDTH; x++)
					*texSurfBase++ = (x&16)?0xF800:0x001F;	
						
			return TRUE;
		}
		else
			ErrBox ("Surface Lock failed");
		RELEASE (pTex->lpDDSTex);
	}
	else
	{
		if (ddrval == DDERR_OUTOFVIDEOMEMORY)
			ErrBox ("Out of vidmem for texture");
		else
			ErrBox ("Could not create texture surface in video memory");
	}

	return FALSE;
}


//Fill out our vertex list arrays. Vertex 2 (lower left of the triangle)
//will have a specular color of white assigned to it.
static void InitVertexLists (void)
{
	gvtl[0].x = 100.0f; gvtl[0].y = 100.0f; gvtl[0].z = 0.0f; gvtl[0].w = 1.0f; 
	gvtl[1].x = 540.0f; gvtl[1].y = 240.0f; gvtl[1].z = 0.0f; gvtl[1].w = 1.0f; 
	gvtl[2].x = 100.0f; gvtl[2].y = 380.0f; gvtl[2].z = 0.0f; gvtl[2].w = 1.0f; 

	gvtl[0].r = 0;   gvtl[0].g = 0;   gvtl[0].b = 0;   
	gvtl[1].r = 255; gvtl[1].g = 255; gvtl[1].b = 255; 
	gvtl[2].r = 255; gvtl[2].g = 255; gvtl[2].b = 255; 

	gvtl[0].s = 0.0f; gvtl[0].t = 0.0f;	
	gvtl[1].s = 1.0f; gvtl[1].t = 0.0f;	
	gvtl[2].s = 0.0f; gvtl[2].t = 1.0f;	

	gvtl[0].specular = 0x000000;
	gvtl[1].specular = 0x000000;
	gvtl[2].specular = 0xFFFFFF;
	
	for (int i=0; i<3; i++)
		gpvtl[i] = &(gvtl[i]);
}


//Makes sure the current mode is 16-bit. Windowed applications dont have 
//the option of calling SetDisplayMode, so we must make sure we are in
//a 16 bit display mode.
static BOOL IsDisplay16Bit (void)
{
    DDSURFACEDESC ddsd;
	memset (&ddsd, 0, sizeof (ddsd));
	ddsd.dwSize = sizeof (ddsd);
	glpDD->GetDisplayMode (&ddsd);

	if (ddsd.ddpfPixelFormat.dwRGBBitCount == 16)
		return TRUE;

	return FALSE;
}


static void ErrBox(const char *errStr, ... )
{	
	va_list vl;

	va_start (vl, errStr);
	wvsprintf (gszErrMsg, errStr, vl);				
	MessageBox (ghWnd, gszErrMsg, NULL, MB_OK);	
	va_end (vl);
}


#include <stdio.h>		//needed for sprintf

static void _Assert (const char *file, int line, const char *msg)
{
	int result;	
	static char buf[1024];

	sprintf (buf, "Assertion Failed %s at %d:  %s", file, line, msg);

	result = MessageBox (NULL, buf, "Assertion Failure", MB_OKCANCEL | 
												MB_APPLMODAL | MB_ICONERROR);

	if (result == IDCANCEL)
		PostQuitMessage (0);
}



