// Copyright © 1998, Bruce A. Lewis
// This code may not be re-used in a commercial product
// of any kind without express written consent from
// the author, Bruce A. Lewis.
//
/////////////////////////////////////////////////////////////////////////////////////
// Windows Includes...
/////////////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <fcntl.h>
#include <time.h>

/////////////////////////////////////////////////////////////////////////////////////
// DirectX Includes...
/////////////////////////////////////////////////////////////////////////////////////
#include <direct.h>
#include <ddraw.h>
#include <dinput.h>
#include <dsound.h>
#include <dplay.h>

/////////////////////////////////////////////////////////////////////////////////////
// Application Includes...
/////////////////////////////////////////////////////////////////////////////////////
#include "resource.h"  // Required for Win32 Resources

/////////////////////////////////////////////////////////////////////////////////////
// "WinDoom" Includes...
/////////////////////////////////////////////////////////////////////////////////////
#include "doomdef.h"
#include "doomstat.h"
#include "d_event.h"
#include "d_main.h"
#include "m_argv.h"
#include "g_game.h"
#include "i_system.h"
#include "m_music.h"
#include "i_cd.h"
#include "i_midi.h"
#include "dxerr.h"

//#include "d_console.h"
#include "dconsole.h"  //11.26.98 for compatibility
/////////////////////////////////////////////////////////////////////////////////////
// DirectX Defines and Data
/////////////////////////////////////////////////////////////////////////////////////

#undef RELEASE
#ifdef __cplusplus
#define RELEASE(x) if (x != NULL) {x->Release(); x = NULL;}
#else
#define RELEASE(x) if (x != NULL) {x->lpVtbl->Release(x); x = NULL;}
#endif

/////////////////////////////////////////////////////////////////////////////////////
// Direct Sound
/////////////////////////////////////////////////////////////////////////////////////

#define SB_SIZE       20480
#define NUM_DSBUFFERS 256

BOOL SetupDirectSound();
void ShutdownDirectSound(void);

LPDIRECTSOUND        lpDS;
LPDIRECTSOUNDBUFFER  lpDSPrimary;
LPDIRECTSOUNDBUFFER  lpDSBuffer[NUM_DSBUFFERS];

/////////////////////////////////////////////////////////////////////////////////////
// Direct Input
/////////////////////////////////////////////////////////////////////////////////////

void HandleKeyStrokes(void);
void KeyboardHandler(void);
BOOL InitDirectInput(void);
void ShutdownDirectInput(void);

LPDIRECTINPUT        lpDirectInput = 0;
LPDIRECTINPUTDEVICE  lpMouse       = 0;
LPDIRECTINPUTDEVICE2 lpJoystick    = 0;
LPDIRECTINPUTDEVICE  lpKeyboard    = 0;

BOOL          bDILive = FALSE;
char          szMouseBuf[1024];
unsigned char diKeyState[256];

extern int    always_run;
extern int    mvert;
extern int    keylink;

extern int    joyb1;
extern int    joyb2;
extern int    joyb3;
extern int    joyb4;
extern int    mouseb1;
extern int    mouseb2;
extern int    mouseb3;

extern boolean         gamekeydown[256]; 


/////////////////////////////////////////////////////////////////////////////////////
// Direct Draw
/////////////////////////////////////////////////////////////////////////////////////

void RenderScene(void);
void InitGlobals(void);
BOOL SetupDirectDraw(HWND);
void ShutdownDirectDraw(void);

void	dxStartText(char *textline);  //10.20.98 dlw
int		dxStCount;


typedef struct tagD3DAppMode
   {
    int     w;                // width
    int     h;                // height
    int     bpp;              // bits per pixel
    int     refresh;          // Refresh rate
    BOOL    ModeX;            // Is this a ModeX video mode?
    BOOL    bThisDriverCanDo; // can current D3D driver render in this mode?
   }D3DAppMode;

int                  NumModes; // number of available display modes
int                  CurrMode; // number of current display mode (only when fullscreen)
D3DAppMode           Mode[50]; // desc avail modes
D3DAppMode           ThisMode; // description of this mode, identical to Mode[CurrMode]

BOOL                 DDHard3D = 0;
int                  DDCount = 0;
LPDIRECTDRAW         lpDD;          // DirectDraw Interface
LPDIRECTDRAW2        lpDD2;         // DirectDraw Interface
LPDIRECTDRAWSURFACE  lpDDSPrimary;  // DirectDraw Surface
LPDIRECTDRAWSURFACE  lpDDSBack;     // DirectDraw Backbuffer Surface
//LPDIRECTDRAWSURFACE  lpDDSZBuf;     // DirectDraw Z-buffer Surface
//LPDIRECTDRAWSURFACE  lpDDSBitMap;   // DirectDraw BitMap Surface
//LPDIRECTDRAWSURFACE  lpDDSName;     // DirectDraw Menu Name Surface
//LPDIRECTDRAWSURFACE  lpDDSMenu;     // DirectDraw Menu Options Surface
//LPDIRECTDRAWSURFACE  lpDDSChars;    // DirectDraw Character Sets Surface
//LPDIRECTDRAWCLIPPER  lpDDClipper;   // DirectDrawClipper object - windowed mode
//int                  BPP;           // bit depth of the current display mode

LPDIRECTDRAWPALETTE  lpPalette = NULL; // Palette
PALETTEENTRY         rPal[768];        // Temp palette used later on

// this structure holds a RGB triple in three unsigned bytes
typedef struct RGB_color_typ
   {
    unsigned char red;      // red   component of color 0-63
    unsigned char green;    // green component of color 0-63
    unsigned char blue;     // blue  component of color 0-63
   }xRGB_color, *xRGB_color_ptr;

// this structure holds an entire color palette
typedef struct xRGB_palette_typ
   {
    int start_reg;          // index of the starting register that is save
    int end_reg;            // index of the ending registe that is saved
    xRGB_color colors[256];  // the storage area for the palette
   }xRGB_palette, *xRGB_palette_ptr;

xRGB_palette xpal;

void CreateDDBitmap(HBITMAP hBitMap, BITMAP *BitMap, LPDIRECTDRAWSURFACE *lpDDSurf);

BOOL FAR PASCAL DDEnumCallback(GUID FAR* lpGUID, LPSTR lpDriverDesc, LPSTR lpDriverName, LPVOID lpContext);
static HRESULT CALLBACK EnumDisplayModesCallback(LPDDSURFACEDESC pddsd, LPVOID lpContext);

BOOL CheckSurfaces(void);

/////////////////////////////////////////////////////////////////////////////////////
// Application Defines and Data
/////////////////////////////////////////////////////////////////////////////////////

#define MUSIC_NONE      0
#define MUSIC_CDAUDIO   1
#define MUSIC_MIDI      2

#define RENDER_GL       0
#define RENDER_D3D      1

#define KS_KEYUP        0
#define KS_KEYDOWN    128

/////////////////////////////////////////////////////////////////////////////////////
// Game states -- these are the modes in which the outer game loop can be
/////////////////////////////////////////////////////////////////////////////////////
#define GAME_START  0
#define GAME_SPLASH 1
#define GAME_MENU   2
#define GAME_PLAY   3
#define GAME_EXIT   4
#define GAME_QUIT   5
#define GAME_LIMBO  6
#define GAME_PAUSE  7

int     GameMode = GAME_START;

extern byte *screens[5];

extern int usemouse;
extern int usejoystick;

short     si_Kbd[256];

char      szMsgText[256];

DWORD     dwCurrWidth, dwCurrHeight, dwCurrBPP;

extern    CD_Data_t   CDData;
extern    MIDI_Data_t MidiData;

char        szMidiFile[] = "doomsong.mid";

int         MusicType = MUSIC_MIDI;
//int         RenderType = RENDER_D3D;  11.4.98 UNUSED
BOOL        bQuit = FALSE;

time_t clock_in, clock_out;	//work seconds for FPS 11.4.98 dlw
long framecount;			//count frames for FPS


void  GetWindowsVersion(void);
void  WriteDebug(char *);

