/*
	WinSTon

	DirectX
*/

#include "..\includes\winston.h"
#include "..\includes\debug.h"
#include "..\includes\dialog.h"
#include "..\includes\dsurface.h"
#include "..\includes\errlog.h"
#include "..\includes\memory.h"
#include "..\includes\misc.h"
#include "..\includes\screen.h"
#include "..\includes\video.h"
#include "..\includes\view.h"
#include "..\includes\vdi.h"

BOOL bDisableDirectDraw=FALSE;
BOOL bDirectDrawWorking = FALSE;					// TRUE if DirectX installed
IDirectDraw *pDDraw=NULL;							// Direct Draw object
IDirectDrawSurface *pPrimarySurface;				// Primary surface
IDirectDrawSurface *pBackSurface;					// Back surface
IDirectDrawPalette *pDDrawPalette=NULL;				// Direct Draw Palette
PALETTEENTRY DDrawPaletteEntry[256];				// Palette RGBs
unsigned char *pGDIWorkspace=NULL;					// GDI surface workspace to repair full-screen
BMP GDIBitmap;										// Bitmap for above
HPALETTE GDIPalette=NULL;							// Palette for 8-bit GDI surface

BOOL bAvailableDDrawModes[NUM_AVAILABLE_DDRAW_MODES];	// Flags of modes for index as MODE_320x200x256
BOOL bDisplayModeValid[NUM_DISPLAYMODEOPTIONS];			// List if modes valid
int DisplayModeOptions[NUM_DISPLAYMODEOPTIONS];			// List of DisplayModes we can use
int nDisplayModeOptions;
char szDisplayModeTextNames[NUM_DISPLAYMODEOPTIONS][48];	// Text names to match DISPLAYMODE_xxxx(have enough space for 'fastest','slowest')
char *pszAvailableDisplayModeTextNames[NUM_DISPLAYMODEOPTIONS+1];

int nVDIModeOptions;
int VDIModeOptions[NUM_VDIMODEOPTIONS];					// List of VDIModes we can use
char szVDIModeTextNames[NUM_VDIMODEOPTIONS][48];		// Text names to match VDIMODE_xxxx
char *pszAvailableVDIModeTextNames[NUM_VDIMODEOPTIONS+1];

RECT OldWindowPos;
long WindowFlags,WindowExFlags,WindowMinFlags;		// Store window flags, to reset when return from full screen
unsigned short int RedMask,GreenMask,BlueMask,RedMaskShift,GreenMaskShift,BlueMaskShift;
int CurrentDisplayMode;								// Current mode we are in, eg MODE_320x200x256

struct {												// 8-Bit palette
	WORD Version;
	WORD NumberOfEntries;
	PALETTEENTRY Entries[256];
} LogPalette = { 0x300,256 };

DISPLAYMODE_DETAILS DisplayModesDetails[NUM_AVAILABLE_DDRAW_MODES] = {
 { 320,200,8 },		// MODE_320x200x256
 { 320,240,8 },		// MODE_320x240x256
 { 640,400,8 },		// MODE_640x400x256
 { 640,480,8 },		// MODE_640x480x256
 { 800,600,8 },		// MODE_800x600x256
 { 1024,768,8 },	// MODE_1024x768x256
 { 320,200,16 },	// MODE_320x200x16BIT
 { 320,240,16 },	// MODE_320x240x16BIT
 { 640,400,16 },	// MODE_640x400x16BIT
 { 640,480,16 },	// MODE_640x480x16BIT
 { 800,600,16 },	// MODE_800x600x16BIT
 { 1024,768,16 },	// MODE_1024x768x16BIT
};

// Default Windows system palette(20 entries)
PALETTEENTRY SystemColours[] = {
	{ 0,0,0, PC_NOCOLLAPSE },
	{ 128,0,0, PC_NOCOLLAPSE },
	{ 0,128,0, PC_NOCOLLAPSE },
	{ 128,128,0, PC_NOCOLLAPSE },
	{ 0,0,128, PC_NOCOLLAPSE },
	{ 128,0,128, PC_NOCOLLAPSE },
	{ 0,128,128, PC_NOCOLLAPSE },
	{ 192,192,192, PC_NOCOLLAPSE },
	{ 192,220,192, PC_NOCOLLAPSE },
	{ 166,202,240, PC_NOCOLLAPSE },

	{ 255,251,240, PC_NOCOLLAPSE },
	{ 160,160,164, PC_NOCOLLAPSE },
	{ 128,128,128, PC_NOCOLLAPSE },
	{ 255,0,0, PC_NOCOLLAPSE },
	{ 0,255,0, PC_NOCOLLAPSE },
	{ 255,255,0, PC_NOCOLLAPSE },
	{ 0,0,255, PC_NOCOLLAPSE },
	{ 255,0,255, PC_NOCOLLAPSE },
	{ 0,255,255, PC_NOCOLLAPSE },
	{ 255,255,255, PC_NOCOLLAPSE },
};

