/*
Copyright (c) 1998 Richard Lawrence

This program is free software; you can redistribute it and/or modify it under the terms 
of the GNU General Public License as published by the Free Software Foundation; either 
version 2 of the License, or (at your option) any later version. This program is 
distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details. You should have received a copy of the GNU
General Public License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <stdio.h>
#include <fcntl.h>
#include <windows.h>
#include <crtdbg.h>
#include <ddraw.h>
#include <mmsystem.h>
#include "atari.h"
#include "monitor.h"
#include "platform.h"
#include "winatari.h"
#include "registry.h"
#include "graphics.h"
#include "resource.h"
#include "diskled.h"
#include "log.h"

#define WIN_INLINE_ASM 1

/* This doesn't buy us anything. It's actually slower than blitting directly from system
   memory to the primary buffer (which is how we have to render because the Atari screen
   is drawn pixel by pixel). */
/* #define USE_FLIP_BUFFER 1 */

TCHAR	gcErrorString[ LOADSTRING_STRING_SIZE ];

extern void Restart_Sound( void );
extern void WriteRegDWORD( HKEY hkInput, char *item, DWORD value);
extern PALETTEENTRY	pe[];
extern ULONG ulAtariState;
/* extern int Sound_Initialise( void ); */
extern void Clear_Sound( BOOL bPermanent ); 
extern ULONG *atari_screen;
extern TVmode tv_mode;
extern UBYTE memory[65536];
extern int mach_xlxe;
extern UBYTE atarixl_os[];
//extern RECT	rcDesktop;

extern unsigned int	darkline_translate[256];

void Screen_Clear( void );
int	nStatusSize = 18;
static LPDIRECTDRAW				lpDirectDrawObject = NULL;
/* Don't use SURFACE3, it doesn't work with NT 4.0 */
static LPDIRECTDRAWSURFACE		lpPrimary = NULL;
static LPDIRECTDRAWSURFACE		lpMemory = NULL;
static unsigned char			*lpSurface = NULL;
#ifdef USE_FLIP_BUFFER
static LPDIRECTDRAWSURFACE		lpFrontBuff = NULL;
static LPDIRECTDRAWSURFACE		lpBackBuff = NULL;
#endif

static BOOL GetDDErrorString (HRESULT hResult, LPTSTR lpszErrorBuff, DWORD cchError);
static RECT	rcDest, rcSource, rcSrcClip;
static unsigned long ulLEDOffset = 0;
LONG	origWndStyle = 0;
unsigned long	ulScreenMode = DISPLAY_MODE_DEFAULT;
int		nStartX = 0, nStartY = 0;

LPBITMAPINFO	lpbmi = NULL;		/* bitmap info for our screen bitmap */

//unsigned long	screenbuff[ (ATARI_WIDTH * (ATARI_HEIGHT+16)) / sizeof( unsigned long) + 1 ];
unsigned char *screenbuff = NULL;
static unsigned char	*screentemp = NULL;

HWND	MainhWnd = NULL, hWnd = NULL;
HDC		h_screenDC = NULL;

unsigned	long	ulModesAvail = 0;

static unsigned long ulLastMode = 0;
static void Screen_DDraw( UBYTE *screen );
static void Screen_DDraw_Stretch( UBYTE *screen );
static void Screen_DDraw_Stretch_Scanlines( UBYTE *screen );
static void Screen_DDraw_Full_Blt( UBYTE *screen );
static void Screen_DDraw_Full_Blt_Scanlines( UBYTE *screen );
static void Screen_DDraw_1024_Scanlines( UBYTE *screen );
#ifdef USE_FLIP_BUFFER
static void Screen_DDraw_Full_FlipBuff( UBYTE *screen );
#endif
static void Screen_GDI_Stretch( UBYTE *screen );
static void Screen_GDI_Stretch_Scanlines( UBYTE *screen );
static void Screen_GDI( UBYTE *screen );

static DWORD	dwMyBltFlags = DDBLT_ASYNC;

static unsigned char ascii_to_screen[128] =
{
	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
};

void ComputeClipArea( void )
{
	RECT	rcWind, rcDesktop;
	int nDivisor;

	if( ulScreenMode & DDRAW_NONE || ulScreenMode & DDRAW_FULL )
		return;	/* Don't need to worry about this for GDI screens */

	/* The following section covers clip behavoir for DDraw windowed modes (important, because the primary
	   surface is the desktop). These are computed once here and then every time a WM_MOVE is received in
	   Atari800WinView. I never had much luck with ClipObjects... */
	SystemParametersInfo( SPI_GETWORKAREA, 0, (LPVOID)&rcDesktop, 0 );
	GetWindowRect( hWnd, &rcWind );
	rcWind.left += GetSystemMetrics( SM_CXEDGE );
	rcWind.top += GetSystemMetrics( SM_CYEDGE );

	if( ulScreenMode & WINDOWED_STRETCH )
		nDivisor = 2;
	else
		nDivisor = 1;

	memcpy( &rcSrcClip, &rcSource, sizeof( RECT ) );

	rcDest.left = rcWind.left;
	if( rcDest.left < rcDesktop.left )
	{
		rcSrcClip.left = (rcDesktop.left - rcDest.left) / nDivisor;
		rcDest.left = rcDesktop.left;
		rcDest.right = rcDest.left + ATARI_VIS_WIDTH * nDivisor - rcSrcClip.left * nDivisor;
	}
	else
	{
		rcDest.right = rcDest.left + ATARI_VIS_WIDTH * nDivisor;
		if( rcDest.right > rcDesktop.right )
		{
			rcSrcClip.right -= (rcDest.right - rcDesktop.right) / nDivisor;
			rcDest.right = rcDesktop.right;
		}
	}

	rcDest.top = rcWind.top - rcDesktop.top;
	if( rcDest.top < 0 )
	{
		rcSrcClip.top = (rcDesktop.top - rcDest.top) / nDivisor;
		rcDest.top = rcDesktop.top;
		rcDest.bottom = rcDest.top + ATARI_HEIGHT * nDivisor - rcSrcClip.top * nDivisor;
	}
	else
	{
		rcDest.bottom = rcDest.top + ATARI_HEIGHT * nDivisor;
		if( rcDest.bottom > rcDesktop.bottom )
		{   /* Sigh..scanline mode is different, since it is already stretched */
			if( nDivisor == 2 && ulScreenMode & SCANLINE_CAPABLE && ulScreenMode & SHOW_SCANLINES ) 
				rcSrcClip.bottom -= (rcDest.bottom - rcDesktop.bottom);
			else
				rcSrcClip.bottom -= (rcDest.bottom - rcDesktop.bottom) / nDivisor;
			rcDest.bottom = rcDesktop.bottom ;
		}
	}
	/* Negative because we are a top-down bitmap. Note that it is CORRECT to only adjust height
	   of the src bitmap. This is to prevent windows from blitting the wrong vertical portion 
	   (from the bottom up) if the bmiHeader.biHeight doesn't match the source rectangle. However
	   you always want width the FULL width, otherwise a blit will assume the memory area described
	   has shorter rows and will unevenly blit. Confused? good, we're not finished. In DDRAW
	   windowed modes the SRC rectangle will usually be 1X Atari size, because we stretch it to 
	   memory first then just directly BLT. The exception is "scan-line" modes, which have to be
	   2X Atari Height to accomodate the extra information (or lack thereof, more appropriately) */
	if( lpbmi )
	{
		if( nDivisor == 1 )
		{
			lpbmi->bmiHeader.biHeight = -(rcSrcClip.bottom - rcSrcClip.top);
		}
	}
}

void Atari_Set_Disk_LED( int unit, int state )
{
	ulDiskUnit = unit;
	ulDiskState = state;
}

void Draw_DiskLED( UBYTE *dest, unsigned int uiPitch )
{
	_ASSERT( ulDiskUnit <= LED_NO_UNIT );
	if( ulDiskUnit <= LED_NO_UNIT )
	{
		int	i = DISKLED_FONT_HEIGHT;
		unsigned char *src = &DiskLED[ ulDiskUnit * DISKLED_FONT_CHARSIZE ]; 
		if( ulDiskState == LED_WRITE )
			src += 9 * DISKLED_FONT_CHARSIZE;
		while( i-- )
		{
			*dest++ = *src++;
			*dest++ = *src++;
			*dest++ = *src++;
			*dest++ = *src++;
			*dest++ = *src++;
			dest+= uiPitch - DISKLED_FONT_WIDTH;
		}
		ulDiskUnit = LED_NO_UNIT;
		ulDiskState = 0;
	}
}

static HRESULT WINAPI EnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext)
{
	_ASSERT( lpDDSurfaceDesc );
	if( lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount == 8 )
	{
		if( lpDDSurfaceDesc->dwHeight == 768 && lpDDSurfaceDesc->dwWidth == 1024 )
			ulModesAvail |= MODE_1024_768;
		if( lpDDSurfaceDesc->dwHeight == 600 && lpDDSurfaceDesc->dwWidth == 800 )
			ulModesAvail |= MODE_800_600;
		if( lpDDSurfaceDesc->dwHeight == 480 && lpDDSurfaceDesc->dwWidth == 640 )
			ulModesAvail |= MODE_640_480;
		if( lpDDSurfaceDesc->dwHeight == 400 && lpDDSurfaceDesc->dwWidth == 640 )
			ulModesAvail |= MODE_640_400;
		if( lpDDSurfaceDesc->dwHeight == 384 && lpDDSurfaceDesc->dwWidth == 512 )
			ulModesAvail |= MODE_512_384;
		if( lpDDSurfaceDesc->dwHeight == 240 && lpDDSurfaceDesc->dwWidth == 320 )
			ulModesAvail |= MODE_320_240;
		if( lpDDSurfaceDesc->dwHeight == 200 && lpDDSurfaceDesc->dwWidth == 320 )
			ulModesAvail |= MODE_320_200;
	}
	return DDENUMRET_OK;
}

void	CheckDDrawModes( void )
{
	if( !lpDirectDrawObject )
	{
		/* This is for IDirectDraw2 objects (don't work with some cards, so much for compatibility */
/*		LPDIRECTDRAW lpDDT;
		if( SUCCEEDED( DirectDrawCreate(NULL,&lpDDT,0)))
		{
			if( SUCCEEDED( IDirectDraw_QueryInterface( lpDDT, &IID_IDirectDraw2, (void**)&lpDirectDrawObject)) )
			{
				IDirectDraw_Release( lpDDT );
				IDirectDraw_EnumDisplayModes( lpDirectDrawObject, 0, NULL, NULL, EnumModesCallback);
				IDirectDraw_Release( lpDirectDrawObject );
			}
			else
				IDirectDraw_Release( lpDDT );
			lpDirectDrawObject = NULL;
		}*/
		if( SUCCEEDED( DirectDrawCreate( NULL, &lpDirectDrawObject, 0 ) ) )
		{
			IDirectDraw_EnumDisplayModes( lpDirectDrawObject, 0, NULL, NULL, EnumModesCallback );
			IDirectDraw_Release( lpDirectDrawObject );
		}
		lpDirectDrawObject = NULL;
		return;
	}
	IDirectDraw_EnumDisplayModes( lpDirectDrawObject, 0, NULL, NULL, EnumModesCallback);
}