int   WinDoomAC;
char *WinDoomAV[256];

char *YesNo[] = { "No", "Yes" };

void  ParseCommand(PSTR szCmdLine);

void  GameLoop(void);

void  D_DoomMain(void);
void *W_CacheLumpName(char *, int);
void  I_SetPalette(byte *);
void  MY_DoomSetup(void);
void  MY_DoomLoop(void);
void  WinDoomExit(void);
void  HandleKeyboard(void);



/////////////////////////////////////////////////////////////////////////////////////
// Windows Defines and Data
/////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
char      szAppName[] = "windoom";
char      szDbgName[] = "windoom.dbg";
//char      szCfgName[] = "WinDoom.cfg"; unused
HINSTANCE hInst;
HWND      hMainWnd;
HACCEL    ghAccel;


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    MSG         msg;
    WNDCLASSEX  wndclass;
    FILE       *fn;
    HWND        hwnd;
    HDC         hDCDesk; //, hdc; //foolhearted
    HWND        hDesktop;
    HACCEL      hAccel;
    int         i, k;
    int         p;
	// 9.4.1999 dlw: MIDI fix... enumerate midi devices
	// dumped to the file and then accessable to VB/FE
    UINT                    cDevs;
    MIDIOUTCAPS             moutCaps;
	FILE	*tempfn;

    hInst = hInstance;

    fn = fopen(szDbgName, "w");
    fclose(fn);

    // We get the current video setup here. Really don't need to do this. The data isn't used.
    dwCurrWidth = GetSystemMetrics(SM_CXSCREEN);
    dwCurrHeight = GetSystemMetrics(SM_CYSCREEN);
    hDesktop = GetDesktopWindow();
	hDCDesk  = GetDC(hDesktop);
	dwCurrBPP = GetDeviceCaps(hDCDesk,BITSPIXEL);
	ReleaseDC(hDesktop,hDCDesk);

    wndclass.cbSize        = sizeof(wndclass);
    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = WndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = hInstance;
    wndclass.hIcon         = LoadIcon(NULL, IDI_WINLOGO);
    wndclass.hCursor       = LoadCursor(NULL, IDC_WAIT);
    wndclass.hbrBackground =(HBRUSH) GetStockObject(BLACK_BRUSH); //blackbrush
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = szAppName;
    wndclass.hIconSm       = LoadIcon(NULL, IDI_WINLOGO);

    RegisterClassEx(&wndclass);


    CDData.CDStatus = cd_empty;
    CDData.CDMedia = FALSE;
    CDData.CDPosition = 0;
    CDData.CDCode[0] = '\0';

    hwnd = CreateWindow(szAppName,            // window class name
		"windoom",              // window caption
		WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,  // window style
		0,0,                  // initial x & y position
		dwCurrWidth, dwCurrHeight,
		NULL,                  // parent window handle
		NULL,                  // window menu handle
		hInstance,             // program instance handle
		NULL);		          // creation parameters
/*
SystemParametersInfo()
*/

    hMainWnd = hwnd;
    SetupDirectSound();
    // Make the window visible & update its client area
    ShowWindow(hwnd, iCmdShow);// Show the window
    UpdateWindow(hwnd);        // Sends WM_PAINT message
    //SetFocus(hwnd);				//10.19.98 dlw

    // This is used to determine what OS we're on (Windows9X or Windows NT)
    GetWindowsVersion();


	// 9.4.1999 dlw: MIDI fix... enumerate midi devices
	// dumped to the file and then accessable to VB/FE
    cDevs = midiOutGetNumDevs();
	tempfn = fopen("midilist.txt", "wt");
    for (p = 0; p < (int)cDevs; p++)
    {
        if (midiOutGetDevCaps(p, &moutCaps, sizeof(moutCaps)) == MMSYSERR_NOERROR)
        {
			//write strings to file
			// store in NumberVal,name
			itoa(p, szMsgText, 10);
			sprintf(szMsgText, "%s,%s\r\n", szMsgText, moutCaps.szPname);
			fprintf(tempfn, "%s", szMsgText);
        }
    }
	fclose(tempfn);


    ParseCommand(szCmdLine);

    p = M_CheckParm("-width");
    if (p && p < myargc-1)
        SCREENWIDTH = atoi(myargv[p+1]);
    else
        SCREENWIDTH = 320;

    p = M_CheckParm("-height");
    if (p && p < myargc-1)
        SCREENHEIGHT = atoi(myargv[p+1]);
    else
        SCREENHEIGHT = 200;

	//11.4.98 dlw optimize
	DOUBLESTUFF=0;
	if(SCREENWIDTH>=640 && SCREENHEIGHT>=400)
		DOUBLESTUFF=1;

	//11.5.98 dlw optimize
	SCREENMULT=SCREENWIDTH*SCREENHEIGHT;

    sprintf(szMsgText, "Resolution: %d x %d\n",SCREENWIDTH,SCREENHEIGHT);
    WriteDebug(szMsgText);

	//11.16.98 dlw testing
	//i=midiOutGetNumDevs();
	//sprintf(szMsgText, "Number of Midi Devices=%d\n", i);
	//WriteDebug(szMsgText);


    // This sets the video mode to video mode described in the default video mode
    InitGlobals();
    if (!SetupDirectDraw(hwnd))
	{
        ShutdownDirectSound();
        exit(0);
	}

    //MoveWindow(hwnd, 0,0, Mode[CurrMode].w, Mode[CurrMode].h, TRUE);

    //hAccel = LoadAccelerators(hInstance,"AppAccel"); 11.8.98 never was here
	hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));
    ghAccel = hAccel;
	if(hAccel==NULL) //11.8.98 dlw debugging
	{
		WriteDebug("Accelerators Blew It\n");
	}

    bDILive = InitDirectInput();

    for(k = 0; k < 256; k++) si_Kbd[k] = KS_KEYUP;

    //PlayMidiFile(szMidiFile);

    bQuit = FALSE;

	//11.4.98 dlw start the FPS clock here
	time(&clock_in);

    sprintf(szMsgText, "windoom Version %d.%03d", CONSVERS/1000, CONSVERS%1000);
	WriteDebug(szMsgText);
	WriteDebug("\n");		// leaves sz working for dxstarttext
	dxStCount=1;
	dxStartText(szMsgText);
	
	//testing foolheartedly 10.19.98 to blast some text on screen
	//for(k=0; k<400; k++)  //test loop see if we can see this
	//{
	//	dxStartText(szMsgText);
	//}

    D_DoomMain();
    MY_DoomSetup();
	if(gamemode != indetermined) //11.9.98 bug catcher
		GameMode = GAME_PLAY;

    ShowCursor(FALSE);

    while(!bQuit)
	{
        //while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
            if(msg.message == WM_QUIT)
			{
				bQuit = TRUE;
                break;
			}
			//else if(msg.message == WM_CLOSE)
			//{
			//	bQuit = TRUE;
			//	break;
			//}

            //11.8.98 Finally debugging this cuz theres no
			// accelerator to begin with
			//if(!TranslateAccelerator(msg.hwnd, hAccel, &msg))
			//{
				//sprintf(szMsgText, "Translate failed: Code: %d\n", GetLastError());
				//WriteDebug(szMsgText);
                TranslateMessage(&msg);
                DispatchMessage(&msg);
			//}
		}
        if(GameMode == GAME_PLAY)
			GameLoop();
        else if(GameMode == GAME_QUIT)
			I_Quit();
	}
    ShowCursor(TRUE);
    for(i = 4; i >= 0; i--)
        if(screens[i]!=NULL) free(screens[i]);
    for(i = (WinDoomAC-1); i > 0; i--)
        if(WinDoomAV[i]!=NULL) free(WinDoomAV[i]);
    return msg.wParam;
}

void ParseCommand(PSTR szCmdLine)
   {
    char *s;

    WinDoomAC = 1;
    s = strtok(szCmdLine, " ");
    while (s != NULL)
       {
        WinDoomAV[WinDoomAC] = (char *)malloc(strlen(s)+1);
        strcpy(WinDoomAV[WinDoomAC], s);
        WinDoomAC++;
        s = strtok(NULL, " ");
       }
    
    myargc = WinDoomAC;
    myargv = WinDoomAV;
   }