//-----------------------------------------------------------------------
/*
	Find Mask/Shift values for RGB mask returned from DirectX
*/
void DSurface_AssignPixelFormat(unsigned short int BitMask,unsigned short int *pMask,unsigned short int *pMaskShift)
{
	int Shift=0;
	int i;

	// Find amount to shift(highest set bit)
	for(i=15; (i>=0) && (Shift==0); i--) {
		if (BitMask&(1<<i)) {						// Is bit set?
			Shift = i-2;							// Store shift, less 2 bits(0-7 for ST colour value)
		}
	}

	*pMaskShift = Shift;			// eg 13
	*pMask = BitMask;				// eg 0xf800, mask entries with this
}

//-----------------------------------------------------------------------
/*
	Set Direct Draw resolution, forced to 640x480x16bit
*/
BOOL DSurface_SetDDrawResolution(int Mode)
{
	DDSURFACEDESC Surface;
	DDSCAPS DDrawCaps;
	DDPIXELFORMAT DDPixelFormat;
	HRESULT ddRetVal;
	int i;

	// NOTE At this point we know we are able to choose correct resolution
	if (pDDraw->SetDisplayMode(DisplayModesDetails[Mode].Width,DisplayModesDetails[Mode].Height,DisplayModesDetails[Mode].BitDepth)!=DD_OK) {
		// Failed, report error
		ErrLog_File("ERROR DirectDraw: Unable to 'SetDisplayMode'\n");
		return(FALSE);
	}

	// Create Direct Draw surface
	Memory_Clear(&Surface,sizeof(DDSURFACEDESC));
	Surface.dwSize = sizeof(DDSURFACEDESC);
	Surface.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
	Surface.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
	// Try for a double screen buffer
	Surface.dwBackBufferCount = 1;
	ddRetVal = pDDraw->CreateSurface(&Surface, &pPrimarySurface, NULL);

	if (ddRetVal!=DD_OK) {
		ErrLog_File("ERROR DirectDraw: Unable to 'CreateSurface'\n");
		return(FALSE);
	}
	else {
		// Find pointer to work screen
		DDrawCaps.dwCaps = DDSCAPS_BACKBUFFER;
		ddRetVal = pPrimarySurface->GetAttachedSurface(&DDrawCaps, &pBackSurface);
		if (ddRetVal!=DD_OK) {
			ErrLog_File("ERROR DirectDraw: Unable to 'GetAttachedSurface'\n");
			return(FALSE);
		}

		// Find pixel depth OR set palette(according to pixel depth of selected display mode)
		if (DisplayModesDetails[Mode].BitDepth==16) {
			DDPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
			ddRetVal = pPrimarySurface->GetPixelFormat(&DDPixelFormat);
			if (ddRetVal!=DD_OK) {
				ErrLog_File("ERROR DirectDraw: Unable to 'GetPixelFormat'\n");
				return(FALSE);
			}

			// Find Masks/Shifts for RGB format, eg RGB565 give shifts of 13,8,2 etc...
			// Calculate this as may have many differing formats - NOTE reports
			// of NT returning '0' for GetPixelFormat. Output to log file if find this!
			DSurface_AssignPixelFormat(DDPixelFormat.dwRBitMask,&RedMask,&RedMaskShift);
			DSurface_AssignPixelFormat(DDPixelFormat.dwGBitMask,&GreenMask,&GreenMaskShift);
			DSurface_AssignPixelFormat(DDPixelFormat.dwBBitMask,&BlueMask,&BlueMaskShift);
			if ( (RedMaskShift==0) && (GreenMaskShift==0) && (BlueMaskShift==0) )
				ErrLog_File("DirectDraw: GetPixelFormat unable to find RGB masks\n");
		}
		else {
			// We've chosen a paletted display so create and set palette
			if (!pDDrawPalette) {
				// Create test grey scale palette
				for(i=0; i<256; i++) {
					DDrawPaletteEntry[i].peRed = i<<4;
					DDrawPaletteEntry[i].peGreen = i<<4;
					DDrawPaletteEntry[i].peBlue = i<<4;
					DDrawPaletteEntry[i].peFlags = PC_NOCOLLAPSE;
				}
				// Copy over 20 system palette entries, so menus/dialog look OK in 8-bit displays
				memcpy(&DDrawPaletteEntry[0],&SystemColours[0],sizeof(PALETTEENTRY)*10);
				memcpy(&DDrawPaletteEntry[246],&SystemColours[10],sizeof(PALETTEENTRY)*10);
				// And copy to GDI bitmap,and logical palette
				for(i=0; i<256; i++) {
					LogPalette.Entries[i].peRed = GDIBitmap.Colours[i].rgbRed = DDrawPaletteEntry[i].peRed;
					LogPalette.Entries[i].peGreen = GDIBitmap.Colours[i].rgbGreen = DDrawPaletteEntry[i].peGreen;
					LogPalette.Entries[i].peBlue = GDIBitmap.Colours[i].rgbBlue = DDrawPaletteEntry[i].peBlue;
					LogPalette.Entries[i].peFlags = GDIBitmap.Colours[i].rgbReserved = 0;
				}

				// And set
				ddRetVal = pDDraw->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, DDrawPaletteEntry, &pDDrawPalette, NULL);
				if (ddRetVal!=DD_OK) {
					ErrLog_File("ERROR DirectDraw: Unable to 'CreatePalette'\n");
					return(FALSE);
				}
			}
			// Set palette
			DSurface_SetPalette();
		}
	}

	// Set mode
	CurrentDisplayMode = Mode;
	return(TRUE);
}