HRESULT ReleaseAllSurfaces( BOOL bReleaseObj )
{
	HRESULT	hResult, hFailure;
	hFailure = hResult = DD_OK;

	if( lpMemory )
	{
		hResult = IDirectDrawSurface_Release( lpMemory );
		if( FAILED( hResult ) )
			hFailure = hResult;
		lpSurface = (unsigned char *)lpMemory = NULL;
	}

#ifdef USE_FLIP_BUFFER
	if( lpFrontBuff )
	{
		hResult = IDirectDrawSurface_Release( lpFrontBuff );
		if( FAILED( hResult ) )
			hFailure = hResult;
		lpFrontBuff = lpBackBuff = NULL;
	}
#endif

	if( lpPrimary )
	{
		hResult = IDirectDrawSurface_Release( lpPrimary );
		if( FAILED( hResult ) )
			hFailure = hResult;
		lpPrimary = NULL;
	}

	if( bReleaseObj && lpDirectDrawObject )
	{
		IDirectDraw_RestoreDisplayMode( lpDirectDrawObject );
		hResult = IDirectDraw_Release( lpDirectDrawObject );
		if( FAILED( hResult ) )
			hFailure = hResult;
		lpDirectDrawObject = NULL;
	}

	return hFailure;
}

void SetSafeDisplay( )
{
	HRESULT	hResult;

	atari_screen = (ULONG *)screenbuff;

	ulLEDOffset = (ATARI_HEIGHT - DISKLED_FONT_HEIGHT) * ATARI_VIS_WIDTH + ATARI_VIS_WIDTH - DISKLED_FONT_WIDTH;
	if( ulScreenMode & WINDOWED_STRETCH )
	{
		Atari_DisplayScreen = Screen_GDI_Stretch;
		if( ulScreenMode & SHOW_SCANLINES )
			Atari_DisplayScreen = Screen_GDI_Stretch_Scanlines;
	}
	else
		Atari_DisplayScreen = Screen_GDI;

	hResult = ReleaseAllSurfaces( TRUE );

	if( MainhWnd && origWndStyle )
		SetWindowLong( MainhWnd, GWL_STYLE, origWndStyle );

	if( FAILED( hResult ) )
	{
		LoadString( NULL, IDS_DDRAW_ERROR_RELEASE_ALL, gcErrorString, LOADSTRING_STRING_SIZE );
		MessageBox( MainhWnd, gcErrorString, "Atari800Win", MB_OK );
	}

	if( !(ulAtariState & ATARI_CLOSING) )
	{
		SetWindowPos( MainhWnd, HWND_NOTOPMOST, nStartX, nStartY, 
			ATARI_VIS_WIDTH + GetSystemMetrics( SM_CXDLGFRAME )*2 + GetSystemMetrics( SM_CXEDGE )*2,
			ATARI_HEIGHT + GetSystemMetrics( SM_CYMENU)+ nStatusSize + GetSystemMetrics( SM_CYDLGFRAME )*2 +	
			GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYEDGE ) * 2, SWP_FRAMECHANGED | SWP_SHOWWINDOW );
	}

	if( ulAtariState & ATARI_CLOSING )
	{
		if( screentemp )
			free( screentemp );
		screentemp = NULL;

		if( screenbuff )
			free( screenbuff );
		screenbuff = NULL;
	}

	return;
}

static void ShowDDrawError( UINT nUID, HRESULT hResult, BOOL quit )
{
	TCHAR	error[256];
	TCHAR	fullstring[LOADSTRING_STRING_SIZE];
	TCHAR	action[LOADSTRING_STRING_SIZE];
	int		result;

	SetSafeDisplay();

	Clear_Sound( FALSE );

	ulScreenMode |= DDRAW_640_480;
	ulScreenMode |= DDRAW_NONE;
	WriteRegDWORD( NULL, REG_DDRAW_MODE, ulScreenMode);

	GetDDErrorString( hResult, error, 256 );
	LoadString( NULL, IDS_DDRAW_ERROR_PROMPT, gcErrorString, LOADSTRING_STRING_SIZE );
	LoadString( NULL, nUID, action, LOADSTRING_STRING_SIZE );
	wsprintf( fullstring, gcErrorString, action, error );
	LoadString( NULL, IDS_DDRAW_ERROR_HDR, gcErrorString, LOADSTRING_STRING_SIZE );
	result = MessageBox( hWnd, fullstring, gcErrorString, MB_YESNO );

	if( result == IDYES )
	{
		Clear_Sound( TRUE );
		ulAtariState = ATARI_UNINITIALIZED | ATARI_PAUSED;
		if( quit )
			PostMessage( MainhWnd, WM_CLOSE, 0, 0L );
	}
	Restart_Sound( );
	return;
}

static int CreateDirectDrawObject( void ) 
{
	/* Get the object if not already allocated */
	if( !lpDirectDrawObject )
	{
		HRESULT hResult;

		/*LPDIRECTDRAW lpDDT; 
		if( SUCCEEDED( DirectDrawCreate(NULL,&lpDDT,0)))
		{
			if( SUCCEEDED( IDirectDraw_QueryInterface( lpDDT, &IID_IDirectDraw2, (void**)&lpDirectDrawObject)) )
				IDirectDraw_Release( lpDDT );
		}*/

		hResult = DirectDrawCreate( NULL, &lpDirectDrawObject, 0 );
		if( FAILED( hResult ) )
		{
			ShowDDrawError( IDS_DDERR_ALLOC_OBJ, hResult, FALSE );
			ulAtariState = ATARI_UNINITIALIZED | ATARI_PAUSED;
			return 0;
		}
	}
	return 1;
}

static int Init_Windowed_Mode( void )
{
	if( MainhWnd && origWndStyle )
		SetWindowLong( MainhWnd, GWL_STYLE, origWndStyle );

	if( lpDirectDrawObject )
		IDirectDraw_RestoreDisplayMode( lpDirectDrawObject );

	ReleaseAllSurfaces( FALSE );

	atari_screen = (ULONG *)screenbuff;

	if( ulScreenMode & DDRAW_NONE ) /* GDI Windowed modes */
	{
		ReleaseAllSurfaces( TRUE );

		if( ulScreenMode & WINDOWED_STRETCH )
		{
			lpbmi->bmiHeader.biWidth = ATARI_STRETCH_VIS_WIDTH;
			lpbmi->bmiHeader.biHeight = -ATARI_STRETCH_HEIGHT;	/* Negative because we are a top-down bitmap */
		}
	}

	/* If we are recovering from a DirectDraw mode, the window frame will be whatever
	   size that mode was instead of what we want it to be, so set window size here */
	if( MainhWnd )
	{
		RECT	rcPos;
		int		height, width;

		GetWindowRect( MainhWnd, &rcPos );
		if( ((rcPos.left != 0) && (rcPos.left!=nStartX)) || ((rcPos.top!=0)&&(rcPos.top!=nStartY)))
		{
			nStartX = rcPos.left;
			nStartY = rcPos.top;
		}
		if( ulScreenMode & WINDOWED_NORMAL )
		{
			height = ATARI_HEIGHT;
			width = ATARI_VIS_WIDTH;
		}
		else
		{
			height = ATARI_STRETCH_HEIGHT;
			width = ATARI_STRETCH_VIS_WIDTH;
		}
		SetWindowPos( MainhWnd, HWND_NOTOPMOST, nStartX, nStartY, 
			width + GetSystemMetrics( SM_CXDLGFRAME )*2 + GetSystemMetrics( SM_CXEDGE )*2,
			height + GetSystemMetrics( SM_CYMENU) + nStatusSize + GetSystemMetrics( SM_CYDLGFRAME )*2 +	
			GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYEDGE ) * 2, SWP_SHOWWINDOW );
	}
	
	
	/* Specifics for non-DDraw windowed modes */
	if( ulScreenMode & DDRAW_NONE )
	{
		/* Sound_Initialise( ); */

		if( ulScreenMode & WINDOWED_STRETCH )
		{
			Atari_DisplayScreen = Screen_GDI_Stretch;
			if( ulScreenMode & SHOW_SCANLINES )
				Atari_DisplayScreen = Screen_GDI_Stretch_Scanlines;
		}
		else
			Atari_DisplayScreen = Screen_GDI;
		return 1;
	}
	else
	{
		/* DDraw Windowed modes */
		HRESULT	hResult;

		if( !CreateDirectDrawObject() )
			return 0;

		if( origWndStyle && MainhWnd)
			SetWindowLong( MainhWnd, GWL_STYLE, origWndStyle );

		_ASSERT( !lpPrimary && !lpMemory );
		_ASSERT( lpDirectDrawObject != NULL );
		hResult = IDirectDraw_SetCooperativeLevel(lpDirectDrawObject, NULL, DDSCL_NORMAL);
		if( FAILED( hResult ) )
		{
			ShowDDrawError( IDS_DDERR_SET_MODE, hResult, FALSE );
			return 0;
		}

		if( ulScreenMode & WINDOWED_NORMAL )
			Atari_DisplayScreen = Screen_DDraw;
		else
		{
			Atari_DisplayScreen = Screen_DDraw_Stretch;
			if( ulScreenMode & SHOW_SCANLINES )
			{
				Atari_DisplayScreen = Screen_DDraw_Stretch_Scanlines;
				rcSource.bottom = ATARI_STRETCH_HEIGHT;
				lpbmi->bmiHeader.biHeight = -ATARI_STRETCH_HEIGHT;
			}
		}
	}

	ulAtariState |= ATARI_HW_READY | ATARI_WINDOWS_READY;

	ComputeClipArea( );

	return 1;
}

int SetupPrimarySurface( void )
{
/*	LPDIRECTDRAWSURFACE lpDDSTemp;*/
	DDSURFACEDESC	ddsd;
	HRESULT hResult;

	ZeroMemory( &ddsd, sizeof( ddsd ) );
	ddsd.dwSize         = sizeof(ddsd);
	ddsd.dwFlags        = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

	_ASSERT( !lpPrimary );

	ReleaseAllSurfaces( FALSE );

	hResult = IDirectDraw_CreateSurface( lpDirectDrawObject, &ddsd, &lpPrimary, NULL);
	if( FAILED( hResult ) )
	{
		lpPrimary = NULL;
		ShowDDrawError( IDS_DDERR_ALLOC_PRIMARY, hResult, FALSE );
		return 0;
	}

/*	hResult = IDirectDrawSurface_QueryInterface( lpDDSTemp, &IID_IDirectDrawSurface2, (void**)&lpPrimary);
	IDirectDrawSurface_Release( lpDDSTemp );
	if( FAILED( hResult ) )
	{
		lpPrimary = NULL;
		ShowDDrawError( IDS_DDERR_ALLOC_PRIMARY, hResult, FALSE );
		return 0;
	}*/

	return 1;
}

static int SetupMemorySurface( )
{
/*	LPDIRECTDRAWSURFACE lpDDSTemp;	 */
	DDSURFACEDESC	ddsd;
	HRESULT hResult;

	_ASSERT( !lpMemory );

	ZeroMemory( &ddsd, sizeof( ddsd ) );
	ddsd.dwSize = sizeof( ddsd );
	if( ulScreenMode & DDRAW_FULL )
	{
		ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
		ddsd.ddpfPixelFormat.dwSize = sizeof( DDPIXELFORMAT );
		ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_PALETTEINDEXED8;
		ddsd.ddpfPixelFormat.dwRGBBitCount = 8;
	}
	else
		ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;

	ddsd.dwHeight = (ATARI_HEIGHT + 16) * 2;
	if( ulScreenMode & DDRAW_FULL && ulScreenMode & SCANLINE_CAPABLE && ulScreenMode & SHOW_SCANLINES )
		ddsd.dwHeight = (ATARI_HEIGHT + 16) * 3;
	ddsd.dwWidth = ATARI_WIDTH;
	
	if( ulScreenMode & DDRAW_USE_LOCALMEM )
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
	else
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;

	hResult = IDirectDraw_CreateSurface( lpDirectDrawObject, &ddsd, &lpMemory, NULL);
	if( FAILED( hResult ) )
	{
		Aprint( "Video memory allocation failed, trying system memory..." );
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;

		hResult = IDirectDraw_CreateSurface( lpDirectDrawObject, &ddsd, &lpMemory, NULL);
		if( FAILED( hResult ) )
		{
			Aprint( "Could not allocate system memory surface in any form!" );
			lpSurface = (unsigned char *)lpMemory = NULL;
			ShowDDrawError( IDS_DDERR_ALLOC_SYSMEM_SURFACE, hResult, FALSE );
			return 0;
		}
	}

	if( ulScreenMode & DDRAW_FULL )
	{
		ZeroMemory( &ddsd, sizeof( ddsd ) );
		ddsd.dwSize = sizeof( ddsd );
		IDirectDrawSurface_Lock( lpMemory, NULL, &ddsd, 0, NULL );
		atari_screen = (unsigned long *)ddsd.lpSurface;
		lpSurface = (unsigned char *)ddsd.lpSurface;
		ZeroMemory( ddsd.lpSurface, ddsd.dwWidth * ddsd.dwHeight );
		IDirectDrawSurface_Unlock( lpMemory, &ddsd );

		if( ulScreenMode & SHOW_SCANLINES && ulScreenMode & SCANLINE_CAPABLE )
			atari_screen = (ULONG *)screenbuff;
	}
	else
		atari_screen = (ULONG *)screenbuff;

/*	hResult = IDirectDrawSurface_QueryInterface( lpDDSTemp, &IID_IDirectDrawSurface2, (void**)&lpMemory);
	IDirectDrawSurface_Release( lpDDSTemp );
	if( FAILED( hResult ) )
	{
		lpMemory = NULL;
		ShowDDrawError( IDS_DDERR_ALLOC_SYSMEM_SURFACE, hResult, FALSE );
		return 0;
	}*/
	return 1;
}