void GameLoop(void)
{
    DIMOUSESTATE         diMouseState;          /* DirectInput mouse state structure */
    static DIMOUSESTATE  diHoldMouse;           /* DirectInput mouse state structure */
    DIJOYSTATE           diJoyState;            /* DirectInput joystick state structure */
    static DIJOYSTATE    diHoldJoy;             /* DirectInput joystick state structure */
    HRESULT          hresult;
    static  event_t  event;
    static  event_t  mbevent;
    static  event_t  jbevent;

    //if(usemouse == TRUE) 11.7.98 dlw
	if(usemouse)
	{
    RetryMouse:;
		hresult = lpMouse->lpVtbl->GetDeviceState(lpMouse, sizeof(DIMOUSESTATE), &diMouseState);
        if(hresult == DIERR_INPUTLOST)
		{
            hresult = lpMouse->lpVtbl->Acquire(lpMouse);
            if (SUCCEEDED(hresult))
                goto RetryMouse;
		}
        else if(hresult != DI_OK)
		{
			DI_Error(hresult, "GetDeviceState (mouse)");
		}

        if (SUCCEEDED(hresult))
		{
            if ((diMouseState.lX != diHoldMouse.lX) ||
                (diMouseState.lY != diHoldMouse.lY) ||
                (diMouseState.lZ != diHoldMouse.lZ) ||
                (diMouseState.rgbButtons[0] != diHoldMouse.rgbButtons[0]) ||
                (diMouseState.rgbButtons[1] != diHoldMouse.rgbButtons[1]) ||
                (diMouseState.rgbButtons[2] != diHoldMouse.rgbButtons[2]) ||
                (diMouseState.rgbButtons[3] != diHoldMouse.rgbButtons[3]))
			{
                event.type = ev_mouse;
                event.data1 = 0;
                if (mouseb1 == 0)
                    event.data1 |= ((diMouseState.rgbButtons[0] & 0x80) >> 7);
                else
				{
                    if ((diHoldMouse.rgbButtons[0] & 0x80) != (diMouseState.rgbButtons[0] & 0x80))
					{
                        if ((diMouseState.rgbButtons[0] & 0x80) == 0x80)
						{
                            mbevent.type = ev_keydown;
						}
                        else
						{
                            mbevent.type = ev_keyup;
						}
                        mbevent.data1 = mouseb1;
                        D_PostEvent(&mbevent);
					}
				}
                if (mouseb2 == 0)
                    event.data1 |= ((diMouseState.rgbButtons[1] & 0x80) >> 6);
                else
				{
                    if ((diHoldMouse.rgbButtons[1] & 0x80) != (diMouseState.rgbButtons[1] & 0x80))
					{
                        if ((diMouseState.rgbButtons[1] & 0x80) == 0x80)
						{
                            mbevent.type = ev_keydown;
						}
                        else
						{
                            mbevent.type = ev_keyup;
						}
                        mbevent.data1 = mouseb2;
                        D_PostEvent(&mbevent);
					}
				}
                if (mouseb3 == 0)
                    event.data1 |= ((diMouseState.rgbButtons[2] & 0x80) >> 5);
                else
				{
                    if ((diHoldMouse.rgbButtons[2] & 0x80) != (diMouseState.rgbButtons[2] & 0x80))
					{
                        if ((diMouseState.rgbButtons[2] & 0x80) == 0x80)
						{
                            mbevent.type = ev_keydown;
						}
                        else
						{
                            mbevent.type = ev_keyup;
						}
                        mbevent.data1 = mouseb3;
                        D_PostEvent(&mbevent);
					}
				}
                event.data2 = diMouseState.lX*4;
                //if(mvert == TRUE)
				if(mvert)
				{
                    event.data3 = (diMouseState.lY * -4);
				}
                else
				{
                    event.data3 = 0;
				}
                D_PostEvent(&event);
                diHoldMouse.lX = diMouseState.lX;
                diHoldMouse.lY = diMouseState.lY;
                diHoldMouse.lZ = diMouseState.lZ;
                diHoldMouse.rgbButtons[0] = diMouseState.rgbButtons[0];
                diHoldMouse.rgbButtons[1] = diMouseState.rgbButtons[1];
                diHoldMouse.rgbButtons[2] = diMouseState.rgbButtons[2];
                diHoldMouse.rgbButtons[3] = diMouseState.rgbButtons[3];
			}
		}
	}

    if(usejoystick)
	{
    RetryJoy:;
		if (lpJoystick != 0)
		{
			hresult = lpJoystick->lpVtbl->Poll(lpJoystick);
			hresult = lpJoystick->lpVtbl->GetDeviceState(lpJoystick, sizeof(DIJOYSTATE), &diJoyState);
			if (hresult == DIERR_INPUTLOST)
			{
				hresult = lpJoystick->lpVtbl->Acquire(lpJoystick);
				if (SUCCEEDED(hresult))
					goto RetryJoy;
			}
			else
				if (hresult != DI_OK)
				{
					DI_Error(hresult, "GetDeviceState (joystick)");
				}
			if (SUCCEEDED(hresult))
			{
				if ((diJoyState.lX != diHoldJoy.lX) ||
					(diJoyState.lY != diHoldJoy.lY) ||
					(diJoyState.rgbButtons[0] != diHoldJoy.rgbButtons[0]) ||
					(diJoyState.rgbButtons[1] != diHoldJoy.rgbButtons[1]) ||
					(diJoyState.rgbButtons[2] != diHoldJoy.rgbButtons[2]) ||
					(diJoyState.rgbButtons[3] != diHoldJoy.rgbButtons[3]))
				{
					event.type = ev_joystick;
					event.data1 = 0;
					if (joyb1 == 0)
						event.data1 |= ((diJoyState.rgbButtons[0] & 0x80) >> 7);
					else
					{
						if ((diHoldJoy.rgbButtons[0] & 0x80) != (diJoyState.rgbButtons[0] & 0x80))
						{
							if ((diJoyState.rgbButtons[0] & 0x80) == 0x80)
							{
								jbevent.type = ev_keydown;
							}
							else
							{
								jbevent.type = ev_keyup;
							}
							jbevent.data1 = joyb1;
							D_PostEvent(&jbevent);
						}
					}
					if (joyb2 == 0)
						event.data1 |= ((diJoyState.rgbButtons[1] & 0x80) >> 6);
					else
					{
						if ((diHoldJoy.rgbButtons[1] & 0x80) != (diJoyState.rgbButtons[1] & 0x80))
						{
							if ((diJoyState.rgbButtons[1] & 0x80) == 0x80)
							{
								jbevent.type = ev_keydown;
							}
							else
							{
								jbevent.type = ev_keyup;
							}
							jbevent.data1 = joyb2;
							D_PostEvent(&jbevent);
						}
					}
					if (joyb3 == 0)
						event.data1 |= ((diJoyState.rgbButtons[2] & 0x80) >> 5);
					else
					{
						if ((diHoldJoy.rgbButtons[2] & 0x80) != (diJoyState.rgbButtons[2] & 0x80))
						{
							if ((diJoyState.rgbButtons[2] & 0x80) == 0x80)
							{
								jbevent.type = ev_keydown;
							}
							else
							{
								jbevent.type = ev_keyup;
							}
							jbevent.data1 = joyb3;
							D_PostEvent(&jbevent);
						}
					}
					if (joyb4 == 0)
						event.data1 |= ((diJoyState.rgbButtons[3] & 0x80) >> 4);
					else
					{
						if ((diHoldJoy.rgbButtons[3] & 0x80) != (diJoyState.rgbButtons[3] & 0x80))
						{
							if ((diJoyState.rgbButtons[2] & 0x80) == 0x80)
							{
								jbevent.type = ev_keydown;
							}
							else
							{
								jbevent.type = ev_keyup;
							}
							jbevent.data1 = joyb4;
							D_PostEvent(&jbevent);
						}
					}
					event.data2 = diJoyState.lX;
					event.data3 = diJoyState.lY;
					D_PostEvent(&event);
					diHoldJoy.lX = diJoyState.lX;
					diHoldJoy.lY = diJoyState.lY;
					diHoldJoy.rgbButtons[0] = diJoyState.rgbButtons[0];
					diHoldJoy.rgbButtons[1] = diJoyState.rgbButtons[1];
					diHoldJoy.rgbButtons[2] = diJoyState.rgbButtons[2];
					diHoldJoy.rgbButtons[3] = diJoyState.rgbButtons[3];
				}
			}
       }
    }

    HandleKeyboard();
    MY_DoomLoop();
}