//-----------------------------------------------------------------------
/*
	Set palette from screen
*/
void DSurface_SetPalette(void)
{
	int i;

	// Copy in our ST palette
	for(i=0; i<16; i++) {
		LogPalette.Entries[i+10].peRed = ((HBLPalettes[i]>>8)&0x7)<<5;
		LogPalette.Entries[i+10].peGreen = ((HBLPalettes[i]>>4)&0x7)<<5;
		LogPalette.Entries[i+10].peBlue = (HBLPalettes[i]&0x7)<<5;
		LogPalette.Entries[i+10].peFlags = PC_RESERVED;
	}
	pDDrawPalette->SetEntries(0,10,16,&LogPalette.Entries[10]);
	pPrimarySurface->SetPalette(pDDrawPalette);
}

//-----------------------------------------------------------------------
/*
	Return TRUE if we are running in a high-resolution full-screen mode, ie 640x400 or higher
*/
BOOL DSurface_IsHighResolutionMode(void)
{
	if ( (DisplayModesDetails[CurrentDisplayMode].Width>=640) && (DisplayModesDetails[CurrentDisplayMode].Height>=400) )
		return(TRUE);
	return(FALSE);
}

//-----------------------------------------------------------------------
/*
	Resize Window to size of desktop to prevent any other windows being drawn
*/
void DSurface_EnlargeWindow(void)
{
	// Expand our window to full screen to overlap other window and start menu
	// And prevent from resizing
	GetWindowRect(hWnd,&OldWindowPos);
	SetWindowPos(hWnd,HWND_TOPMOST,0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN),0);
	WindowFlags = GetWindowLong(hWnd, GWL_STYLE);
	WindowExFlags = GetWindowLong(hWnd, GWL_EXSTYLE);
	SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
}

//-----------------------------------------------------------------------
/*
	Shrink Window to original size after 'DSurface_EnlargeWindow()'
*/
void DSurface_ShrinkWindow(void)
{
	// Return window to normal size and viewing
	SetWindowLong(hWnd, GWL_STYLE, WindowFlags);
	SetWindowLong(hWnd, GWL_EXSTYLE, WindowExFlags);
	SetWindowPos(hWnd, HWND_BOTTOM, OldWindowPos.left, OldWindowPos.top, OldWindowPos.right-OldWindowPos.left, OldWindowPos.bottom-OldWindowPos.top, 0);
}