#ifdef USE_FLIP_BUFFER
int SetupFlipBuffer( void )
{
/*	LPDIRECTDRAWSURFACE lpDDSTemp;	*/
	DDSURFACEDESC	ddsd;
	HRESULT hResult;

	ZeroMemory( &ddsd, sizeof( ddsd ) );
	ddsd.dwSize = sizeof( ddsd );
	ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
	ddsd.dwBackBufferCount = 1;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY;

	hResult = IDirectDraw_CreateSurface( lpDirectDrawObject, &ddsd, &lpFrontBuff, NULL);
	if( FAILED( hResult ) )
	{
		IDirectDrawSurface_Release( lpFrontBuff );
		lpFrontBuff = NULL;
		ShowDDrawError( IDS_DDERR_ALLOC_PRIMARY, hResult, FALSE );
		return 0;
	}
/*	hResult = IDirectDrawSurface_QueryInterface( lpDDSTemp, &IID_IDirectDrawSurface2, (void**)&lpFrontBuff);
	IDirectDrawSurface_Release( lpDDSTemp );
	if( FAILED( hResult ) )
	{
		ShowDDrawError( IDS_DDERR_ALLOC_PRIMARY, hResult, FALSE );
		return 0;
	} */
	else
	{
		DDSCAPS caps;
		ZeroMemory( &caps, sizeof( caps ) );
		caps.dwCaps = DDSCAPS_BACKBUFFER;
		hResult = IDirectDrawSurface_GetAttachedSurface( lpFrontBuff, &caps, &lpBackBuff);
		if( FAILED( hResult ) )
		{
			ShowDDrawError( IDS_DDERR_ALLOC_PRIMARY, hResult, FALSE );
			IDirectDrawSurface_Release( lpFrontBuff );
			lpFrontBuff = NULL;
			return 0;
		}
	}

	return 1;
}
#endif

void Screen_Clear( void )
{
	if( screenbuff )
		memset( screenbuff, 0, (ATARI_WIDTH * (ATARI_HEIGHT+16)) );

	if( screentemp )
		memset( screentemp, 0, ATARI_STRETCH_VIS_SCREEN_SIZE );

	if( lpMemory )
	{
		DDSURFACEDESC	ddsd;
		HRESULT			hResult;
		ZeroMemory( &ddsd, sizeof( ddsd ) );
		ddsd.dwSize = sizeof( ddsd );

		hResult = IDirectDrawSurface_Lock( lpMemory, NULL, &ddsd, 0, NULL );
		if( SUCCEEDED( hResult )) 
		{
			ZeroMemory( ddsd.lpSurface, ddsd.dwHeight * ddsd.dwWidth );
			IDirectDrawSurface_Unlock( lpMemory, &ddsd );
		}
	}

	if( lpPrimary && ulScreenMode & DDRAW_FULL )
	{
		DDSURFACEDESC	ddsd;
		HRESULT			hResult;

		ZeroMemory( &ddsd, sizeof( ddsd ) );
		ddsd.dwSize = sizeof( ddsd );
		hResult = IDirectDrawSurface_Lock( lpPrimary, NULL, &ddsd, 0, NULL );
		if( SUCCEEDED( hResult )) 
		{
			RECT			rcTemp;
			DDBLTFX			ddbltfx;

			rcTemp.top = GetSystemMetrics( SM_CYMENU );
			rcTemp.left = 0;
			rcTemp.right = ddsd.dwWidth;
			rcTemp.bottom = ddsd.dwHeight - rcTemp.top;
			IDirectDrawSurface_Unlock( lpPrimary, &ddsd );

			ZeroMemory( &ddbltfx, sizeof( ddbltfx ) );
			ddbltfx.dwSize = sizeof( ddbltfx );
			ddbltfx.dwFillColor = 0;

			IDirectDrawSurface_Blt( lpPrimary, &rcTemp, NULL, NULL, dwMyBltFlags | DDBLT_COLORFILL , &ddbltfx);
		}
	}
}