void HandleKeyboard()
{
    HRESULT          hresult;

RetryKeyboard:
	hresult = lpKeyboard->lpVtbl->GetDeviceState(lpKeyboard, sizeof(diKeyState), &diKeyState);
	if(hresult == DIERR_INPUTLOST)
	{
		hresult = lpKeyboard->lpVtbl->Acquire(lpKeyboard);
		if(SUCCEEDED(hresult)) goto RetryKeyboard;
	}
	else if(hresult != DI_OK)
	{
		DI_Error(hresult, "GetDeviceState (keyboard)");
	}
	else
	{
		HandleKeyStrokes();
	}
}

void HandleKeyStrokes(void)
{
    static  event_t  event;
    int     i;
	//char	ogod[20]; //debugging and optimizing 11.7.98

    //11.7.98 dlw optimize - every one of these had
	// if(keylink==TRUE) && (diKey..... but keylink is made
	// true in the ini and never changed so... speed babee
	// I did some more reading/thinking and found a faster way
	// than all these ifs.  I'm just gonna leave this mess and
	// recode below
	//if(diKeyState[DIK_LMENU]==KS_KEYDOWN)
	//	diKeyState[DIK_RMENU] = KS_KEYDOWN;
    /* if( (diKeyState[DIK_RMENU] == KS_KEYDOWN) && (diKeyState[DIK_LMENU] == KS_KEYUP))
        diKeyState[DIK_LMENU] = KS_KEYDOWN;
		11.7.98 dlw-no need for the second check we're forcing states here */
    //if(diKeyState[DIK_RMENU] == KS_KEYDOWN)
    //    diKeyState[DIK_LMENU] = KS_KEYDOWN;
    //if( diKeyState[DIK_LSHIFT] == KS_KEYDOWN )
    //    diKeyState[DIK_RSHIFT] = KS_KEYDOWN;
    //if( diKeyState[DIK_RSHIFT] == KS_KEYDOWN)
    //    diKeyState[DIK_LSHIFT] = KS_KEYDOWN;
    //if( (diKeyState[DIK_LCONTROL] == KS_KEYDOWN))
    //    diKeyState[DIK_RCONTROL] = KS_KEYDOWN;
    //if( (diKeyState[DIK_RCONTROL] == KS_KEYDOWN) && (diKeyState[DIK_LCONTROL] == KS_KEYUP))
    //    diKeyState[DIK_LCONTROL] = KS_KEYDOWN;

	diKeyState[DIK_RMENU] |= diKeyState[DIK_LMENU]; //rt-alt is strafe

	//diKeyState[DIK_LSHIFT] |= diKeyState[DIK_RSHIFT];
	//diKeyState[DIK_LCONTROL] |= diKeyState[DIK_RCONTROL];

	// Can this be limited to a smaller range? What about international keyboards?
	// 11.7.98 dlw did some testing and found that only up to 210
	// are used...
    for (i = 1; i < 256; i++)
	{
        //11.7.98 optimized is getting 3-4 frames better
		//if(((diKeyState[i] & 0x80) == 0) && (si_Kbd[i] == KS_KEYDOWN))
		if( !diKeyState[i] ) 
		{
			if(si_Kbd[i])			
			{
				event.type = ev_keyup;
				event.data1 = i;
				D_PostEvent(&event);
				si_Kbd[i] = KS_KEYUP;
			}
		}
		//if ((diKeyState[i] & 0x80) && (si_Kbd[i] == KS_KEYUP))
		else // if( diKeyState[i] )
		{
            //if ((i != DIK_TAB) || ((diKeyState[DIK_LMENU] == KS_KEYUP) && (diKeyState[DIK_RMENU] == KS_KEYUP)))
			if( !si_Kbd[i] )
			{
                event.type = ev_keydown;
                event.data1 = i;
                D_PostEvent(&event);
                si_Kbd[i] = KS_KEYDOWN;
				// 11.7.98 debug/optimize crack that could probably kill a machine
				//sprintf(ogod, "Key went down:%d\n", i);
				//WriteDebug(ogod);
			}
		}
	}
}

void WinDoomExit(void)
{
    if (demorecording)
	{
        if (demotype == DEMO_I) G_EndDemo();
        else G_EndDemo_II();
	}
    GameMode = GAME_LIMBO;
    StopMusic();
    ShutdownDirectInput();
    ShutdownDirectSound();
	//11.4.98 dlw best place to calc FPS is at close of gfx
    ShutdownDirectDraw();
	//WriteDebug("Sending WM_CLOSE\n");
    SendMessage(hMainWnd, WM_CLOSE, 0, 0);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    static HDC         hDC;
    static PAINTSTRUCT ps;
    static event_t     event;
    static unsigned char KeyPress;
    static int         scancode;

    switch(iMsg)
	{
	case WM_CREATE:
		GetCDInfo(hwnd);
		break;
	
	case MM_MCINOTIFY:
		if (wParam == MCI_NOTIFY_SUCCESSFUL)
		{
			if (MidiData.MidiStatus == midi_play)
				MidiReplay(hwnd, &MidiData);
			if (CDData.CDStatus == cd_play)
				CDTrackPlay(hwnd, &CDData);
		}
		if (wParam == MCI_NOTIFY_FAILURE)
		{
			if (CDData.CDStatus == cd_play)
			{
				MidiPlay(hwnd, &MidiData);
				CDData.CDStatus = cd_stop;
			}
		}
		break;
	
	case WM_KEYDOWN:
		if ((lParam & 0x40000000) != 0)  // This "debounces" the keys so that we only process
			break;                        // the message when the key is first pressed and not after.
		switch(wParam)
		{
		case VK_PAUSE:
			event.type = ev_keydown;
			event.data1 = KEY_PAUSE;
			D_PostEvent(&event);
			break;
		case VK_SHIFT:
			//if (keylink == TRUE)  11.7.98 dlw always true anyway
			//{
				event.type = ev_keydown;
				event.data1 = DIK_RSHIFT;
				D_PostEvent(&event);
			//}
			break;
		case VK_CONTROL:
			//if (keylink == TRUE)  11.7.98 dlw always true anyway
			//{
				event.type = ev_keydown;
				event.data1 = DIK_RCONTROL;
				D_PostEvent(&event);
			//}
			break;
		}
		break;
		
	case WM_ACTIVATE:
		if (LOWORD(wParam) != WA_INACTIVE)
		{
			GameMode = GAME_PLAY;
		}
		else
		{
			GameMode = GAME_PAUSE;
		}
		break;

      case WM_KEYUP:
		  switch(wParam)
		  {
		  case VK_PAUSE:
			  event.type = ev_keyup;
			  event.data1 = KEY_PAUSE;
			  D_PostEvent(&event);
			  break;
		  case VK_SHIFT:
			  //if (keylink == TRUE)  11.7.98 dlw always true anyway
			  //{
				  event.type = ev_keyup;
				  event.data1 = DIK_RSHIFT;
				  D_PostEvent(&event);
			  //}
			  break;
		  case VK_CONTROL:
			  //if (keylink == TRUE)  11.7.98 dlw always true anyway
			  //{
				  event.type = ev_keyup;
				  event.data1 = DIK_RCONTROL;
				  D_PostEvent(&event);
			  //}
			  break;
		  }
		  break;

       case WM_DESTROY:
		   PostQuitMessage(0);
		   return 0;
	}
    
	return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

void WriteDebug(char *Message)
{
    FILE *fn;

    fn = fopen(szDbgName, "a+");
    fprintf(fn, "%s", Message);
    fclose(fn);
}

void GetWindowsVersion()
{
    OSVERSIONINFO OSVersionInfo;
    FILE *fn;

    fn = fopen(szDbgName, "a+");

    OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVersionInfo);
    GetVersionEx(&OSVersionInfo);

    switch(OSVersionInfo.dwPlatformId)
	{
	case VER_PLATFORM_WIN32s:
		fprintf(fn, "Platform is Windows %d.%02d\n",
			OSVersionInfo.dwMajorVersion,
			OSVersionInfo.dwMinorVersion);
		break;
	case VER_PLATFORM_WIN32_WINDOWS:
		fprintf(fn, "Platform is Windows 9X ");
		fprintf(fn, "Version %d.%02d.%d\n", OSVersionInfo.dwMajorVersion,
			OSVersionInfo.dwMinorVersion,
			OSVersionInfo.dwBuildNumber & 0xFFFF);
		break;
	case VER_PLATFORM_WIN32_NT:
		fprintf(fn, "Platform is Windows NT ");
		fprintf(fn, "Version %d.%02d ", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion);
		fprintf(fn, "Build %d\n", OSVersionInfo.dwBuildNumber & 0xFFFF);
		break;
	}
    if(strlen(OSVersionInfo.szCSDVersion) > 0)
		fprintf(fn, "Windows Info: %s\n", OSVersionInfo.szCSDVersion);
    fclose(fn);
}