//-----------------------------------------------------------------------
/*
	Go full screen, pass mode eg MODE_320x200x256
	Returns TRUE if mode was changed, FALSE is same or error
*/
BOOL DSurface_EnterDDrawFullScreen(int Mode)
{
	HRESULT ddRetVal;

	if (pDDraw && bDirectDrawWorking) {
		if (!bInFullScreen) {
			// Enlarge Window to cover screen
			DSurface_EnlargeWindow();
			// Set full screen
			ddRetVal = pDDraw->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_NOWINDOWCHANGES);
			if (ddRetVal==DD_OK ) {
				// Goto into resired resolution
				if (!DSurface_SetDDrawResolution(Mode)) {
					// Failed, return to normal
					ddRetVal = pDDraw->SetCooperativeLevel(hWnd, DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES);
					if (ddRetVal!=DD_OK ) {
						// Cannot set mode
						ErrLog_File("ERROR DirectDraw: Unable to 'SetCooperativeLevel', error code %d\n",ddRetVal);
						// Return window to normal size and viewing
						DSurface_ShrinkWindow();
					}

					return(FALSE);
				}

				// Set full screen flag
				bInFullScreen = TRUE;
				Screen_SetupRGBTable(TRUE);				// Set full screen RGB

				return(TRUE);
			}
			else {
				// Cannot set cooperative level
				ErrLog_File("ERROR DirectDraw: Unable to 'SetCooperativeLevel', error code %d\n",ddRetVal);
				// Return window to normal size and viewing
				DSurface_ShrinkWindow();

				return(FALSE);
			}
		}
		else {
			// Already in full-screen mode so swap display(only if differs)
			if (CurrentDisplayMode!=Mode) {
				// Return to normal
				ddRetVal = pDDraw->SetCooperativeLevel(hWnd, DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES);
				if (ddRetVal==DD_OK ) {
					ddRetVal = pDDraw->RestoreDisplayMode();
					if (ddRetVal!=DD_OK ) {
						// Cannot restore display mode
						ErrLog_File("ERROR DirectDraw: Unable to 'RestoreDisplayMode', error code %d\n",ddRetVal);
						// Return window to normal size and viewing
						DSurface_ShrinkWindow();

						return(FALSE);
					}
	
					// Free surfaces
					pBackSurface->Release();
					pPrimarySurface->Release();

					// Set new
					ddRetVal = pDDraw->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_NOWINDOWCHANGES);
					if (ddRetVal!=DD_OK ) {
						// Cannot set cooperative level
						ErrLog_File("ERROR DirectDraw: Unable to 'SetCooperativeLevel', error code %d\n",ddRetVal);
						// Return window to normal size and viewing
						DSurface_ShrinkWindow();

						return(FALSE);
					}
					if (!DSurface_SetDDrawResolution(Mode)) {
						// Failed, return to normal
						ddRetVal = pDDraw->SetCooperativeLevel(hWnd, DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES);
						if (ddRetVal!=DD_OK ) {
							// Cannot set cooperative level
							ErrLog_File("ERROR DirectDraw: Unable to 'SetCooperativeLevel', error code %d\n",ddRetVal);
						}
						// Return window to normal size and viewing
						DSurface_ShrinkWindow();

						return(FALSE);
					}

					Screen_SetupRGBTable(TRUE);				// Set full screen RGB

					return(TRUE);
				}
				else {
					// Cannot set cooperative level
					ErrLog_File("ERROR DirectDraw: Unable to 'SetCooperativeLevel', error code %d\n",ddRetVal);
					// Return window to normal size and viewing
					DSurface_ShrinkWindow();

					return(FALSE);
				}
			}
			else
				return(FALSE);
		}
	}

	return(FALSE);
}

//-----------------------------------------------------------------------
/*
	Back to a window
*/
BOOL DSurface_ReturnFromDDrawFullScreen(void)
{
	HRESULT ddRetVal;

	if (pDDraw && bDirectDrawWorking && bInFullScreen) {
		// Clear full screen flag, need first as don't focus messages to access Direct Draw
		bInFullScreen = FALSE;
		// Free any GDI workspace
		DSurface_FreeGDIScreen();

		// Return to normal
		ddRetVal = pDDraw->SetCooperativeLevel(hWnd, DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES);
		if (ddRetVal!=DD_OK ) {
			// Cannot set cooperative level
			ErrLog_File("ERROR DirectDraw: Unable to 'SetCooperativeLevel', error code %d\n",ddRetVal);

			return(FALSE);
		}
		ddRetVal = pDDraw->RestoreDisplayMode();
		if (ddRetVal!=DD_OK ) {
			// Cannot restore display mode
			ErrLog_File("ERROR DirectDraw: Unable to 'RestoreDisplayMode', error code %d\n",ddRetVal);

			return(FALSE);
		}
		
		// Free surfaces
		pBackSurface->Release();
		pPrimarySurface->Release();

		// Return window to normal size and viewing
		DSurface_ShrinkWindow();

		return(TRUE);
	}

	return(FALSE);
}