int Init_Graphics( void )
{
	LPDIRECTDRAWPALETTE	lpDDPalette;
	HRESULT	hResult;

	if( !screenbuff )
		screenbuff = malloc( ATARI_WIDTH * (ATARI_HEIGHT + 16) );

	if( !screentemp )
		screentemp = malloc( ATARI_STRETCH_VIS_SCREEN_SIZE + 1 );
	
	if( !screentemp || !screenbuff )
		return 0;

	Screen_Clear();

	if( ulLastMode == ulScreenMode )
		return 1;
	ulLastMode = ulScreenMode;

	ulLEDOffset = (ATARI_HEIGHT - DISKLED_FONT_HEIGHT) * ATARI_WIDTH + ATARI_WIDTH - ATARI_HORZ_CLIP - DISKLED_FONT_WIDTH;

	ulScreenMode &= ~(DDRAW_NO_MENU_MODE | SCANLINE_CAPABLE | SMALL_DIALOG_MODE);

	rcSource.left = 0;
	rcSource.top = 0;
	rcSource.right = ATARI_VIS_WIDTH;
	rcSource.bottom = ATARI_HEIGHT;

	lpbmi->bmiHeader.biWidth = ATARI_VIS_WIDTH;
	lpbmi->bmiHeader.biHeight = -ATARI_HEIGHT;	/* Negative because we are a top-down bitmap */
	
	atari_screen = (ULONG *)screenbuff;

	dwMyBltFlags = DDBLT_ASYNC;
	if( (ulScreenMode & DDRAW_DDBLT_WAIT)!=0 )
		dwMyBltFlags = DDBLT_WAIT;

	/* Sometimes when changing from window to full screen, DirectSound becomes very
	   confused, so stop it here and start it re-init it again when we have finished
	   the screen change */
	/* Clear_Sound( TRUE ); */

	if( !origWndStyle )
		origWndStyle = GetWindowLong( MainhWnd, GWL_STYLE );

	if( !(ulScreenMode & DDRAW_FULL) && (ulScreenMode & DDRAW_NONE) )
		ReleaseAllSurfaces( TRUE );
	else
		ReleaseAllSurfaces( FALSE );

	if( !(ulScreenMode & DDRAW_FULL) && ((ulScreenMode & DDRAW_WIND) || (ulScreenMode & DDRAW_NONE)) )
	{
		if( Init_Windowed_Mode()!=1 )
			return 0;

		if( ulScreenMode & DDRAW_NONE )	/* No more processing required for GDI modes */
			return 1;
	}

	/* Handle the various FULL (exclusive screen) modes */
	if( ulScreenMode & DDRAW_FULL )
	{
		int	iMenuSize = GetSystemMetrics( SM_CYMENU );

		if( !CreateDirectDrawObject() )
			return 0;

		if( iMenuSize & 0x01 )
			iMenuSize++;

		if( MainhWnd )
			SetWindowLong( MainhWnd, GWL_STYLE, WS_BORDER );


		hResult = IDirectDraw_SetCooperativeLevel(lpDirectDrawObject, MainhWnd, 
						DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT );

		if( FAILED( hResult ) )
		{
			ShowDDrawError( IDS_DDERR_SET_MODE, hResult, FALSE );
			return 0;
		}

		Atari_DisplayScreen = Screen_DDraw_Full_Blt;

		if( ulScreenMode & DDRAW_320_200 )
		{
			hResult = IDirectDraw_SetDisplayMode( lpDirectDrawObject, 320, 200, 8 );
			if( FAILED( hResult ) )
			{
				ShowDDrawError( IDS_DDERR_INIT_RES, hResult, FALSE );
				return 0;
			}
			rcDest.left = 0;
			rcDest.top = 0;
			rcDest.right = 320;
			rcDest.bottom = 200;
			rcSource.left = ATARI_HORZ_CLIP + ((ATARI_VIS_WIDTH - 320) >> 1);
			rcSource.right = ATARI_WIDTH - rcSource.left;
		    rcSource.top = (ATARI_HEIGHT - 200) >> 1;
			rcSource.bottom = ATARI_HEIGHT - rcSource.top;
			ulScreenMode |= SMALL_DIALOG_MODE | DDRAW_NO_MENU_MODE;
			ulLEDOffset = (rcSource.bottom - DISKLED_FONT_HEIGHT) * ATARI_WIDTH + rcSource.right - DISKLED_FONT_WIDTH;
		}

		if( ulScreenMode & DDRAW_320_240 )
		{
			hResult = IDirectDraw_SetDisplayMode( lpDirectDrawObject, 320, 240, 8 );
			if( FAILED( hResult ) )
			{
				ShowDDrawError( IDS_DDERR_INIT_RES, hResult, FALSE );
				return 0;
			}
			rcDest.left = 0;
			rcDest.top = 0;
			rcDest.right = 320;
			rcDest.bottom = 240;
			rcSource.left = ATARI_HORZ_CLIP + ((ATARI_VIS_WIDTH - 320) >> 1);
			rcSource.right = ATARI_WIDTH - rcSource.left;
		    rcSource.top = 0;
			rcSource.bottom = ATARI_HEIGHT;
			ulScreenMode |= SMALL_DIALOG_MODE | DDRAW_NO_MENU_MODE;
			ulLEDOffset = (rcSource.bottom - DISKLED_FONT_HEIGHT) * ATARI_WIDTH + rcSource.right - DISKLED_FONT_WIDTH;
		}

		if( ulScreenMode & DDRAW_512_384 )
		{

			hResult = IDirectDraw_SetDisplayMode( lpDirectDrawObject, 512, 384, 8 );
			//SleepEx( 300, TRUE );
			if( FAILED( hResult ) )
			{
				ShowDDrawError( IDS_DDERR_INIT_RES, hResult, FALSE );
				return 0;
			}
			rcDest.left = ( 512 - ATARI_VIS_WIDTH ) >> 1;
			rcDest.top =  ( 384 - ATARI_HEIGHT ) >> 1;
			rcDest.right = rcDest.left + ATARI_VIS_WIDTH;
			rcDest.bottom = rcDest.top + ATARI_HEIGHT;
			rcSource.left = ATARI_HORZ_CLIP;
			rcSource.right = ATARI_WIDTH - ATARI_HORZ_CLIP;
		}

		if( ulScreenMode & DDRAW_640_400 )
		{
			hResult = IDirectDraw_SetDisplayMode( lpDirectDrawObject, 640, 400, 8 );
			if( FAILED( hResult ) )
			{
				ShowDDrawError( IDS_DDERR_INIT_RES, hResult, FALSE );
				return 0;
			}
			rcDest.left = 0;
			rcDest.top = 0;
			rcDest.right = 640;
			rcDest.bottom = 400;
			rcSource.left = ATARI_HORZ_CLIP + ((ATARI_VIS_WIDTH - 320) >> 1);
			rcSource.right = ATARI_WIDTH - rcSource.left;
			rcSource.top = (ATARI_HEIGHT - 200) >> 1;
			rcSource.bottom = ATARI_HEIGHT - rcSource.top;
			ulScreenMode |= SCANLINE_CAPABLE | DDRAW_NO_MENU_MODE;
			ulLEDOffset = (rcSource.bottom - DISKLED_FONT_HEIGHT) * ATARI_WIDTH + rcSource.right - DISKLED_FONT_WIDTH;
			if( ulScreenMode & SHOW_SCANLINES )
			{
				rcSource.top = (ATARI_STRETCH_HEIGHT - 400) / 2;
				rcSource.bottom = ATARI_STRETCH_HEIGHT - rcSource.top;
				Atari_DisplayScreen = Screen_DDraw_Full_Blt_Scanlines;
			}
		}

		if( ulScreenMode & DDRAW_640_480 )
		{
			hResult = IDirectDraw_SetDisplayMode( lpDirectDrawObject, 640, 480, 8 );
			//SleepEx( 300, TRUE );
			if( FAILED( hResult ) )
			{
				ShowDDrawError( IDS_DDERR_INIT_RES, hResult, FALSE );
				return 0;
			}
			rcDest.left = 0;
			rcDest.top = 0;
			rcDest.right = 640;
			rcDest.bottom = 480;
			rcSource.left = ATARI_HORZ_CLIP + ((ATARI_VIS_WIDTH - 320) >> 1);
			rcSource.right = ATARI_WIDTH - rcSource.left;
			rcSource.top = 0;
			rcSource.bottom = ATARI_HEIGHT;
			ulScreenMode |= SCANLINE_CAPABLE | DDRAW_NO_MENU_MODE;
			ulLEDOffset = (rcSource.bottom - DISKLED_FONT_HEIGHT) * ATARI_WIDTH + rcSource.right - DISKLED_FONT_WIDTH;
			if( ulScreenMode & SHOW_SCANLINES )
			{
				rcSource.bottom = ATARI_STRETCH_HEIGHT;
				Atari_DisplayScreen = Screen_DDraw_Full_Blt_Scanlines;
			}
		}

		if( ulScreenMode & DDRAW_800_600 )
		{

			hResult = IDirectDraw_SetDisplayMode( lpDirectDrawObject, 800, 600, 8 );
			//SleepEx( 300, TRUE );
			if( FAILED( hResult ) )
			{
				ShowDDrawError( IDS_DDERR_INIT_RES, hResult, FALSE );
				return 0;
			}
			rcDest.left = ( 800 - ATARI_STRETCH_VIS_WIDTH ) / 2;
			rcDest.top =  ( 600 - ATARI_STRETCH_HEIGHT ) / 2;
			rcDest.right = rcDest.left + ATARI_STRETCH_VIS_WIDTH;
			rcDest.bottom = rcDest.top + ATARI_STRETCH_HEIGHT;
			rcSource.left = ATARI_HORZ_CLIP;
			rcSource.right = ATARI_WIDTH - ATARI_HORZ_CLIP;
			ulScreenMode |= SCANLINE_CAPABLE;
			if( ulScreenMode & SHOW_SCANLINES )
			{
				rcSource.bottom = ATARI_STRETCH_HEIGHT;
				Atari_DisplayScreen = Screen_DDraw_Full_Blt_Scanlines;
			}
		}

		if( ulScreenMode & DDRAW_1024_768 )
		{
			hResult = IDirectDraw_SetDisplayMode( lpDirectDrawObject, 1024, 768, 8 );
			//SleepEx( 300, TRUE );
			if( FAILED( hResult ) )
			{
				ShowDDrawError( IDS_DDERR_INIT_RES, hResult, FALSE );
				return 0;
			}
			rcDest.left = ( 1024 - (ATARI_VIS_WIDTH * 3 ) )>> 1;
			rcDest.top =  ( 768 - (ATARI_HEIGHT * 3 ) ) >> 1;
			rcDest.right = rcDest.left + (ATARI_VIS_WIDTH * 3);
			rcDest.bottom = rcDest.top + (ATARI_HEIGHT * 3);
			rcSource.left = ATARI_HORZ_CLIP;
			rcSource.right = ATARI_WIDTH - ATARI_HORZ_CLIP;
			ulScreenMode |= SCANLINE_CAPABLE;
			if( ulScreenMode & SHOW_SCANLINES )
			{
				rcSource.bottom = ATARI_HEIGHT * 3;
				Atari_DisplayScreen = Screen_DDraw_1024_Scanlines;
			}
		}
	} 

	/* Now that the cooperative mode is set, create a palette */
	hResult = IDirectDraw_CreatePalette( lpDirectDrawObject, DDPCAPS_8BIT | DDPCAPS_ALLOW256, pe, &lpDDPalette, NULL );
	if( FAILED( hResult ) )
	{
		ShowDDrawError( IDS_DDERR_ALLOC_PALETTE, hResult, FALSE );
		return 0;
	}

	if( !SetupPrimarySurface() )
		return 0;

	if( ulScreenMode & DDRAW_FULL )
	{
		hResult = IDirectDrawSurface_SetPalette( lpPrimary, lpDDPalette );
		if( FAILED( hResult ) )
		{
			ShowDDrawError( IDS_DDERR_SETPAL_PRIMARY, hResult, FALSE );
			return 0;
		}

#ifdef USE_FLIP_BUFFER
		if( !SetupFlipBuffer() )
			return 0;

		hResult = IDirectDrawSurface_SetPalette( lpFrontBuff, lpDDPalette );
		if( SUCCEEDED( hResult ) )
			Atari_DisplayScreen = Screen_DDraw_Full_FlipBuff;
#endif
	}

	if( ulScreenMode & DDRAW_FULL || (ulScreenMode & DDRAW_WIND && ulScreenMode & WINDOWED_STRETCH) )
	{
		/* Memory surface is only used for full screen, stretched window modes */
		if( !SetupMemorySurface( ) )
			return 0;
	}

	/* Don't actually want to set a palette for this if it's stretched window DDRAW, because
	   that's what we use this surface for - a colorspace conversion to whatever the primary
	   surface is via StretchDIB */
	if( ulScreenMode & DDRAW_FULL )
	{
		hResult = IDirectDrawSurface_SetPalette( lpMemory, lpDDPalette );
		if( FAILED( hResult ) )
		{
			ShowDDrawError( IDS_DDERR_SETPAL_MEMORY, hResult, FALSE );
			return 0;
		}
	}

	Screen_Clear();

	ulAtariState |= ATARI_HW_READY | ATARI_WINDOWS_READY;

	/* Sound_Initialise( ); */

	return 1;
}

static BOOL RestoreSurfaces( void )
{
	HRESULT hResult; 

	if( lpMemory )
	{
		if( FAILED( IDirectDrawSurface_IsLost( lpMemory ) ) )
		{
			hResult = IDirectDrawSurface_Restore( lpMemory );
			if( FAILED( hResult ) )
				return FALSE;
		}
	}

#ifdef USE_FLIP_BUFFER
	if( lpFrontBuff )
	{
		if( FAILED( IDirectDrawSurface_IsLost( lpFrontBuff ) ) )
		{
			hResult = IDirectDrawSurface_Restore( lpFrontBuff );
			if( FAILED( hResult ) )
				return FALSE;
		}
	}
#endif

	if( lpPrimary )
	{
		if( FAILED( IDirectDrawSurface_IsLost( lpPrimary ) ) ) 
		{
			hResult = IDirectDrawSurface_Restore( lpPrimary ) ;
			if( FAILED( hResult ) )
				return FALSE;
		}
	}
	return TRUE;
}

static void Screen_DDraw( UBYTE *screen )
{
	HDC	hdc;
	HRESULT hResult;

	if( ulAtariState & ATARI_PAUSED )
		return;

	if( ulDiskState )
		Draw_DiskLED( screenbuff + ulLEDOffset, ATARI_WIDTH );

#ifdef WIN_INLINE_ASM
	/* We have to copy the full size atari screen to another buffer to chop off the left and right
	   overflow (reason: allowing StretchDIB to cut the rectangle for us is VERY slow). It so happens
	   everything involved is DWORD aligned, and the offsets and such are constants, so I'm sticking
	   this asm routine in here...roughly equivalent C source code is below */
	_asm
	{
        mov     esi, dword ptr [screenbuff]		;; the source pointer (screenbuff + ATARI_HORZ_CLIP)
		add		esi, 018h
        mov     edi, dword ptr [screentemp]		;; the dest pointer
        mov     eax, 0f0h						;; number of lines (ATARI_HEIGHT)
scan_line:
        mov     ecx, 054h       ;; Our count of DWORDs to copy (ATARI_VIS_WIDTH/4)
        rep     movsd			;; Move that string
		add		esi, 030h		;; add in the offset to the next scan line for src

        dec     eax
        jnz     scan_line
	}
#else
	{
		unsigned char *dest, *src, *stop;

		stop = screentemp + ATARI_VIS_SCREEN_SIZE;
		dest = screentemp;
		src = screenbuff + ATARI_HORZ_CLIP;
		while( dest < stop )
		{
			memcpy( dest, src, ATARI_VIS_WIDTH );
			dest += ATARI_VIS_WIDTH;
			src += ATARI_WIDTH;
		}
	}
#endif
	hResult = IDirectDrawSurface_GetDC( lpPrimary, &hdc );
	if( SUCCEEDED( hResult ) )
	{
		StretchDIBits( hdc, rcDest.left, rcDest.top, rcDest.right - rcDest.left, rcDest.bottom - rcDest.top, 
			rcSrcClip.left, rcSrcClip.top, rcSrcClip.right - rcSrcClip.left, rcSrcClip.bottom - rcSrcClip.top, 
			screentemp, lpbmi, DIB_RGB_COLORS, SRCCOPY);
		IDirectDrawSurface_ReleaseDC( lpPrimary, hdc );
	}
	else
		ShowDDrawError( IDS_DDERR_SURFACE_LOCK, hResult, TRUE );
	return;
}

static void Screen_DDraw_Stretch( UBYTE *screen )
{
	HDC	hdc;
	HRESULT	hResult;

	if( ulDiskState )
		Draw_DiskLED( screenbuff + ulLEDOffset, ATARI_WIDTH );

#ifdef WIN_INLINE_ASM
	_asm
	{
        mov     esi, dword ptr [screenbuff]		;; the source pointer (screenbuff + ATARI_HORZ_CLIP)
		add		esi, 018h
        mov     edi, dword ptr [screentemp]		;; the dest pointer
        mov     eax, 0f0h						;; number of lines (ATARI_HEIGHT)
scan_line:
        mov     ecx, 054h       ;; Our count of DWORDs to copy (ATARI_VIS_WIDTH/4)
        rep     movsd			;; Move that string
		add		esi, 030h		;; add in the offset to the next scan line for src

        dec     eax
        jnz     scan_line
	}
#else
	{
		unsigned char *dest, *src, *stop;

		stop = screentemp + ATARI_VIS_SCREEN_SIZE;
		dest = screentemp;
		src = screenbuff + ATARI_HORZ_CLIP;
		while( dest < stop )
		{
			memcpy( dest, src, ATARI_VIS_WIDTH );
			dest += ATARI_VIS_WIDTH;
			src += ATARI_WIDTH;
		}
	}
#endif
	_ASSERT( lpMemory );
	hResult = IDirectDrawSurface_GetDC( lpMemory, &hdc );
	if( SUCCEEDED( hResult ) )
	{
		/* This StretchDIB basically does only the color space conversion to the memory surface */
		StretchDIBits( hdc, 0, 0, ATARI_VIS_WIDTH, ATARI_HEIGHT,
			0,  0, ATARI_VIS_WIDTH, ATARI_HEIGHT,
			screentemp, lpbmi, DIB_RGB_COLORS, SRCCOPY );
		IDirectDrawSurface_ReleaseDC( lpMemory, hdc );
	}
	else
		ShowDDrawError( IDS_DDERR_SURFACE_LOCK, hResult, TRUE );

	hResult = IDirectDrawSurface_Blt( lpPrimary, &rcDest, lpMemory, &rcSrcClip, dwMyBltFlags, NULL);
	if( SUCCEEDED( hResult ) )
		return;

	if( hResult == DDERR_WASSTILLDRAWING )
		return;

	ShowDDrawError( IDS_DDERR_PRIMARY_BLT, hResult, TRUE );
	return;
}