/////////////////////////////////////////////////////////////////////////////////////
// DIRECTX
/////////////////////////////////////////////////////////////////////////////////////
// The next section of code deals with Microsoft's DirectX.
/////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////
// DIRECTSOUND - Sound effects
/////////////////////////////////////////////////////////////////////////////////////

BOOL SetupDirectSound()
   {
    HRESULT        hresult;
    int            buff;
    DSBUFFERDESC   dsbdesc;
    WAVEFORMATEX   wfx;

    // Create an instance of DirectSound
    hresult = DirectSoundCreate(NULL, &lpDS, NULL);
    if (hresult != DS_OK)
       {
        DS_Error(hresult, "DirectSoundCreate");
        for (buff = 0; buff < NUM_DSBUFFERS; buff++)
           lpDSBuffer[buff] = 0;
        return FALSE;
       }

    // Set the cooperative level so it doesn't get confused
    hresult = lpDS->lpVtbl->SetCooperativeLevel(lpDS, hMainWnd, DSSCL_EXCLUSIVE);
    if (hresult != DS_OK)
       DS_Error(hresult, "DirectSound.SetCooperativeLevel");

    // Set up DSBUFFERDESC structure.
    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));  // Zero it out.
    dsbdesc.dwSize              = sizeof(DSBUFFERDESC);
    dsbdesc.dwFlags             = DSBCAPS_PRIMARYBUFFER;
    dsbdesc.dwBufferBytes       = 0;
    dsbdesc.lpwfxFormat         = NULL;

    hresult = lpDS->lpVtbl->CreateSoundBuffer(lpDS, &dsbdesc, &lpDSPrimary, NULL);
    if (hresult != DS_OK)
       {
        DS_Error(hresult, "DirectSound.CreateSoundBuffer - Primary");
       }

    // Set up wave format structure.
    memset( &wfx, 0, sizeof(WAVEFORMATEX) );
    wfx.wFormatTag         = WAVE_FORMAT_PCM;      
    wfx.nChannels          = 2;
    wfx.nSamplesPerSec     = 11025;
    wfx.nAvgBytesPerSec    = 11025*2*1;
    wfx.nBlockAlign        = 2; // ?
    wfx.wBitsPerSample     = (WORD)8;
    wfx.cbSize             = 0;

    hresult = lpDSPrimary->lpVtbl->SetFormat(lpDSPrimary, &wfx);
    if (hresult != DS_OK)
       {
        DS_Error(hresult, "DirectSound.SetFormat - Primary");
       }

    // Set the cooperative level so it doesn't get confused
    hresult = lpDS->lpVtbl->SetCooperativeLevel(lpDS, hMainWnd, DSSCL_NORMAL);
    if (hresult != DS_OK)
       DS_Error(hresult, "DirectSound.SetCooperativeLevel");

    return(TRUE);
   }

void CreateSoundBuffer(int Channel, int length, unsigned char *data)
   {
    HRESULT        hresult;
    //int            buff;
    DSBUFFERDESC   dsbdesc;
    PCMWAVEFORMAT  pcmwf;
    void          *buffer, *buff2;
    DWORD          size1, size2;

    if (Channel > NUM_DSBUFFERS)
       {
        WriteDebug("Invalid sound effect...\n");
        return;
       }

    // Set up wave format structure.
    memset( &pcmwf, 0, sizeof(PCMWAVEFORMAT) );
    pcmwf.wf.wFormatTag         = WAVE_FORMAT_PCM;      
    pcmwf.wf.nChannels          = 1;
    pcmwf.wf.nSamplesPerSec     = 11025;
    pcmwf.wf.nBlockAlign        = 1; // ?
    pcmwf.wf.nAvgBytesPerSec    = 11025*1*1;
    pcmwf.wBitsPerSample        = (WORD)8;

    // Set up DSBUFFERDESC structure.
    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));  // Zero it out.
    dsbdesc.dwSize              = sizeof(DSBUFFERDESC);
    dsbdesc.dwFlags             = DSBCAPS_CTRLDEFAULT|DSBCAPS_CTRLPOSITIONNOTIFY;
    dsbdesc.dwBufferBytes       = length;
    dsbdesc.lpwfxFormat         = (LPWAVEFORMATEX)&pcmwf;

    if ((hresult = lpDS->lpVtbl->CreateSoundBuffer(lpDS, &dsbdesc, &lpDSBuffer[Channel], NULL)) != DS_OK)
       {
        DS_Error(hresult, "DirectSound.CreateSoundBuffer");
        return;
       }

    hresult = lpDSBuffer[Channel]->lpVtbl->Lock(lpDSBuffer[Channel],0,length,&buffer,&size1,&buff2,&size2,DSBLOCK_ENTIREBUFFER );
    if (hresult == DS_OK)
       {
        memcpy(buffer, data, length);
        hresult = lpDSBuffer[Channel]->lpVtbl->Unlock(lpDSBuffer[Channel],buffer,length,buff2,size2);
        if (hresult != DS_OK)
           DS_Error(hresult, "lpDSBuffer.Unlock");
       }
    else
       {
        DS_Error(hresult, "lpDSBuffer.Lock");
       }
   }

void ShutdownDirectSound(void)
{
    int buff;
    DWORD BufferStatus;

    for(buff = 0; buff < NUM_DSBUFFERS; buff++)
	{
        if(lpDSBuffer[buff] != 0)
		{
            BufferStatus = DSBSTATUS_PLAYING;
            while (BufferStatus == DSBSTATUS_PLAYING)
                lpDSBuffer[buff]->lpVtbl->GetStatus(lpDSBuffer[buff], &BufferStatus);
            RELEASE(lpDSBuffer[buff]);
		}
	}
    if(lpDSPrimary != NULL) RELEASE(lpDSPrimary);
    if(lpDS != NULL) RELEASE(lpDS);
}

/////////////////////////////////////////////////////////////////////////////////////
// DirectDraw
/////////////////////////////////////////////////////////////////////////////////////

//11.4.98 dlw UNUSED
//DWORD FrameCount, FrameCount0, FrameTime, FrameTime0, FrameRate;

void InitGlobals(void)
{
    lpDD = NULL;
    lpDD2 = NULL;
    lpDDSPrimary = NULL;
    lpDDSBack = NULL;
    //UNUSED: lpDDClipper = NULL;
}