//-----------------------------------------------------------------------
/*
	Direct Draw resolution callback - fill in flags for resolutions we are interested in
*/
HRESULT CALLBACK DSurface_EnumDisplayModesCallback(DDSURFACEDESC *pSurface, VOID *pContext)
{
	// Check 256 colours modes
	if ( (pSurface->dwWidth==320) && (pSurface->dwHeight==200) && (pSurface->ddpfPixelFormat.dwRGBBitCount==8) )
		bAvailableDDrawModes[MODE_320x200x256] = TRUE;
	if ( (pSurface->dwWidth==320) && (pSurface->dwHeight==240) && (pSurface->ddpfPixelFormat.dwRGBBitCount==8) )
		bAvailableDDrawModes[MODE_320x240x256] = TRUE;
	if ( (pSurface->dwWidth==640) && (pSurface->dwHeight==400) && (pSurface->ddpfPixelFormat.dwRGBBitCount==8) )
		bAvailableDDrawModes[MODE_640x400x256] = TRUE;
	if ( (pSurface->dwWidth==640) && (pSurface->dwHeight==480) && (pSurface->ddpfPixelFormat.dwRGBBitCount==8) )
		bAvailableDDrawModes[MODE_640x480x256] = TRUE;
	if ( (pSurface->dwWidth==800) && (pSurface->dwHeight==600) && (pSurface->ddpfPixelFormat.dwRGBBitCount==8) )
		bAvailableDDrawModes[MODE_800x600x256] = TRUE;
	if ( (pSurface->dwWidth==1024) && (pSurface->dwHeight==768) && (pSurface->ddpfPixelFormat.dwRGBBitCount==8) )
		bAvailableDDrawModes[MODE_1024x768x256] = TRUE;
	// And 16-bit
	if ( (pSurface->dwWidth==320) && (pSurface->dwHeight==200) && (pSurface->ddpfPixelFormat.dwRGBBitCount==16) )
		bAvailableDDrawModes[MODE_320x200x16BIT] = TRUE;
	if ( (pSurface->dwWidth==320) && (pSurface->dwHeight==240) && (pSurface->ddpfPixelFormat.dwRGBBitCount==16) )
		bAvailableDDrawModes[MODE_320x240x16BIT] = TRUE;
	if ( (pSurface->dwWidth==640) && (pSurface->dwHeight==400) && (pSurface->ddpfPixelFormat.dwRGBBitCount==16) )
		bAvailableDDrawModes[MODE_640x400x16BIT] = TRUE;
	if ( (pSurface->dwWidth==640) && (pSurface->dwHeight==480) && (pSurface->ddpfPixelFormat.dwRGBBitCount==16) )
		bAvailableDDrawModes[MODE_640x480x16BIT] = TRUE;
	if ( (pSurface->dwWidth==800) && (pSurface->dwHeight==600) && (pSurface->ddpfPixelFormat.dwRGBBitCount==16) )
		bAvailableDDrawModes[MODE_800x600x16BIT] = TRUE;
	if ( (pSurface->dwWidth==1024) && (pSurface->dwHeight==768) && (pSurface->ddpfPixelFormat.dwRGBBitCount==16) )
		bAvailableDDrawModes[MODE_1024x768x16BIT] = TRUE;
	
	// Output to log file
	ErrLog_File("\t%d x %d : %d bit\n",pSurface->dwWidth,pSurface->dwHeight,pSurface->ddpfPixelFormat.dwRGBBitCount);

    return (DDENUMRET_OK);
}