static void Screen_DDraw_Stretch_Scanlines( UBYTE *screen )
{
	HDC	hdc;
	HRESULT	hResult;

	if( ulDiskState )
		Draw_DiskLED( screenbuff + ulLEDOffset, ATARI_WIDTH );

#ifdef WIN_INLINE_ASM
	_asm
	{
        mov     esi, dword ptr [screenbuff]		;; the source pointer (screenbuff + ATARI_HORZ_CLIP)
		add		esi, 018h
        mov     edi, dword ptr [screentemp]		;; the dest pointer
        mov     eax, 0f0h						;; number of line pairs (ATARI_HEIGHT)
new_line:
        mov     ecx, 054h       ;; Our count of DWORDs to copy (ATARI_VIS_WIDTH/4)
        rep     movsd			;; Move that string

		sub		esi, ATARI_VIS_WIDTH			;; Move back to origin of original scan line
		mov		edx, ATARI_VIS_WIDTH			;; The pixel counter
dark_line:
		mov		bl, byte ptr [esi]				;; Make copy of original pixel
		mov		cl, bl							;; Another copy of original pixel
		and		bl, 0x0f						;; Bottom nybble of this pixel
		shr		bl, 1							;; nybble / 2
		and		cl, 0xf0						;; Top nybble of this pixel
		add		cl, bl							;; Make darkened pixel by adding nybbles
		inc		esi								;; move to next source pixel
		mov		byte ptr [edi], cl				;; Copy darkened pixel into destination point
		inc		edi
		dec		edx     						;; decrement our pixel counter
		jnz		dark_line						;; back to start of pixel operation

		add		esi, 030h						;; add in the offset to the next scan line for src
        dec     eax
        jnz     new_line
	}
#else
	{
		unsigned char *dest, *src, *stop;

		stop = screentemp + (ATARI_STRETCH_HEIGHT * ATARI_VIS_WIDTH);
		dest = screentemp;
		src = screenbuff + ATARI_HORZ_CLIP;
		while( dest < stop )
		{
			memcpy( dest, src, ATARI_VIS_WIDTH );
			dest += ATARI_VIS_WIDTH * 2;
			src += ATARI_WIDTH;
		}
	}
#endif
	_ASSERT( lpMemory );
	hResult = IDirectDrawSurface_GetDC( lpMemory, &hdc );
	if( SUCCEEDED( hResult ) )
	{
		/* This StretchDIB basically does only the color space conversion to the memory surface */
		StretchDIBits( hdc, 0, 0, ATARI_VIS_WIDTH, ATARI_STRETCH_HEIGHT,
			0,  0, ATARI_VIS_WIDTH, ATARI_STRETCH_HEIGHT,
			screentemp, lpbmi, DIB_RGB_COLORS, SRCCOPY );
		IDirectDrawSurface_ReleaseDC( lpMemory, hdc );
	}
	else
		ShowDDrawError( IDS_DDERR_SURFACE_LOCK, hResult, TRUE );

	hResult = IDirectDrawSurface_Blt( lpPrimary, &rcDest, lpMemory, &rcSrcClip, dwMyBltFlags, NULL);
	if( SUCCEEDED( hResult ) )
		return;

	if( hResult == DDERR_WASSTILLDRAWING )
		return;

	ShowDDrawError( IDS_DDERR_PRIMARY_BLT, hResult, TRUE );
	return;
}

#ifdef USE_FLIP_BUFFER
static void Screen_DDraw_Full_FlipBuff( UBYTE *screen )
{
	HRESULT	hResult;

	if( ulDiskState )
		Draw_DiskLED( screen + ulLEDOffset );

	IDirectDrawSurface_Blt( lpBackBuff, &rcDest, lpMemory, &rcSource, 0, NULL );

	//hResult = IDirectDrawSurface_Flip( lpFrontBuff, NULL, DDFLIP_WAIT );
	hResult = IDirectDrawSurface_Flip( lpFrontBuff, NULL, 0 );

	if( SUCCEEDED( hResult ) )
		return;

	if( hResult == DDERR_WASSTILLDRAWING )
		return;

    if( hResult == DDERR_SURFACELOST )
	{
		if( !RestoreSurfaces() )
			ShowDDrawError( IDS_DDERR_RESTORE_SURFACE, hResult, TRUE );
	}
	else 
		ShowDDrawError( IDS_DDERR_PRIMARY_BLT, hResult, TRUE );
}
#endif

static void Screen_DDraw_Full_Blt( UBYTE *screen )
{
	HRESULT	hResult;

	if( ulDiskState )
		Draw_DiskLED( screen + ulLEDOffset, ATARI_WIDTH );

	hResult = IDirectDrawSurface_Blt( lpPrimary, &rcDest, lpMemory, &rcSource, dwMyBltFlags, NULL);
	if( SUCCEEDED( hResult ) || hResult == DDERR_WASSTILLDRAWING)
		return;

    if( hResult == DDERR_SURFACELOST )
	{
		if( !RestoreSurfaces() )
			ShowDDrawError( IDS_DDERR_RESTORE_SURFACE, hResult, TRUE );
	}
	else
		ShowDDrawError( IDS_DDERR_PRIMARY_BLT, hResult, TRUE );
	return;
}

static void Screen_DDraw_Full_Blt_Scanlines( UBYTE *screen )
{
	HRESULT	hResult;

	if( ulDiskState )
		Draw_DiskLED( screen + ulLEDOffset, ATARI_WIDTH );

#ifdef WIN_INLINE_ASM
	_asm
	{
        mov     esi, dword ptr [screenbuff]		;; the source pointer 
        mov     edi, dword ptr [lpSurface]		;; the dest pointer
        mov     eax, ATARI_HEIGHT				;; number of line pairs (ATARI_HEIGHT)
new_line:
        mov     ecx, 060h						;; Our count of DWORDs to copy (ATARI_WIDTH/4)
        rep     movsd							;; Move that string

		sub		esi, ATARI_WIDTH				;; Move back to origin of original scan line
		mov		edx, ATARI_WIDTH				;; The pixel counter
dark_line:
		mov		bl, byte ptr [esi]				;; Make copy of original pixel
		mov		cl, bl							;; Another copy of original pixel
		and		bl, 0x0f						;; Bottom nybble of this pixel
		shr		bl, 1							;; nybble / 2
		and		cl, 0xf0						;; Top nybble of this pixel
		add		cl, bl							;; Make darkened pixel by adding nybbles
		inc		esi								;; move to next source pixel
		mov		byte ptr [edi], cl				;; Copy darkened pixel into destination point
		inc		edi
		dec		edx     						;; decrement our pixel counter
		jnz		dark_line						;; back to start of pixel operation

        dec     eax
        jnz     new_line
	}
#else
	{
		unsigned char *dest, *src, *stop;

		stop = lpSurface + (ATARI_STRETCH_HEIGHT * ATARI_WIDTH);
		dest = lpSurface + ATARI_HORZ_CLIP;
		src = screenbuff + ATARI_HORZ_CLIP;
		while( dest < stop )
		{
			memcpy( dest, src, ATARI_VIS_WIDTH );
			dest += ATARI_WIDTH * 2;
			src += ATARI_WIDTH;
		}
	}
#endif
	hResult = IDirectDrawSurface_Blt( lpPrimary, &rcDest, lpMemory, &rcSource, dwMyBltFlags, NULL);
	if( SUCCEEDED( hResult ) || hResult == DDERR_WASSTILLDRAWING)
		return;

    if( hResult == DDERR_SURFACELOST )
	{
		if( !RestoreSurfaces() )
			ShowDDrawError( IDS_DDERR_RESTORE_SURFACE, hResult, TRUE );
	}
	else
		ShowDDrawError( IDS_DDERR_PRIMARY_BLT, hResult, TRUE );
	return;
}

static void Screen_DDraw_1024_Scanlines( UBYTE *screen )
{
	HRESULT	hResult;

	if( ulDiskState )
		Draw_DiskLED( screen + ulLEDOffset, ATARI_WIDTH );

#ifdef WIN_INLINE_ASM
	_asm
	{
		;; Order in this routine has been manipulated to maximize pairing on a Pentium processor
        mov     esi, dword ptr [screenbuff]		;; the source pointer 
        mov     edi, dword ptr [lpSurface]		;; the dest pointer
        mov     eax, ATARI_HEIGHT				;; number of line pairs (ATARI_HEIGHT)
new_line:
        mov     ecx, 060h						;; Our count of DWORDs to copy (ATARI_WIDTH/4)
        rep     movsd							;; Move that string
		sub		esi, ATARI_WIDTH				;; Move back to origin of this scan line
		mov		ecx, 060h						;; Going to move another set of words...
		rep		movsd							;; Make another copy of scanline

		sub		esi, ATARI_WIDTH				;; Move back to origin of original scan line
		mov		edx, ATARI_WIDTH				;; The pixel counter
dark_line:
		mov		bl, byte ptr [esi]				;; Make copy of original pixel
		mov		cl, bl							;; Another copy of original pixel
		and		bl, 0x0f						;; Bottom nybble of this pixel
		shr		bl, 1							;; nybble / 2
		and		cl, 0xf0						;; Top nybble of this pixel
		add		cl, bl							;; Make darkened pixel by adding nybbles
		inc		esi								;; move to next source pixel
		mov		byte ptr [edi], cl				;; Copy darkened pixel into destination point
		inc		edi
		dec		edx     						;; decrement our pixel counter
		jnz		dark_line						;; back to start of pixel operation


        dec     eax								;; decrement our line counter
        jnz     new_line
	}
#else
	{
		unsigned char *dest, *src, *stop;

		stop = lpSurface + (ATARI_HEIGHT * 3 * ATARI_WIDTH);
		dest = lpSurface + ATARI_HORZ_CLIP;
		src = screenbuff + ATARI_HORZ_CLIP;
		while( dest < stop )
		{
			memcpy( dest, src, ATARI_VIS_WIDTH );
			dest += ATARI_WIDTH;
			memcpy( dest, src, ATARI_VIS_WIDTH );
			dest += ATARI_WIDTH * 2;
			src += ATARI_WIDTH;
		}
	}
#endif
	hResult = IDirectDrawSurface_Blt( lpPrimary, &rcDest, lpMemory, &rcSource, dwMyBltFlags, NULL);
	if( SUCCEEDED( hResult ) || hResult == DDERR_WASSTILLDRAWING)
		return;

    if( hResult == DDERR_SURFACELOST )
	{
		if( !RestoreSurfaces() )
			ShowDDrawError( IDS_DDERR_RESTORE_SURFACE, hResult, TRUE );
	}
	else
		ShowDDrawError( IDS_DDERR_PRIMARY_BLT, hResult, TRUE );
	return;
}