BOOL SetupDirectDraw( HWND hwnd )
{
    static RECT    rect;
    static HDC     hdc;
    static HRESULT hresult;
    static int     width, height;
    static DDSURFACEDESC ddsd;
    static DDSCAPS ddscaps;
    static DDCAPS  DriverCaps, HELCaps;
    static char    msg[512];
    int            i, x;
    FILE *fn;

    // Record the current display BPP
//    hdc = GetDC(hwnd);
//    BPP = GetDeviceCaps(hdc, BITSPIXEL);
//    ReleaseDC(hwnd, hdc);

    // create the main DirectDraw object
    hresult = DirectDrawEnumerate(DDEnumCallback, &lpDD);
    if (hresult != DD_OK)
        DD_Error(hresult, "DirectDrawEnumerate");

    if (lpDD == 0)
       {
        hresult = DirectDrawCreate( NULL, &lpDD, NULL );
        if (hresult != DD_OK)
            DD_Error(hresult, "DirectDrawCreate");
       }

    // Get the DirectDraw capabilities
    memset(&DriverCaps, 0, sizeof(DDCAPS));
    DriverCaps.dwSize = sizeof(DDCAPS);
        
    memset(&HELCaps, 0, sizeof(DDCAPS));
    HELCaps.dwSize = sizeof(DDCAPS);
        
    hresult = lpDD->lpVtbl->GetCaps(lpDD, &DriverCaps, &HELCaps);
    if (hresult != DD_OK)
       DD_Error(hresult, "GetCaps");

    // Does this driver have 3D hardware capabilites?
    if (DriverCaps.dwCaps & DDCAPS_3D)
        DDHard3D = 1;

    hresult = lpDD->lpVtbl->SetCooperativeLevel(lpDD, hwnd, DDSCL_NORMAL);
    if (hresult != DD_OK)
        DD_Error(hresult, "SetCooperativeLevel");
    hresult = lpDD->lpVtbl->QueryInterface(lpDD, &IID_IDirectDraw2, (LPVOID *)&lpDD2);
    if (hresult != DD_OK)
        DD_Error(hresult, "QueryInterface");

    // Get exclusive mode
    hresult = lpDD2->lpVtbl->SetCooperativeLevel(lpDD2, hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX);
    if (hresult != DD_OK)
        DD_Error(hresult, "SetCooperativeLevel");

    // See what display modes are available...
    lpDD2->lpVtbl->EnumDisplayModes(lpDD2, DDEDM_REFRESHRATES | DDEDM_STANDARDVGAMODES, NULL, 0, EnumDisplayModesCallback );

    CurrMode = -1;  // should be -1 for proper checking...
    fn = fopen(szDbgName, "a+");
    fprintf(fn, "Mode count : %d\n", NumModes);
    fprintf(fn, "DirectDraw Video Mode List\n");
    fprintf(fn, "Mode Width x Height x Color Bits ModeX\n");
    for (x = 0; x < NumModes; x++)
       {
        if (Mode[x].bpp == 8)
           fprintf(fn, "%2d    %4d    %4d        %2d      %s\n", x,Mode[x].w,Mode[x].h,Mode[x].bpp,YesNo[Mode[x].ModeX]);
        if ((Mode[x].w == SCREENWIDTH) && (Mode[x].h == SCREENHEIGHT) && (Mode[x].bpp == 8))
           CurrMode = x;
       }
    fclose(fn);

    // should check video mode here and abort if not available...
    if (CurrMode == -1)
       {
        hresult = lpDD->lpVtbl->SetCooperativeLevel(lpDD, hwnd, DDSCL_NORMAL);
        RELEASE(lpDD);
        return(FALSE);
       }
    // Set the video mode
    if (Mode[CurrMode].ModeX == FALSE)
        hresult = lpDD2->lpVtbl->SetDisplayMode(lpDD2, Mode[CurrMode].w, Mode[CurrMode].h, Mode[CurrMode].bpp, 0, DDSDM_STANDARDVGAMODE );
    else
        hresult = lpDD2->lpVtbl->SetDisplayMode(lpDD2, Mode[CurrMode].w, Mode[CurrMode].h, Mode[CurrMode].bpp, 0, 0 );
    if (hresult != DD_OK)
        DD_Error(hresult, "SetDisplayMode");

    memset(&ddsd, sizeof(DDSURFACEDESC), 0);
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
    //ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY;
    ddsd.dwBackBufferCount = 1;
    hresult = lpDD2->lpVtbl->CreateSurface(lpDD2, &ddsd, &lpDDSPrimary, NULL);
    if (hresult != DD_OK)
        DD_Error(hresult, "CreateSurface");
    ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
    hresult = lpDDSPrimary->lpVtbl->GetAttachedSurface(lpDDSPrimary, &ddscaps, &lpDDSBack);
    if (hresult != DD_OK)
        DD_Error(hresult, "GetAttachedSurface");

    // Retrieve the caps of the primary surface
    lpDDSPrimary->lpVtbl->GetCaps(lpDDSPrimary, &ddscaps);
    WriteDebug("DirectDraw Back Buffer Surface Description:\n");
    ddsd.dwSize = sizeof(ddsd);
    lpDDSBack->lpVtbl->GetSurfaceDesc(lpDDSBack, &ddsd);
    sprintf(szMsgText, "Screen Width %d, Height %d, Surface Pitch %d\n",ddsd.dwWidth, ddsd.dwHeight, ddsd.lPitch );
    WriteDebug(szMsgText);

    // Create and attach palette (only if we in 8-bit palettized color modes)
    if ((Mode[CurrMode].bpp == 8) && (ddscaps.dwCaps & DDCAPS_PALETTE))
       {
        // Set up palette 
        // These 3 are read only, I can't change them.
        //rPal[0].peFlags   = D3DPAL_READONLY;
        //rPal[255].peFlags = D3DPAL_READONLY;
 
        // The rest are free for my use, it can set then to whatever I want.
		// 10.20.98 This one really got me... freeking 0s
        for (i = 0; i < 256; i++)
		{
            rPal[i].peRed   =i;		//xpal.colors[i].red*4;
            rPal[i].peBlue  =i;		//xpal.colors[i].blue*4;
            rPal[i].peGreen =i;		//xpal.colors[i].green*4;
            rPal[i].peFlags = D3DPAL_FREE;
			//sprintf(szMsgText, "%d R, %d B, %d G. at slot %d.\n", rPal[i].peRed,rPal[i].peBlue,rPal[i].peGreen,i);
			//WriteDebug(szMsgText);
		}
   
        // Create the palette
        hresult = lpDD2->lpVtbl->CreatePalette(lpDD2, DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE, rPal, &lpPalette, NULL);
        if (hresult != DD_OK)
            DD_Error(hresult, "CreatePalette");
        // Set the back buffer's palette
        hresult = lpDDSBack->lpVtbl->SetPalette(lpDDSBack, lpPalette);
        if (hresult != DD_OK)
            DD_Error(hresult, "SetPalette - Back Buffer");
        // Set the primary surface's palette
        hresult = lpDDSPrimary->lpVtbl->SetPalette(lpDDSPrimary, lpPalette);
        if (hresult != DD_OK)
            DD_Error(hresult, "SetPalette - Primary Surface");
       }

    return TRUE;
   }


// Checks all the surfaces created, if they are lost it restores them.  Returns FALSE
// if a surface was lost, so the viewport can be recreated if necessary
BOOL CheckSurfaces()
{
	// Check the primary surface
    if (lpDDSPrimary)
	{
        if (lpDDSPrimary->lpVtbl->IsLost(lpDDSPrimary))
		{
            lpDDSPrimary->lpVtbl->Restore(lpDDSPrimary);
            return FALSE;
		}
	}

    return TRUE;
}