//-----------------------------------------------------------------------
/*
	Check we have correct resolution needed to run WinSTon in DirectX
	Return TRUE is all OK
*/
BOOL DSurface_CheckDirectDDisplayModes(void)
{
	int i;

	nDisplayModeOptions = nVDIModeOptions =0;
	Memory_Clear(bDisplayModeValid,sizeof(BOOL)*NUM_DISPLAYMODEOPTIONS);

	// Now check if we have enough resolutions for our 4 screen display options
	if ( (bAvailableDDrawModes[MODE_320x200x256]||bAvailableDDrawModes[MODE_320x240x256]) && (bAvailableDDrawModes[MODE_640x480x256]) ) {
		DisplayModeOptions[nDisplayModeOptions++] = DISPLAYMODE_16COL_LOWRES;
		bDisplayModeValid[DISPLAYMODE_16COL_LOWRES] = TRUE;
		ErrLog_File("DirectDraw: Found correct video modes for 16 Colour, Low Resolution\n");
	}
	if (bAvailableDDrawModes[MODE_640x480x256]) {
		DisplayModeOptions[nDisplayModeOptions++] = DISPLAYMODE_16COL_HIGHRES;
		bDisplayModeValid[DISPLAYMODE_16COL_HIGHRES] = TRUE;
		ErrLog_File("DirectDraw: Found correct video modes for 16 Colour, High Resolution\n");
	}
	if (bAvailableDDrawModes[MODE_800x600x256]) {
		DisplayModeOptions[nDisplayModeOptions++] = DISPLAYMODE_16COL_FULL;
		bDisplayModeValid[DISPLAYMODE_16COL_FULL] = TRUE;
		ErrLog_File("DirectDraw: Found correct video modes for 16 Colour, Full View\n");
	}
	if ( (bAvailableDDrawModes[MODE_320x200x16BIT]||bAvailableDDrawModes[MODE_320x240x16BIT]) && bAvailableDDrawModes[MODE_640x480x16BIT] && (bAvailableDDrawModes[MODE_640x480x256]) ) {
		DisplayModeOptions[nDisplayModeOptions++] = DISPLAYMODE_HICOL_LOWRES;
		bDisplayModeValid[DISPLAYMODE_HICOL_LOWRES] = TRUE;
		ErrLog_File("DirectDraw: Found correct video modes for Hi-Colour, Low Resolution\n");
	}
	if (bAvailableDDrawModes[MODE_640x480x16BIT] && bAvailableDDrawModes[MODE_640x480x256]) {
		DisplayModeOptions[nDisplayModeOptions++] = DISPLAYMODE_HICOL_HIGHRES;
		bDisplayModeValid[DISPLAYMODE_HICOL_HIGHRES] = TRUE;
		ErrLog_File("DirectDraw: Found correct video modes for Hi-Colour, High Resolution\n");
	}
	if (bAvailableDDrawModes[MODE_800x600x16BIT]) {
		DisplayModeOptions[nDisplayModeOptions++] = DISPLAYMODE_HICOL_FULL;
		bDisplayModeValid[DISPLAYMODE_HICOL_FULL] = TRUE;
		ErrLog_File("DirectDraw: Found correct video modes for Hi-Colour, Full View\n");
	}
	// Create text names to show in dialog
	strcpy(szDisplayModeTextNames[0],"16 Colours, Low Resolution");
	strcpy(szDisplayModeTextNames[1],"16 Colours, High Resolution");
	strcpy(szDisplayModeTextNames[2],"16 Colours, Full View");
	strcpy(szDisplayModeTextNames[3],"Hi-Colour, Low Resolution");
	strcpy(szDisplayModeTextNames[4],"Hi-Colour, High Resolution");
	strcpy(szDisplayModeTextNames[5],"Hi-Colour, Full View");
	// And add (fastest) and (slowest) to names
	if (nDisplayModeOptions>=2) {
		strcat(szDisplayModeTextNames[ DisplayModeOptions[0] ],"(fastest)");
		strcat(szDisplayModeTextNames[ DisplayModeOptions[nDisplayModeOptions-1] ],"(slowest)");
	}
	// Set up as list for dialog
	for(i=0; i<nDisplayModeOptions; i++)
		pszAvailableDisplayModeTextNames[i] = szDisplayModeTextNames[DisplayModeOptions[i]];
	// And term
	pszAvailableDisplayModeTextNames[i] = NULL;

	// Now check if we have enough resolutions for our 3 VDI screen display options
	if (bAvailableDDrawModes[MODE_640x480x256]) {
		VDIModeOptions[nVDIModeOptions++] = VDIMODE_640x480;
		ErrLog_File("DirectDraw: Found correct video mode for VDI 640x480\n");
	}
	if (bAvailableDDrawModes[MODE_800x600x256]) {
		VDIModeOptions[nVDIModeOptions++] = VDIMODE_800x600;
		ErrLog_File("DirectDraw: Found correct video mode for VDI 800x600\n");
	}
	if (bAvailableDDrawModes[MODE_1024x768x256]) {
		VDIModeOptions[nVDIModeOptions++] = VDIMODE_1024x768;
		ErrLog_File("DirectDraw: Found correct video mode for VDI 1024x768\n");
	}
	
	// Create text names to show in dialog
	strcpy(szVDIModeTextNames[0],"640x480");
	strcpy(szVDIModeTextNames[1],"800x600");
	strcpy(szVDIModeTextNames[2],"1024x768");
	// Set up as list for dialog
	for(i=0; i<nVDIModeOptions; i++)
		pszAvailableVDIModeTextNames[i] = szVDIModeTextNames[VDIModeOptions[i]];
	// And term
	pszAvailableVDIModeTextNames[i] = NULL;


	// Check have at least one display option available
	if (nDisplayModeOptions!=0) {
		// Is chosen DisplayMode valid? If not choose best we can
		if (!bDisplayModeValid[ConfigureParams.Screen.ChosenDisplayMode])
			ConfigureParams.Screen.ChosenDisplayMode = DisplayModeOptions[nDisplayModeOptions-1];
		return(TRUE);
	}

	// DirectX support is not good enough - duff drivers and/or card
	return(FALSE);
}

//-----------------------------------------------------------------------
/*
	Setup Direct Draw
*/
void DSurface_Init(void)
{
	HRESULT ddRetVal;

	// Is enabled?
	if (bDisableDirectDraw) {
		// Stop any Direct Draw access
		ErrLog_File("DirectDraw: Disabled\n");
		bDirectDrawWorking = FALSE;
		return;
	}

	// Create Direct Draw object
	ddRetVal = DirectDrawCreate(0, &pDDraw, 0);
    if (ddRetVal!=DD_OK) {
		// Error, cannot find DirectX
		bDirectDrawWorking = FALSE;
		ErrLog_File("ERROR DirectDraw: Unable to 'DirectDrawCreate', error code %d\n",ddRetVal);
	}
	else {	// Direct Draw is initialised
		bDirectDrawWorking = FALSE;
		ErrLog_File("DirectDraw: 'DirectDrawCreate' - OK\n");

		// Set ready for full screen
		ddRetVal = pDDraw->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_NOWINDOWCHANGES);
		if (ddRetVal==DD_OK) {
			ErrLog_File("DirectDraw: 'SetCooperativeLevel' - OK\n");
			// Find resolutions available for this mode, store to DDrawModes[] array
			ErrLog_File("DirectDraw: Available video modes:-\n");
			// Clear flags
			Memory_Clear(bAvailableDDrawModes,sizeof(BOOL)*NUM_AVAILABLE_DDRAW_MODES);
			// Enumerate display modes, set flags for usable resolutions
		    pDDraw->EnumDisplayModes(0, 0, 0, DSurface_EnumDisplayModesCallback);
			// Check if we have correct display mode - ie MUST have set of 320x200,320x240 and 640x480 at 256colours or 16bit colour depth
			if (DSurface_CheckDirectDDisplayModes()) {
				// And reset
				pDDraw->SetCooperativeLevel(hWnd, DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES);
				// Seems OK!
				bDirectDrawWorking = TRUE;
			}
			else {
				// We've got shitty graphics card and/or drivers. Give user the bad news
				ErrLog_File("ERROR DirectDraw: Unable to find video modes needed\n");
				ErrLog_File("WinSTon checks for 320x200,320x240 and 640x480 in 256 and 16Bit colour resolutions to run in full-screen mode.\n");
				ErrLog_File("Please install latest version of DirectX and/or video card drivers!\n");
			}
		}
		else {
			// Cannot set cooperative level
			ErrLog_File("ERROR DirectDraw: Unable to 'SetCooperativeLevel', error code %d\n",ddRetVal);
		}
	}
}