static void Screen_GDI_Stretch( UBYTE *screen )
{
	if( ulDiskState )
		Draw_DiskLED( screen + ulLEDOffset, ATARI_WIDTH );

#ifdef WIN_INLINE_ASM
	_asm
	{
		;; Order in this routine has been manipulated to maximize pairing on a Pentium processor
        mov     ebx, dword ptr [screenbuff]		;; the source pointer (screenbuff + ATARI_HORZ_CLIP)
		add		ebx, 018h
		mov     edi, dword ptr [screentemp]		;; the dest pointer
		mov		eax, 0f00000h					;; number of scan lines in the source (shifted 16 bits)
new_line_b:
		mov		ecx, 0150h						;; We're going to copy ATARI_VIS_WIDTH pixel pairs
copy_bytes_b:
		mov		dl, byte ptr [ebx]				;; Get this pixel
		mov		byte ptr [edi], dl				;; Store copy 1
		inc		edi								
		inc		ebx								;; Next source
		mov		byte ptr [edi], dl				;; Store copy 2
		inc		edi								;; Next destination

		dec		ecx
		jnz 	copy_bytes_b					;; do this until ATARI_VIS_WIDTH pairs are done

		mov		esi, edi						;; Current destination pointer into source index
		mov     ecx, 0a8h						;; Our count of DWORDs to copy (ATARI_VIS_WIDTH * 2 / 4)
		sub		esi, 02a0h						;; move back ATARI_VIS_WIDTH * 2 (beginning of pixel-doubled scan line)
		add		ebx, 030h						;; add in the offset to the next scan line for src
		rep     movsd							;; Move that string

		sub		eax, 10000h						;; decrement high word of eax
		jnz		new_line_b						;; Jump if we're not done
	}
#else
	{
		unsigned char *dest, *src, *stop;

		dest = screentemp;
		src = screenbuff + ATARI_HORZ_CLIP;
		stop = dest + ATARI_STRETCH_VIS_SCREEN_SIZE;
		while( dest < stop )
		{
			char *linep = dest;
			while( linep < dest + ATARI_STRETCH_VIS_WIDTH )
			{
				*linep++ = *src;
				*linep++ = *src++;
			}
			memcpy( linep, linep - ATARI_STRETCH_VIS_WIDTH, ATARI_STRETCH_VIS_WIDTH );
			dest += ATARI_STRETCH_VIS_WIDTH * 2;
			src += ATARI_HORZ_CLIP * 2;
		}
	}
#endif
	StretchDIBits( h_screenDC, 0, 0, ATARI_STRETCH_VIS_WIDTH, ATARI_STRETCH_HEIGHT,
		0, 0, ATARI_STRETCH_VIS_WIDTH, ATARI_STRETCH_HEIGHT,
		screentemp, lpbmi, DIB_RGB_COLORS, SRCCOPY);
}

static void Screen_GDI_Stretch_Scanlines( UBYTE *screen )
{
	if( ulDiskState )
		Draw_DiskLED( screen + ulLEDOffset, ATARI_WIDTH );

#ifdef WIN_INLINE_ASM
	_asm
	{
		;; Order in this routine has been manipulated to maximize pairing on a Pentium processor
        mov     esi, dword ptr [screenbuff]		;; the source pointer (screenbuff + ATARI_HORZ_CLIP)
		add		esi, ATARI_HORZ_CLIP
		mov     edi, dword ptr [screentemp]		;; the dest pointer
		dec		edi
		mov		eax, 0f00000h					;; number of scan lines in the source (shifted 16 bits)
new_line:
		mov		ax, ATARI_VIS_WIDTH						;; We're going to copy ATARI_VIS_WIDTH pixel pairs
copy_scanline:
		mov		dl, byte ptr [esi]								;; Get this pixel
		inc		edi												;; Next destination
		mov		cl, dl											;; Make a copy of original pixel
		mov		bl, dl											;; Another copy of original pixel
		and		bl, 0x0f										;; Bottom nybble of this pixel
		mov		byte ptr [edi], dl								;; Store copy 1 of regular pixel
		shr		bl, 1											;; bits / 2
		and     cl, 0xf0										;; Top nybble of this pixel
		add		bl, cl											;; (pixel val / 16) * 16 + (val % 16) / 2
		mov		byte ptr [edi + ATARI_STRETCH_VIS_WIDTH], bl	;; Store darkline pixel 1
		inc		esi												;; Next source pixel
		inc		edi												;; Next destination
		mov		byte ptr [edi], dl								;; Store copy 2 of regular pixel
		mov		byte ptr [edi + ATARI_STRETCH_VIS_WIDTH], bl	;; Store darkline pixel 2

		dec		ax												;; Decrement counter
		jnz		copy_scanline					;; do this until ATARI_VIS_WIDTH pairs are done

		add		edi, 02a0h						;; skip over the darkline
		add		esi, 030h						;; add in the offset to the next scan line for src

		sub		eax, 10000h						;; decrement high word of eax
		jnz		new_line						;; Jump if we're not done
	}
#else
	{
		unsigned char *dest, *src, *stop;

		dest = screentemp;
		src = screenbuff + ATARI_HORZ_CLIP;
		stop = dest + ATARI_STRETCH_VIS_SCREEN_SIZE;
		while( dest < stop )
		{
			char *linep = dest;
			char *darkline = linep + ATARI_STRETCH_VIS_WIDTH;
			while( linep < dest + ATARI_STRETCH_VIS_WIDTH )
			{
				*linep++ = *src;
				*darkline++ = (char)darkline_translate[ *src ];
				*linep++ = *src;
				*darkline++ = (char)darkline_translate[ *src++ ];
			}
			dest += ATARI_STRETCH_VIS_WIDTH * 2;
			src += ATARI_HORZ_CLIP * 2;
		}
	}
#endif
	StretchDIBits( h_screenDC, 0, 0, ATARI_STRETCH_VIS_WIDTH, ATARI_STRETCH_HEIGHT,
		0, 0, ATARI_STRETCH_VIS_WIDTH, ATARI_STRETCH_HEIGHT,
		screentemp, lpbmi, DIB_RGB_COLORS, SRCCOPY);
}

static void Screen_GDI( UBYTE *screen )
{
	if( ulDiskState )
		Draw_DiskLED( screen + ulLEDOffset, ATARI_WIDTH );

#ifdef WIN_INLINE_ASM
	_asm
	{
        mov     esi, dword ptr [screenbuff]		;; the source pointer (screenbuff + ATARI_HORZ_CLIP)
		add		esi, 018h
        mov     edi, dword ptr [screentemp]		;; the dest pointer
        mov     eax, 0f0h						;; number of lines (ATARI_HEIGHT)
scan_line:
        mov     ecx, 054h       ;; Our count of DWORDs to copy (ATARI_VIS_WIDTH/4)
        rep     movsd			;; Move that string
		add		esi, 030h		;; add in the offset to the next scan line for src

        dec     eax
        jnz     scan_line
	}
#else
	{
		unsigned char *dest, *src, *stop;

		stop = screentemp + ATARI_VIS_SCREEN_SIZE;
		dest = screentemp;
		src = screenbuff + ATARI_HORZ_CLIP;
		while( dest < stop )
		{
			memcpy( dest, src, ATARI_VIS_WIDTH );
			dest += ATARI_VIS_WIDTH;
			src += ATARI_WIDTH;
		}
	}
#endif
	StretchDIBits( h_screenDC, 0, 0, ATARI_VIS_WIDTH, ATARI_HEIGHT, 
		0, 0, ATARI_VIS_WIDTH, ATARI_HEIGHT, 
		screentemp, lpbmi, DIB_RGB_COLORS, SRCCOPY);
}

void SafeShowScreen( void )
{
	if( Atari_DisplayScreen != NULL )
		Atari_DisplayScreen( (unsigned char *)atari_screen );
	return;
}


/* Routines for plotting on the Atari screen (taken out of ui.c, which I don't use currently */
void AtariPlot(UBYTE * screen, int fg, int bg, int ch, int x, int y)
{
	int offset = ascii_to_screen[(ch & 0x07f)] * 8;
	int i;
	int j;
	char *ataricharset;
	char *ptr;

	if( !ulAtariState & ATARI_RUNNING )
		return;

	if( mach_xlxe != 0  )
		ataricharset = atarixl_os + 0x2000;
	else
		ataricharset = memory + 0xe000;

	ptr = &screen[24 * ATARI_WIDTH + 32];

	for (i = 0; i < 8; i++) {
		UBYTE data;

		data = ataricharset[offset++];

		for (j = 0; j < 8; j++) {
			int pixel;

			if (data & 0x80)
				pixel = fg;
			else
				pixel = bg;

			ptr[(y * 8 + i) * ATARI_WIDTH + (x * 8 + j)] = pixel;

			data = data << 1;
		}
	}
}

void AtariPrint(UBYTE * screen, int fg, int bg, char *string, int x, int y)
{
	while (*string) {
		AtariPlot(screen, fg, bg, *string++, x, y);
		x++;
	}
}

void CenterPrint(UBYTE * screen, int fg, int bg, char *string, int y)
{
	AtariPrint(screen, fg, bg, string, (40 - strlen(string)) / 2, y);
}

void Screen_Paused( UBYTE *screen )
{
	int i, j;
	UBYTE	*blank;
	
	for( i = 0; i < ATARI_HEIGHT; i++ )
	{
		blank = screen + i * ATARI_WIDTH + i % 2;
		for( j = 0; j < ATARI_WIDTH; j+=2 )
		{
			*blank = 0;
			blank +=2;
		}
	}
	CenterPrint( &screen[0], 255, 0, "Paused:F9 to continue", 11 );
	InvalidateRect( hWnd, NULL, FALSE );
	Atari_DisplayScreen( screen );
}

void Screen_Benchmark( )
{
	char	szReport[LOADSTRING_STRING_SIZE];
	ULONG	starttime, rendertime, i;
	double	result;

	LoadString( NULL, IDS_HIT_RETURN, gcErrorString, LOADSTRING_STRING_SIZE );
	MessageBox( hWnd, gcErrorString, "Atari800Win", MB_OK );
	starttime = timeGetTime();
	for( i=0; i  < 30; i++ )
		Atari_DisplayScreen( (UBYTE *)atari_screen );
	rendertime = timeGetTime() - starttime;
	result = (double)rendertime/30.0;
	LoadString( NULL, IDS_REPORT_FORMAT, gcErrorString, LOADSTRING_STRING_SIZE );
	sprintf( szReport, gcErrorString, rendertime, result );
	if( result >  20.0 )
	{
		LoadString( NULL, IDS_SPEED_WARNING, gcErrorString, LOADSTRING_STRING_SIZE );
		strcat( szReport, gcErrorString );
	}
	LoadString( NULL, IDS_BENCHMARK_RESULTS, gcErrorString, LOADSTRING_STRING_SIZE );
	MessageBox( hWnd, szReport, gcErrorString, MB_OK );
}

/*
**-----------------------------------------------------------------------------
** Name:    GetDDErrorString
** Purpose: outputs a debug string to debugger
**-----------------------------------------------------------------------------
*/