void CreateDDBitmap(HBITMAP hBitMap, BITMAP *BitMap, LPDIRECTDRAWSURFACE *lpDDSurf)
   {
    HDC                 hdcImage, hdc;
    DDSURFACEDESC       ddsd;
    HRESULT             hresult;
    LPDIRECTDRAWSURFACE lpDDTemp;

    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
    ddsd.dwWidth = BitMap->bmWidth;
    ddsd.dwHeight = BitMap->bmHeight;

    hresult = lpDD2->lpVtbl->CreateSurface(lpDD2, &ddsd, &lpDDTemp, NULL);
    if (hresult != DD_OK)
       {
        DD_Error( hresult, "CreateDDBitmap CreateSurface");
        return;
       }

    hdcImage = CreateCompatibleDC(NULL);
    if (!hdcImage)
	   WriteDebug("createcompatible dc failed\n");

    SelectObject(hdcImage, hBitMap);

    //
    // get size of surface.
    //
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
    lpDDTemp->lpVtbl->GetSurfaceDesc(lpDDTemp, &ddsd);

    hresult = lpDDTemp->lpVtbl->GetDC(lpDDTemp, &hdc);
    if (hresult == DD_OK)
       {
        StretchBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, 0, 0, BitMap->bmWidth, BitMap->bmHeight, SRCCOPY);
        lpDDTemp->lpVtbl->ReleaseDC(lpDDTemp, hdc);
       }
    else
       {
        DD_Error(hresult, "CreateDDBitmap GetDC");
       }
    *lpDDSurf = lpDDTemp;
    DeleteDC(hdcImage);
   }

#define PU_CACHE 101