//-----------------------------------------------------------------------
/*
	Free Direct Draw
*/
void DSurface_UnInit(void)
{
    if (pDDraw) {
		// Return to window on quit
		if (bInFullScreen)
			DSurface_ReturnFromDDrawFullScreen();

		// And Direct Draw
		pDDraw->Release();
		pDDraw = NULL;
	}
}

//-----------------------------------------------------------------------
/*
	Copy from locked 16-bit DirectSurface to workspace, convert to Windows RGB 1555
*/
void DSurface_CopyScreen_16Bit(int Width,int Height,int BytesPerLine,unsigned short int *pGDILine,unsigned char *pScreenLine)
{
	unsigned short int *pScreen;
	unsigned short int Colour,Red,Green,Blue;
	int x,y;

	for(y=0; y<Height; y++) {
		pScreen = (unsigned short int *)pScreenLine;
		for(x=0; x<Width; x++) {
			// Convert pixel from DirectSurface format to Windows RGB 1555
			Colour = *pScreen++;
			Red = (Colour>>(RedMaskShift-2))&0x1f;
			Green = (Colour>>(GreenMaskShift-2))&0x1f;
			Blue = (Colour>>(BlueMaskShift-2))&0x1f;
			Colour = (Red<<10) | (Green<<5) | Blue;
			*pGDILine++ = Colour;
		}
		pScreenLine += BytesPerLine;
	}
}

//-----------------------------------------------------------------------
/*
	Copy from locked 8-bit DirectSurface to workspace
*/
void DSurface_CopyScreen_8Bit(int Width,int Height,int BytesPerLine,unsigned char *pGDILine,unsigned char *pScreenLine)
{
	int y;

	for(y=0; y<Height; y++) {
		// Just copy line of pixels
		memcpy(pGDILine,pScreenLine,Width);
		pGDILine += Width;
		pScreenLine += BytesPerLine;
	}
}