static BOOL GetDDErrorString (HRESULT hResult, LPTSTR lpszErrorBuff, DWORD cchError)
{
    DWORD  cLen;
    LPTSTR lpszError;
    TCHAR  szMsg[256];
	
    // Check parameters
    if (!lpszErrorBuff || !cchError)
    {
        // Error, invalid parameters
        return FALSE;
    }
	
    switch (hResult)
    {
//#ifdef VERBOSE_DIRECTX_ERROR
		case DD_OK:
		/*
		* The request completed successfully.
		*/
        lpszError = TEXT("DD_OK");
        break;

		/*
		* This object is already initialized
		*/
	case DDERR_ALREADYINITIALIZED:
		lpszError = TEXT("DDERR_ALREADYINITIALIZED");
		break;
		
		/*
		* This surface can not be attached to the requested surface.
		*/
    case DDERR_CANNOTATTACHSURFACE:
        lpszError = TEXT("DDERR_CANNOTATTACHSURFACE");
        break;
		
		/*
		* This surface can not be detached from the requested surface.
		*/
    case DDERR_CANNOTDETACHSURFACE:
        lpszError = TEXT("DDERR_CANNOTDETACHSURFACE");
        break;
		
		/*
		* Support is currently not available.
		*/
    case DDERR_CURRENTLYNOTAVAIL:
        lpszError = TEXT("DDERR_CURRENTLYNOTAVAIL");
        break;
		
		/*
		* An exception was encountered while performing the requested operation
		*/
    case DDERR_EXCEPTION:
        lpszError = TEXT("DDERR_EXCEPTION");
        break;
		
		/*
		* Generic failure.
		*/
    case DDERR_GENERIC:
		lpszError = TEXT("DDERR_GENERIC");
		break;
		
		/*
		* Height of rectangle provided is not a multiple of reqd alignment
		*/
    case DDERR_HEIGHTALIGN:
        lpszError = TEXT("DDERR_HEIGHTALIGN");
        break;
		
		/*
		* Unable to match primary surface creation request with existing
		* primary surface.
		*/
    case DDERR_INCOMPATIBLEPRIMARY:
        lpszError = TEXT("DDERR_INCOMPATIBLEPRIMARY");
        break;
		
		/*
		* One or more of the caps bits passed to the callback are incorrect.
		*/
    case DDERR_INVALIDCAPS:
        lpszError = TEXT("DDERR_INVALIDCAPS");
        break;
		
		/*
		* DirectDraw does not support provided Cliplist.
		*/
    case DDERR_INVALIDCLIPLIST:
        lpszError = TEXT("DDERR_INVALIDCLIPLIST");
        break;
		
		/*
		* DirectDraw does not support the requested mode
		*/
    case DDERR_INVALIDMODE:
        lpszError = TEXT("DDERR_INVALIDMODE");
        break;
		
		/*
		* DirectDraw received a pointer that was an invalid DIRECTDRAW object.
		*/
    case DDERR_INVALIDOBJECT:
        lpszError = TEXT("DDERR_INVALIDOBJECT");
        break;
		
		/*
		* One or more of the parameters passed to the callback function are
		* incorrect.
		*/
    case DDERR_INVALIDPARAMS:
        lpszError = TEXT("DDERR_INVALIDPARAMS");
        break;
		
		/*
		* pixel format was invalid as specified
		*/
    case DDERR_INVALIDPIXELFORMAT:
        lpszError = TEXT("DDERR_INVALIDPIXELFORMAT");
        break;
		
		/*
		* Rectangle provided was invalid.
		*/
    case DDERR_INVALIDRECT:
        lpszError = TEXT("DDERR_INVALIDRECT");
        break;
		
		/*
		* Operation could not be carried out because one or more surfaces are locked
		*/
    case DDERR_LOCKEDSURFACES:
        lpszError = TEXT("DDERR_LOCKEDSURFACES");
        break;
		
		/*
		* There is no 3D present.
		*/
    case DDERR_NO3D:
        lpszError = TEXT("DDERR_NO3D");
        break;
		
		/*
		* Operation could not be carried out because there is no alpha accleration
		* hardware present or available.
		*/
    case DDERR_NOALPHAHW:
        lpszError = TEXT("DDERR_NOALPHAHW");
        break;
		
		
		/*
		* no clip list available
		*/
    case DDERR_NOCLIPLIST:
        lpszError = TEXT("DDERR_NOCLIPLIST");
        break;
		
		/*
		* Operation could not be carried out because there is no color conversion
		* hardware present or available.
		*/
    case DDERR_NOCOLORCONVHW:
        lpszError = TEXT("DDERR_NOCOLORCONVHW");
        break;
		
		/*
		* Create function called without DirectDraw object method SetCooperativeLevel
		* being called.
		*/
    case DDERR_NOCOOPERATIVELEVELSET:
        lpszError = TEXT("DDERR_NOCOOPERATIVELEVELSET");
        break;
		
		/*
		* Surface doesn't currently have a color key
		*/
    case DDERR_NOCOLORKEY:
        lpszError = TEXT("DDERR_NOCOLORKEY");
        break;
		
		/*
		* Operation could not be carried out because there is no hardware support
		* of the dest color key.
		*/
    case DDERR_NOCOLORKEYHW:
        lpszError = TEXT("DDERR_NOCOLORKEYHW");
        break;
		
		/*
		* No DirectDraw support possible with current display driver
		*/
    case DDERR_NODIRECTDRAWSUPPORT:
        lpszError = TEXT("DDERR_NODIRECTDRAWSUPPORT");
        break;
		
		/*
		* Operation requires the application to have exclusive mode but the
		* application does not have exclusive mode.
		*/
    case DDERR_NOEXCLUSIVEMODE:
        lpszError = TEXT("DDERR_NOEXCLUSIVEMODE");
        break;
		
		/*
		* Flipping visible surfaces is not supported.
		*/
    case DDERR_NOFLIPHW:
        lpszError = TEXT("DDERR_NOFLIPHW");
        break;
		
		/*
		* There is no GDI present.
		*/
    case DDERR_NOGDI:
        lpszError = TEXT("DDERR_NOGDI");
        break;
		
		/*
		* Operation could not be carried out because there is no hardware present
		* or available.
		*/
    case DDERR_NOMIRRORHW:
        lpszError = TEXT("DDERR_NOMIRRORHW");
        break;
		
		/*
		* Requested item was not found
		*/
    case DDERR_NOTFOUND:
        lpszError = TEXT("DDERR_NOTFOUND");
        break;
		
		/*
		* Operation could not be carried out because there is no overlay hardware
		* present or available.
		*/
    case DDERR_NOOVERLAYHW:
        lpszError = TEXT("DDERR_NOOVERLAYHW");
        break;
		
		/*
		* Operation could not be carried out because there is no appropriate raster
		* op hardware present or available.
		*/
    case DDERR_NORASTEROPHW:
		lpszError = TEXT("DDERR_NORASTEROPHW");
        break;
		
		/*
		* Operation could not be carried out because there is no rotation hardware
		* present or available.
		*/
    case DDERR_NOROTATIONHW:
        lpszError = TEXT("DDERR_NOROTATIONHW");
        break;
		
		/*
		* Operation could not be carried out because there is no hardware support
		* for stretching
		*/
    case DDERR_NOSTRETCHHW:
        lpszError = TEXT("DDERR_NOSTRETCHHW");
        break;
		
		/*
		* DirectDrawSurface is not in 4 bit color palette and the requested operation
		* requires 4 bit color palette.
		*/
    case DDERR_NOT4BITCOLOR:
        lpszError = TEXT("DDERR_NOT4BITCOLOR");
        break;
		
		/*
		* DirectDrawSurface is not in 4 bit color index palette and the requested
		* operation requires 4 bit color index palette.
		*/
    case DDERR_NOT4BITCOLORINDEX:
        lpszError = TEXT("DDERR_NOT4BITCOLORINDEX");
        break;
		
		/*
		* DirectDraw Surface is not in 8 bit color mode and the requested operation
		* requires 8 bit color.
		*/
    case DDERR_NOT8BITCOLOR:
        lpszError = TEXT("DDERR_NOT8BITCOLOR");
        break;
		
		/*
		* Operation could not be carried out because there is no texture mapping
		* hardware present or available.
		*/
    case DDERR_NOTEXTUREHW:
        lpszError = TEXT("DDERR_NOTEXTUREHW");
        break;
		
		/*
		* Operation could not be carried out because there is no hardware support
		* for vertical blank synchronized operations.
		*/
    case DDERR_NOVSYNCHW:
        lpszError = TEXT("DDERR_NOVSYNCHW");
        break;
		
		/*
		* Operation could not be carried out because there is no hardware support
		* for zbuffer blting.
		*/
    case DDERR_NOZBUFFERHW:
        lpszError = TEXT("DDERR_NOZBUFFERHW");
        break;
		
		/*
		* Overlay surfaces could not be z layered based on their BltOrder because
		* the hardware does not support z layering of overlays.
		*/
    case DDERR_NOZOVERLAYHW:
        lpszError = TEXT("DDERR_NOZOVERLAYHW");
        break;
		
		/*
		* The hardware needed for the requested operation has already been
		* allocated.
		*/
    case DDERR_OUTOFCAPS:
        lpszError = TEXT("DDERR_OUTOFCAPS");
        break;
		
		/*
		* DirectDraw does not have enough memory to perform the operation.
		*/
    case DDERR_OUTOFMEMORY:
        lpszError = TEXT("DDERR_OUTOFMEMORY");
        break;
		
		/*
		* DirectDraw does not have enough memory to perform the operation.
		*/
    case DDERR_OUTOFVIDEOMEMORY:
        lpszError = TEXT("DDERR_OUTOFVIDEOMEMORY");
        break;
		
		/*
		* hardware does not support clipped overlays
		*/
    case DDERR_OVERLAYCANTCLIP:
        lpszError = TEXT("DDERR_OVERLAYCANTCLIP");
        break;
		
		/*
		* Can only have ony color key active at one time for overlays
		*/
    case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
        lpszError = TEXT("DDERR_OVERLAYCOLORKEYONLYONEACTIVE");
        break;
		
		/*
		* Access to this palette is being refused because the palette is already
		* locked by another thread.
		*/
    case DDERR_PALETTEBUSY:
        lpszError = TEXT("DDERR_PALETTEBUSY");
        break;
		
		/*
		* No src color key specified for this operation.
		*/
    case DDERR_COLORKEYNOTSET:
        lpszError = TEXT("DDERR_COLORKEYNOTSET");
        break;
		
		/*
		* This surface is already attached to the surface it is being attached to.
		*/
    case DDERR_SURFACEALREADYATTACHED:
        lpszError = TEXT("DDERR_SURFACEALREADYATTACHED");
        break;
		
		/*
		* This surface is already a dependency of the surface it is being made a
		* dependency of.
		*/
    case DDERR_SURFACEALREADYDEPENDENT:
        lpszError = TEXT("DDERR_SURFACEALREADYDEPENDENT");
        break;
		
		/*
		* Access to this surface is being refused because the surface is already
		* locked by another thread.
		*/
    case DDERR_SURFACEBUSY:
        lpszError = TEXT("DDERR_SURFACEBUSY");
        break;
		
		/*
		* Access to this surface is being refused because no driver exists
		* which can supply a pointer to the surface.
		* This is most likely to happen when attempting to lock the primary
		* surface when no DCI provider is present.
		* Will also happen on attempts to lock an optimized surface.
		*/
    case DDERR_CANTLOCKSURFACE:
        lpszError = TEXT("DDERR_CANTLOCKSURFACE");
        break;
		
		/*
		* Access to Surface refused because Surface is obscured.
		*/
    case DDERR_SURFACEISOBSCURED:
        lpszError = TEXT("DDERR_SURFACEISOBSCURED");
        break;
		
		/*
		* Access to this surface is being refused because the surface is gone.
		* The DIRECTDRAWSURFACE object representing this surface should
		* have Restore called on it.
		*/
    case DDERR_SURFACELOST:
        lpszError = TEXT("DDERR_SURFACELOST");
        break;
		
		/*
		* The requested surface is not attached.
		*/
    case DDERR_SURFACENOTATTACHED:
        lpszError = TEXT("DDERR_SURFACENOTATTACHED");
        break;
		
		/*
		* Height requested by DirectDraw is too large.
		*/
    case DDERR_TOOBIGHEIGHT:
        lpszError = TEXT("DDERR_TOOBIGHEIGHT");
        break;
		
		/*
		* Size requested by DirectDraw is too large --	 The individual height and
		* width are OK.
		*/
    case DDERR_TOOBIGSIZE:
        lpszError = TEXT("DDERR_TOOBIGSIZE");
        break;
		
		/*
		* Width requested by DirectDraw is too large.
		*/
    case DDERR_TOOBIGWIDTH:
        lpszError = TEXT("DDERR_TOOBIGWIDTH");
        break;
		
		/*
		* Action not supported.
		*/
    case DDERR_UNSUPPORTED:
        lpszError = TEXT("DDERR_UNSUPPORTED");
        break;
		
		/*
		* FOURCC format requested is unsupported by DirectDraw
		*/
    case DDERR_UNSUPPORTEDFORMAT:
        lpszError = TEXT("DDERR_UNSUPPORTEDFORMAT");
        break;
		
		/*
		* Bitmask in the pixel format requested is unsupported by DirectDraw
		*/
    case DDERR_UNSUPPORTEDMASK:
        lpszError = TEXT("DDERR_UNSUPPORTEDMASK");
        break;
		
		/*
		* vertical blank is in progress
		*/
    case DDERR_VERTICALBLANKINPROGRESS:
        lpszError = TEXT("DDERR_VERTICALBLANKINPROGRESS");
        break;
		
		/*
		* Informs DirectDraw that the previous Blt which is transfering information
		* to or from this Surface is incomplete.
		*/
    case DDERR_WASSTILLDRAWING:
        lpszError = TEXT("DDERR_WASSTILLDRAWING");
        break;
		
		
		/*
		* Rectangle provided was not horizontally aligned on reqd. boundary
		*/
    case DDERR_XALIGN:
        lpszError = TEXT("DDERR_XALIGN");
        break;
		
		/*
		* The GUID passed to DirectDrawCreate is not a valid DirectDraw driver
		* identifier.
		*/
    case DDERR_INVALIDDIRECTDRAWGUID:
        lpszError = TEXT("DDERR_INVALIDDIRECTDRAWGUID");
        break;
		
		/*
		* A DirectDraw object representing this driver has already been created
		* for this process.
		*/
    case DDERR_DIRECTDRAWALREADYCREATED:
        lpszError = TEXT("DDERR_DIRECTDRAWALREADYCREATED");
        break;
		
		/*
		* A hardware only DirectDraw object creation was attempted but the driver
		* did not support any hardware.
		*/
    case DDERR_NODIRECTDRAWHW:
        lpszError = TEXT("DDERR_NODIRECTDRAWHW");
        break;
		
		/*
		* this process already has created a primary surface
		*/
    case DDERR_PRIMARYSURFACEALREADYEXISTS:
        lpszError = TEXT("DDERR_PRIMARYSURFACEALREADYEXISTS");
        break;
		
		/*
		* software emulation not available.
		*/
    case DDERR_NOEMULATION:
        lpszError = TEXT("DDERR_NOEMULATION");
        break;
		
		/*
		* region passed to Clipper::GetClipList is too small.
		*/
    case DDERR_REGIONTOOSMALL:
        lpszError = TEXT("DDERR_REGIONTOOSMALL");
        break;
		
		/*
		* an attempt was made to set a clip list for a clipper objec that
		* is already monitoring an hwnd.
		*/
    case DDERR_CLIPPERISUSINGHWND:
        lpszError = TEXT("DDERR_CLIPPERISUSINGHWND");
        break;
		
		/*
		* No clipper object attached to surface object
		*/
    case DDERR_NOCLIPPERATTACHED:
        lpszError = TEXT("DDERR_NOCLIPPERATTACHED");
        break;
		
		/*
		* Clipper notification requires an HWND or
		* no HWND has previously been set as the CooperativeLevel HWND.
		*/
    case DDERR_NOHWND:
        lpszError = TEXT("DDERR_NOHWND");
        break;
		
		/*
		* HWND used by DirectDraw CooperativeLevel has been subclassed,
		* this prevents DirectDraw from restoring state.
		*/
    case DDERR_HWNDSUBCLASSED:
        lpszError = TEXT("DDERR_HWNDSUBCLASSED");
        break;
		
		/*
		* The CooperativeLevel HWND has already been set.
		* It can not be reset while the process has surfaces or palettes created.
		*/
    case DDERR_HWNDALREADYSET:
        lpszError = TEXT("DDERR_HWNDALREADYSET");
        break;
		
		/*
		* No palette object attached to this surface.
		*/
    case DDERR_NOPALETTEATTACHED:
        lpszError = TEXT("DDERR_NOPALETTEATTACHED");
        break;
		
		/*
		* No hardware support for 16 or 256 color palettes.
		*/
    case DDERR_NOPALETTEHW:
        lpszError = TEXT("DDERR_NOPALETTEHW");
        break;
		
		/*
		* If a clipper object is attached to the source surface passed into a
		* BltFast call.
		*/
    case DDERR_BLTFASTCANTCLIP:
        lpszError = TEXT("DDERR_BLTFASTCANTCLIP");
        break;
		
		/*
		* No blter.
		*/
    case DDERR_NOBLTHW:
        lpszError = TEXT("DDERR_NOBLTHW");
        break;
		
		/*
		* No DirectDraw ROP hardware.
		*/
    case DDERR_NODDROPSHW:
        lpszError = TEXT("DDERR_NODDROPSHW");
        break;
		
		/*
		* returned when GetOverlayPosition is called on a hidden overlay
		*/
    case DDERR_OVERLAYNOTVISIBLE:
        lpszError = TEXT("DDERR_OVERLAYNOTVISIBLE");
        break;
		
		/*
		* returned when GetOverlayPosition is called on a overlay that UpdateOverlay
		* has never been called on to establish a destionation.
		*/
    case DDERR_NOOVERLAYDEST:
        lpszError = TEXT("DDERR_NOOVERLAYDEST");
        break;
		
		/*
		* returned when the position of the overlay on the destionation is no longer
		* legal for that destionation.
		*/
    case DDERR_INVALIDPOSITION:
        lpszError = TEXT("DDERR_INVALIDPOSITION");
        break;
		
		/*
		* returned when an overlay member is called for a non-overlay surface
		*/
    case DDERR_NOTAOVERLAYSURFACE:
        lpszError = TEXT("DDERR_NOTAOVERLAYSURFACE");
        break;
		
		/*
		* An attempt was made to set the cooperative level when it was already
		* set to exclusive.
		*/
    case DDERR_EXCLUSIVEMODEALREADYSET:
        lpszError = TEXT("DDERR_EXCLUSIVEMODEALREADYSET");
        break;
		
		/*
		* An attempt has been made to flip a surface that is not flippable.
		*/
    case DDERR_NOTFLIPPABLE:
        lpszError = TEXT("DDERR_NOTFLIPPABLE");
        break;
		
		/*
		* Can't duplicate primary & 3D surfaces, or surfaces that are implicitly
		* created.
		*/
    case DDERR_CANTDUPLICATE:
        lpszError = TEXT("DDERR_CANTDUPLICATE");
        break;
		
		/*
		* Surface was not locked.  An attempt to unlock a surface that was not
		* locked at all, or by this process, has been attempted.
		*/
    case DDERR_NOTLOCKED:
        lpszError = TEXT("DDERR_NOTLOCKED");
        break;
		
		/*
		* Windows can not create any more DCs
		*/
    case DDERR_CANTCREATEDC:
        lpszError = TEXT("DDERR_CANTCREATEDC");
        break;
		
		/*
		* No DC was ever created for this surface.
		*/
    case DDERR_NODC:
        lpszError = TEXT("DDERR_NODC");
        break;
		
		/*
		* This surface can not be restored because it was created in a different
		* mode.
		*/
    case DDERR_WRONGMODE:
        lpszError = TEXT("DDERR_WRONGMODE");
        break;
		
		/*
		* This surface can not be restored because it is an implicitly created
		* surface.
		*/
    case DDERR_IMPLICITLYCREATED:
        lpszError = TEXT("DDERR_IMPLICITLYCREATED");
        break;
		
		/*
		* The surface being used is not a palette-based surface
		*/
    case DDERR_NOTPALETTIZED:
        lpszError = TEXT("DDERR_NOTPALETTIZED");
        break;
		
		
		/*
		* The display is currently in an unsupported mode
		*/
    case DDERR_UNSUPPORTEDMODE:
        lpszError = TEXT("DDERR_UNSUPPORTEDMODE");
        break;
		
		/*
		* Operation could not be carried out because there is no mip-map
		* texture mapping hardware present or available.
		*/
    case DDERR_NOMIPMAPHW:
        lpszError = TEXT("DDERR_NOMIPMAPHW");
        break;
		
		/*
		* The requested action could not be performed because the surface was of
		* the wrong type.
		*/
    case DDERR_INVALIDSURFACETYPE:
        lpszError = TEXT("DDERR_INVALIDSURFACETYPE");
        break;
		
		/*
		* Device does not support optimized surfaces, therefore no video memory optimized surfaces
		*/
    case DDERR_NOOPTIMIZEHW:
        lpszError = TEXT("DDERR_NOOPTIMIZEHW");
        break;
		
		/*
		* Surface is an optimized surface, but has not yet been allocated any memory
		*/
    case DDERR_NOTLOADED:
        lpszError = TEXT("DDERR_NOTLOADED");
        break;
		
		/*
		* A DC has already been returned for this surface. Only one DC can be
		* retrieved per surface.
		*/
    case DDERR_DCALREADYCREATED:
        lpszError = TEXT("DDERR_DCALREADYCREATED");
        break;
		
		/*
		* An attempt was made to allocate non-local video memory from a device
		* that does not support non-local video memory.
		*/
    case DDERR_NONONLOCALVIDMEM:
        lpszError = TEXT("DDERR_NONONLOCALVIDMEM");
        break;
		
		/*
		* The attempt to page lock a surface failed.
		*/
    case DDERR_CANTPAGELOCK:
        lpszError = TEXT("DDERR_CANTPAGELOCK");
        break;
		
		/*
		* The attempt to page unlock a surface failed.
		*/
    case DDERR_CANTPAGEUNLOCK:
        lpszError = TEXT("DDERR_CANTPAGEUNLOCK");
        break;
		
		/*
		* An attempt was made to page unlock a surface with no outstanding page locks.
		*/
    case DDERR_NOTPAGELOCKED:
        lpszError = TEXT("DDERR_NOTPAGELOCKED");
        break;
		
		/*
		* There is more data available than the specified buffer size could hold
		*/
    case DDERR_MOREDATA:
        lpszError = TEXT("DDERR_MOREDATA");
        break;
		
		/*
		* The video port is not active
		*/
    case DDERR_VIDEONOTACTIVE:
        lpszError = TEXT("DDERR_VIDEONOTACTIVE");
        break;
		
		/*
		* Surfaces created by one direct draw device cannot be used directly by
		* another direct draw device.
		*/
    case DDERR_DEVICEDOESNTOWNSURFACE:
        lpszError = TEXT("DDERR_DEVICEDOESNTOWNSURFACE");
        break;
		
		/*
		* An attempt was made to invoke an interface member of a DirectDraw object
		* created by CoCreateInstance() before it was initialized.
		*/
    case DDERR_NOTINITIALIZED:
        lpszError = TEXT("DDERR_NOTINITIALIZED");
        break;

//#endif

    default:
        /* Unknown DD/App Error */
        wsprintf (szMsg, "Error #%ld", (DWORD)hResult );
        lpszError = szMsg;
        break;
    }
	
    // Copy DD Error string to buff
    cLen = strlen (lpszError);
    if (cLen >= cchError)
    {
        cLen = cchError - 1;
    }
	
    if (cLen)
    {
        strncpy (lpszErrorBuff, lpszError, cLen);
        lpszErrorBuff[cLen] = 0;
    }
	
    return TRUE;
} // End GetDDErrorString