void RenderScene()
{
    DDSURFACEDESC ddsd;
    static int    i, j, x, y, dwsw, pad;
    HRESULT       hresult;
    DWORD        *Buff, *Scrn;
//	HDC		hdc; //foolhearted
//	COLORREF	bgc, fgc;
	//int	testjunk;


    if (GameMode != GAME_PLAY) return;

    if (CheckSurfaces() == FALSE) return;

    Scrn = (DWORD *)screens[0];
    ddsd.dwSize = sizeof(ddsd);
    lpDDSBack->lpVtbl->GetSurfaceDesc(lpDDSBack, &ddsd);
    hresult = lpDDSBack->lpVtbl->Lock(lpDDSBack, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
    if (hresult == DD_OK)
	{
        Buff = (DWORD *)ddsd.lpSurface;
        if (ddsd.lPitch == SCREENWIDTH)
            memcpy(Buff, Scrn, SCREENMULT);
        else
		{
            dwsw = SCREENWIDTH / 4;
            pad = ddsd.lPitch / 4;
            for (j = 0; j < SCREENHEIGHT; j++)
			{
                x = j * dwsw; // tried adding -- it's slower...don't know why ?!?
                y = j * pad;
                memcpy(&Buff[y], &Scrn[x], SCREENWIDTH);
			}
		}
        lpDDSBack->lpVtbl->Unlock(lpDDSBack, NULL);
	}
    else
		DD_Error(hresult, "Lock DD Surface");

    // we have rendered the backbuffer, call flip so we can see it
	while (lpDDSPrimary->lpVtbl->Flip(lpDDSPrimary, NULL, DDFLIP_WAIT) == DDERR_WASSTILLDRAWING);

	// 11.4.98 Benchmark the FPS (average for whole game)
	// actually blit/flips /S
	++framecount;

	//foolhearted testing
	//dxStartText("Heres some shit");

}


void ShutdownDirectDraw(void)
{
	int calcfps;	//11.4.98 junker

    WriteDebug("Running ShutdownDirectDraw...\n");
    if(lpDD2 != NULL)
		lpDD2->lpVtbl->RestoreDisplayMode(lpDD2);
    MoveWindow(hMainWnd, 0,0, dwCurrWidth, dwCurrHeight, TRUE);
    WriteDebug("Restored display mode...\n");
    if(lpDD2 != NULL)
        lpDD2->lpVtbl->SetCooperativeLevel(lpDD2, hMainWnd, DDSCL_NORMAL);
    WriteDebug("Reset cooperative level...\n");
    RELEASE(lpDDSBack);
    lpDDSBack = 0;
    RELEASE(lpDDSPrimary);
    lpDDSPrimary = 0;
    RELEASE(lpPalette);
    lpPalette = 0;
    RELEASE(lpDD2);
    lpDD2 = 0;
    RELEASE(lpDD);
    lpDD = 0;
    WriteDebug("Shutdown DirectDraw Complete...\n");
	time(&clock_out);	//11.4.98 dlw calc some FPS and write
	for(calcfps=0; calcfps<200; calcfps++)
		totalscoretextline[calcfps]=0;	//no junk-reuse text var
	if(clock_out-clock_in)
		calcfps = (int)( framecount/(clock_out-clock_in) );
	else
		calcfps=0;
	sprintf(totalscoretextline, "Frames Per Second: %d\n", calcfps);
	WriteDebug(totalscoretextline);

}

// Finds a DirectDraw driver that doesn't have 3D(if any)
BOOL FAR PASCAL DDEnumCallback(GUID FAR* lpGUID, LPSTR lpDriverDesc, LPSTR lpDriverName, LPVOID lpContext)
   {
    LPDIRECTDRAW  l_lpDD;
    DDCAPS DriverCaps, HELCaps;
    HRESULT hresult;

    // Make sure the guid is valid
    if (lpGUID)
       {
        // Try to create a DirectDraw object
        hresult = DirectDrawCreate(lpGUID, &l_lpDD, NULL);
        if (hresult != DD_OK)
           DD_Error(hresult, "DirectDrawCreate");
        
        // Get the DirectDraw capabilities
        memset(&DriverCaps, 0, sizeof(DDCAPS));
        DriverCaps.dwSize = sizeof(DDCAPS);
        
        memset(&HELCaps, 0, sizeof(DDCAPS));
        HELCaps.dwSize = sizeof(DDCAPS);
        
        hresult = l_lpDD->lpVtbl->GetCaps(l_lpDD, &DriverCaps, &HELCaps);
        if (hresult != DD_OK)
           DD_Error(hresult, "GetCaps");

        // Does this driver have 3D hardware capabilites?
        if (!(DriverCaps.dwCaps & DDCAPS_NOHARDWARE) && (DriverCaps.dwCaps & DDCAPS_GDI))
           {
            *(LPDIRECTDRAW*)lpContext = l_lpDD;
            return DDENUMRET_CANCEL;
           }
        else
           {
            *(LPDIRECTDRAW*)lpContext = NULL;
            l_lpDD->lpVtbl->Release(l_lpDD);
            DDCount++;
           }
       }
    return DDENUMRET_OK;
   }


static HRESULT CALLBACK EnumDisplayModesCallback(LPDDSURFACEDESC pddsd, LPVOID lpContext)
   {
    /*
     * Very large resolutions cause problems on some hardware.  They are also
     * not very useful for real-time rendering.  We have chosen to disable
     * them by not reporting them as available.
     */
    if (pddsd->dwWidth > 1024 || pddsd->dwHeight > 768)
        return DDENUMRET_OK;
    /*
     * Save this mode at the end of the mode array and increment mode count
     */
    Mode[NumModes].w = pddsd->dwWidth;
    Mode[NumModes].h = pddsd->dwHeight;
    Mode[NumModes].bpp = pddsd->ddpfPixelFormat.dwRGBBitCount;
    Mode[NumModes].bThisDriverCanDo = FALSE;
    if ((pddsd->ddsCaps.dwCaps & DDSCAPS_MODEX) == DDSCAPS_MODEX)
       Mode[NumModes].ModeX = TRUE;
    else
       Mode[NumModes].ModeX = FALSE;
    if ((pddsd->dwFlags & DDSD_REFRESHRATE) == DDSD_REFRESHRATE)
       Mode[NumModes].refresh = pddsd->dwRefreshRate;
    else
       Mode[NumModes].refresh = 0;
    NumModes++;
    if (NumModes == 50)
        return DDENUMRET_CANCEL;
    else
        return DDENUMRET_OK;
   }

// 10.20.98 dlw --- Finally unclogged my brain and got this simple didly
// workin.  Have it working fine in a buncho other dx progs.. but the C
// calls confused me.
void dxStartText(char *textline)
{
    DDBLTFX                     ddbltfx;
	HDC		hdc;
	COLORREF	bgc, fgc;
	static char scrollstorage[52][100];
	int stoppage, startage, ix;
	// time_t ltime, ltime2;	//timewasters for debug


	// save some words
	strcpy(scrollstorage[dxStCount], textline);

	// the things we will do to make windows look like dos
	// if the bottom line would overrun....
	startage = 1;
	stoppage = (SCREENHEIGHT / 20)-1;		// max print lines
	if( dxStCount > stoppage )					// 11>10 by 1
		startage=dxStCount-stoppage;			// start->counter = full screen

    ddbltfx.dwSize = sizeof(ddbltfx);
    ddbltfx.dwFillColor = 0;
    if(DD_OK != lpDDSBack->lpVtbl->Blt(lpDDSBack, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx) )
		WriteDebug("blt clear background failed\n");

	
	// foolhearted testing -- Got this working (my own stupid mistake) 10-20 dlw
	if( lpDDSBack->lpVtbl->GetDC(lpDDSBack, &hdc) == DD_OK)
	{
        //WriteDebug(".");
		bgc=SetBkColor(hdc, RGB(0, 0, 255));
		if(bgc == CLR_INVALID)
			WriteDebug("SetBkColor failed--bgc\n");
        fgc=SetTextColor(hdc, RGB(255, 255, 255));
		if(fgc == CLR_INVALID)
			WriteDebug("SetTextColor failed--fgc\n");
		if( !SetBkMode(hdc, TRANSPARENT) )
			WriteDebug("SetBkMode Transparant failed!\n");
		for(ix=0; startage<=dxStCount; startage++)
		{
			if( !TextOut(hdc, 0, ix*20, scrollstorage[startage], strlen(scrollstorage[startage]) ))
				WriteDebug("TextOut Failed Miserably\n");
			++ix;
		}
		//sprintf(scrollstorage[49], "%d count of lines\n", startage);
		//WriteDebug(scrollstorage[49]);
		if(DD_OK != lpDDSBack->lpVtbl->ReleaseDC(lpDDSBack, hdc) )
			WriteDebug("ReleaseDC crapped out\n");
	}
	else
		WriteDebug("GetDC for TextOot Crapped Oot, Eh?\n");

	while (lpDDSPrimary->lpVtbl->Flip(lpDDSPrimary, NULL, DDFLIP_WAIT) == DDERR_WASSTILLDRAWING);
	++framecount;  //11.4.98 better count 'em here too

	// timewaster for debugging
    //time( &ltime );
	//++ltime;
	//do
	//{
	//	time(&ltime2);
	//}while(ltime2<ltime);


}



















/////////////////////////////////////////////////////////////////////////////////////
// DirectInput
/////////////////////////////////////////////////////////////////////////////////////

BOOL InitDirectInput()
   {
    HRESULT hresult;
    LPDIRECTINPUTDEVICE lpJoy;
    DIPROPRANGE         diprg; 
    DIPROPDWORD         dipdw;

    hresult = DirectInputCreate(hInst, DIRECTINPUT_VERSION, &lpDirectInput, NULL );
    if (hresult != DI_OK)
       {
        DI_Error( hresult, "DirectInputCreate");
        return FALSE;
       }

    hresult = lpDirectInput->lpVtbl->CreateDevice(lpDirectInput, &GUID_SysMouse, &lpMouse, NULL );
    if (hresult != DI_OK)
       {
        DI_Error( hresult, "CreateDevice (mouse)");
        ShutdownDirectInput();
        return FALSE;
       }

    hresult = lpDirectInput->lpVtbl->CreateDevice(lpDirectInput, &GUID_Joystick, &lpJoy, NULL );
    if (hresult == DI_OK)
       {
        hresult = lpJoy->lpVtbl->QueryInterface(lpJoy,&IID_IDirectInputDevice2,(LPVOID *)&lpJoystick); 
        if (hresult != DI_OK)
           {
            DI_Error( hresult, "CreateDevice (real joystick)");
           }
        RELEASE(lpJoy);
       }


    hresult = lpDirectInput->lpVtbl->CreateDevice(lpDirectInput, &GUID_SysKeyboard, &lpKeyboard, NULL );
    if (hresult != DI_OK)
       {
        DI_Error( hresult, "CreateDevice (keyboard)");
        ShutdownDirectInput();
        return FALSE;
       }

    hresult = lpMouse->lpVtbl->SetDataFormat(lpMouse, &c_dfDIMouse);
    if (hresult != DI_OK)
       {
        DI_Error( hresult, "SetDataFormat (mouse)");
        ShutdownDirectInput();
        return FALSE;
       }

    if (lpJoystick != 0)
       {
        hresult = lpJoystick->lpVtbl->SetDataFormat(lpJoystick, &c_dfDIJoystick);
        if (hresult != DI_OK)
           {
            DI_Error( hresult, "SetDataFormat (joystick)");
            ShutdownDirectInput();
            return FALSE;
           }
       }

    hresult = lpKeyboard->lpVtbl->SetDataFormat(lpKeyboard, &c_dfDIKeyboard);
    if (hresult != DI_OK)
       {
        DI_Error( hresult, "SetDataFormat (keyboard)");
        ShutdownDirectInput();
        return FALSE;
       }

    if (lpJoystick != 0)
       {
        hresult = lpJoystick->lpVtbl->SetCooperativeLevel(lpJoystick, hMainWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
        if (hresult != DI_OK)
           {
            DI_Error( hresult, "SetCooperativeLevel (joystick)");
            ShutdownDirectInput();
            return FALSE;
           }
        else
           {
            diprg.diph.dwSize       = sizeof(diprg); 
            diprg.diph.dwHeaderSize = sizeof(diprg.diph); 
            diprg.diph.dwObj        = DIJOFS_X; 
            diprg.diph.dwHow        = DIPH_BYOFFSET; 
            diprg.lMin              = -1000; 
            diprg.lMax              = +1000; 
            lpJoystick->lpVtbl->SetProperty(lpJoystick, DIPROP_RANGE, &diprg.diph);
            diprg.diph.dwObj        = DIJOFS_Y; 
            lpJoystick->lpVtbl->SetProperty(lpJoystick, DIPROP_RANGE, &diprg.diph);

            dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
            dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
            dipdw.diph.dwHow        = DIPH_BYOFFSET;
            dipdw.dwData            = 1500;
            dipdw.diph.dwObj        = DIJOFS_X;
            lpJoystick->lpVtbl->SetProperty(lpJoystick, DIPROP_DEADZONE, &dipdw.diph);
            dipdw.diph.dwObj         = DIJOFS_Y;
            lpJoystick->lpVtbl->SetProperty(lpJoystick, DIPROP_DEADZONE, &dipdw.diph);
           }
       }

    hresult = lpMouse->lpVtbl->SetCooperativeLevel(lpMouse, hMainWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
    if (hresult != DI_OK)
       {
        DI_Error( hresult, "SetCooperativeLevel (mouse)");
        ShutdownDirectInput();
        return FALSE;
       }

    hresult = lpKeyboard->lpVtbl->SetCooperativeLevel(lpKeyboard, hMainWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
    if (hresult != DI_OK)
       {
        DI_Error( hresult, "SetCooperativeLevel (keyboard)");
        ShutdownDirectInput();
        return FALSE;
       }

    hresult = lpMouse->lpVtbl->Acquire(lpMouse);
    if (lpJoystick != 0)
       hresult = lpJoystick->lpVtbl->Acquire(lpJoystick);
    hresult = lpKeyboard->lpVtbl->Acquire(lpKeyboard);

    return TRUE;
   }


void ShutdownDirectInput()
{
    if (lpKeyboard != 0)
       {
        lpKeyboard->lpVtbl->Unacquire(lpKeyboard);
        lpKeyboard->lpVtbl->Release(lpKeyboard);
        lpKeyboard = 0;
       }
    if (lpJoystick != 0)
       {
        lpJoystick->lpVtbl->Unacquire(lpJoystick);
        lpJoystick->lpVtbl->Release(lpJoystick);
        lpJoystick = 0;
       }
    if (lpMouse != 0)
       {
        lpMouse->lpVtbl->Unacquire(lpMouse);
        lpMouse->lpVtbl->Release(lpMouse);
        lpMouse = 0;
       }

    if(lpDirectInput) RELEASE(lpDirectInput);
}