//-----------------------------------------------------------------------
/*
	Update both DirectX buffers, and copy contents into a Windows bitmap which
	we can use later to blit back to the GDI surface to repair the screen when showing
	dialogs and so on...
*/
BOOL DSurface_CreateGDIScreen(void)
{
	DDSURFACEDESC Surface;
	FRAMEBUFFER *pOtherFrameBuffer;
	int i,WorkspaceSize;

	// We need to make BOTH frame buffers hold the same contents(show the current for the user, have
	// the same so we can grab the data from the backbuffer, and also keep it sweet for when we release)
	// To do this, we cause a full-update and re-draw both buffers using the current ST screen data which
	// must be copied into both ST frame buffers(to keep same contents on each when draws) and also
	// in copy buffers(used when we release). Phew!
	pOtherFrameBuffer = Screen_GetOtherFrameBuffer();
	Screen_SetFullUpdate();								// Cause full update of screen
	memcpy(pFrameBuffer->pSTScreenCopy,pFrameBuffer->pSTScreen,((MAX_VDI_WIDTH*MAX_VDI_PLANES)/8)*MAX_VDI_HEIGHT);
	memcpy(pOtherFrameBuffer->pSTScreen,pFrameBuffer->pSTScreen,((MAX_VDI_WIDTH*MAX_VDI_PLANES)/8)*MAX_VDI_HEIGHT);
	memcpy(pOtherFrameBuffer->pSTScreenCopy,pFrameBuffer->pSTScreen,((MAX_VDI_WIDTH*MAX_VDI_PLANES)/8)*MAX_VDI_HEIGHT);

	// Re-draw both screens fully
	Screen_DrawFrame(TRUE);								// Force to draw(and flip)
	Screen_DrawFrame(TRUE);								// Force to draw(now both buffer hold same contents)


	// Allocate space for workspace(store background)
	WorkspaceSize = DisplayModesDetails[CurrentDisplayMode].Width*DisplayModesDetails[CurrentDisplayMode].Height*sizeof(short int);
	pGDIWorkspace = (unsigned char *)Memory_Alloc(WorkspaceSize);

	// Now lock buffer
	Memory_Clear(&Surface,sizeof(DDSURFACEDESC));
	Surface.dwSize = sizeof(DDSURFACEDESC);

	// All OK? If not need to jump back to a window
	pBackSurface->Lock(0,&Surface,DDLOCK_WAIT,0);

	// Copy screen in our buffer(convert RGB format if needed)
	if (DisplayModesDetails[CurrentDisplayMode].BitDepth==16) {
		DSurface_CopyScreen_16Bit(DisplayModesDetails[CurrentDisplayMode].Width,DisplayModesDetails[CurrentDisplayMode].Height,Surface.lPitch
			,(unsigned short int *)pGDIWorkspace,(unsigned char *)Surface.lpSurface);
	}
	else {
		DSurface_CopyScreen_8Bit(DisplayModesDetails[CurrentDisplayMode].Width,DisplayModesDetails[CurrentDisplayMode].Height,Surface.lPitch
			,(unsigned char *)pGDIWorkspace,(unsigned char *)Surface.lpSurface);
		// Copy our palette into our source bitmap(will also need to set in DC), other entries are assigned to the system palette
		for(i=10; i<10+16; i++) {
			GDIBitmap.Colours[i].rgbRed = LogPalette.Entries[i].peRed;
			GDIBitmap.Colours[i].rgbGreen = LogPalette.Entries[i].peGreen;
			GDIBitmap.Colours[i].rgbBlue = LogPalette.Entries[i].peBlue;
		}

		// Need to create usual Windows palette, using DC
		GDIPalette = CreatePalette((LOGPALETTE *)&LogPalette);
		SelectPalette(MainDC,GDIPalette,FALSE);
		RealizePalette(MainDC);
	}

	// Unlock
	pBackSurface->Unlock(0);

	// Set details of the GDI bitmap ready for blitting(assigns 8-bit or 16-bit)
	Screen_SetBMPHeaders(&GDIBitmap,DisplayModesDetails[CurrentDisplayMode].Width,DisplayModesDetails[CurrentDisplayMode].Height,DisplayModesDetails[CurrentDisplayMode].BitDepth);

	return(TRUE);
}

//-----------------------------------------------------------------------
/*
	Free the GDI surface bitmap
*/
void DSurface_FreeGDIScreen(void)
{
	// Free old screen if have one
	if (pGDIWorkspace) {
		// Set full update so sure we do not to 'glitch' when release
		Screen_SetFullUpdate();

		if (GDIPalette)
			DeleteObject(GDIPalette);
		GDIPalette = NULL;
		Memory_Free(pGDIWorkspace);
		pGDIWorkspace = NULL;
	}
}

//-----------------------------------------------------------------------
/*
	Blit the the GDI surface to the screen to repair
*/
void DSurface_BlitGDIScreen(void)
{
	// Blit image back to screen(offset as screen DC now has menu)
	StretchDIBits(MainDC,0,-GetSystemMetrics(SM_CYMENU),GDIBitmap.InfoHeader.biWidth,-GDIBitmap.InfoHeader.biHeight,0,0,GDIBitmap.InfoHeader.biWidth,-GDIBitmap.InfoHeader.biHeight,pGDIWorkspace,(BITMAPINFO *)&GDIBitmap.InfoHeader,DIB_RGB_COLORS,SRCCOPY);
	// And update menu
	View_DrawMenu();
}

//-----------------------------------------------------------------------
/*
	Flip to GDI surface for showing Windows dialog etc... while in full-screen
	If users are running in lower than 640x400, or the call fails, this will return FALSE
*/
BOOL DSurface_FlipToGDI(void)
{
	// Stop it
	WinSTon_PauseEmulation();							// Hold things...

	// Don't allow full-screen menu if running in lower than 640x400
	if (DSurface_IsHighResolutionMode()) {
		if (DSurface_CreateGDIScreen()) {				// Copy back buffer contents to workspace for re-draw
			if (pDDraw->FlipToGDISurface()==DD_OK) {	// Go back to screen that has GDI access
				bFullScreenHold = TRUE;					// Hold screen while in this mode(to prevent any more flips)
				View_SetFullScreenMenu();				// Bring up menu
				View_ToggleWindowsMouse(MOUSE_WINDOWS);
				View_Update();							// Update screen, and show menu
			}

			return(TRUE);
		}
	}

	// Failed to set, return to Windows
	Screen_ReturnFromFullScreen();

	return(FALSE);
}
