/*--------------------------------------------------------------------------
 * 090-19 97 73 - A.Hed
 *
 * File: dx16.c
 * Written by: Fredrik Kling, 1997-11-13
 * Description: A DirectX driver for 16bit resolutions, based on Alex 8bit driver..
 *
 * Updates:
 * -- Date -- | ----- Name ----- |-- Did what....
 * 1997-11-13 | Fredrik Kling    | Implementation
 * 1997-04-01 | Alexander Boczar | Tog bort ModeX support pga av bristande information om lock()
 * 1997-04-01 | Alexander Boczar | Žndrade s† att ifall lPitch = Width, s† kopierar jag i ett svep ist„llet f”r rad f”r rad,
 * 1997-03-31 | Alexander Boczar | Fixade s† att copy() anv„nder lPitch f”r att skippa ev data efter varje rad.
 * 1997-06-06 | Alexander Boczar | Gjorde om drivern till det nya formatet
 *
 * Todo:
 *
 * Kolla vad h†rdvaran klarar ist„llet f”r k”ra rakt p† och faila..
 -------------------------------------------------------------------------------*/

#include <windows.h>
#include <ddraw.h>
#include "system/xstdio.h"
#include "drivers/drv16.h"
#include "draw16/draw16.h"

extern DRV dx16DRV;

#define MAXKEYS 256

typedef enum
{
	ERR_NOERR,
	ERR_NOMEM,
	ERR_NODLL,
	ERR_INVDLL,
	ERR_FAILWIN,
	ERR_FAILDDRAW,
	ERR_FAILCOOPERATIVE,
	ERR_FAILENUM,
	ERR_FAILMODE,
	ERR_FAILSURFACE
} ERRORS;

/* ######################################################################### */

static PALETTEENTRY dxpal[256];

static RGBA black_col={0,0,0,0};
static WORD RGB_r[256],RGB_g[256],RGB_b[256];

static int windowactive = 0;
static int keys = 0;
static char keystack[MAXKEYS];



static DWORD mousetimer = 0;

static int mouse_minx,mouse_maxx;
static int mouse_miny,mouse_maxy;
static int mousex,mousey;
static int mousexchange,mouseychange;
static int gmousexchange,gmouseychange;

static HINSTANCE dxDLL;
static HWND window;
static HINSTANCE process;
static LPDIRECTDRAW dd;
static LPDIRECTDRAWSURFACE showscreen;
//static LPDIRECTDRAWSURFACE workscreen;
static LPDIRECTDRAWPALETTE ddpal;

static FARPROC _DirectDrawCreate;
//static HRESULT WINAPI (*_DirectDrawCreate)( GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter );

static ERRORS error = ERR_NOERR;

/* ######################################################################### */

static char scan2key[256] =
{
	0,	1,	2,	3,	4,	5,	6,	7,
	8,	9,	10,	11,	12,	13,	14,	15,
	16,	17,	18,	19,	20,	21,	22,	23,
	24,	25,	26,	'',28,	29,	30,	31,

	32,	33,	34,	35,	36,	37,	38,	39,
	40,	41,	42,	43,	44,	45,	46,	47,
	'0','1','2','3','4','5','6','7',
	'8','9',':',';','<','=','>','?',

	'@','A','B','C','D','E','F','G',
	'H','I','J','K','L','N','M','O',
	'P','Q','R','S','T','U','V','W',
	'X','Y','Z',	0,	0,	0,	0,	0,

	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	'+',0,  '-',0,  0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,

	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,

	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,

	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,

	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0
};
/* ######################################################################### */

static void checkmsg()
{
	MSG msg;

	while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

static long FAR PASCAL WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	POINT mousepos;

	switch( message )
	{
		case WM_ACTIVATEAPP:
//			printf("WM_ACTIVEAPP\n");
			windowactive = wParam;
			break;

		case WM_TIMER:
			if (wParam == mousetimer)
			{
				if	( windowactive)
				{
					GetCursorPos( &mousepos);
					mousexchange = mousepos.x - (dx16DRV.width >> 1);
					mouseychange = mousepos.y - (dx16DRV.height >> 1);
					gmousexchange += mousexchange;
					gmouseychange += mouseychange;
					mousex += mousexchange;
					mousey += mouseychange;
					if ( mousex > mouse_maxx)
						mousex = mouse_maxx;
					else if ( mousex < mouse_minx)
						mousex = mouse_minx;
					if ( mousey > mouse_maxy)
						mousey = mouse_maxy;
					else if ( mousey < mouse_miny)
						mousey = mouse_miny;

					SetCursorPos( (dx16DRV.width >> 1), (dx16DRV.height >> 1));
				}
			}
			else
			{
				printf("Unknown timer caught!\n");
			}

		case WM_CREATE:
//			printf("WM_CREATE\n");
			break;

		case WM_SETCURSOR:
//			printf("WM_SETCURSOR\n");
			SetCursor(NULL);
			return TRUE;

		case WM_KEYDOWN:
//			printf("WM_KEYDOWN (%d,'%c')\n", wParam, wParam);
			if ( keys < MAXKEYS)
			{
				keystack[keys] = wParam;
				keys++;
			}
			break;

		case WM_PAINT:
//			printf("WM_PAINT\n");

			break;

		case WM_DESTROY:
//			printf("WM_DESTROY\n");
			PostQuitMessage( 0 );
			break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

/* ######################################################################### */

static HRESULT WINAPI countmodes( LPDDSURFACEDESC ddsd, LPVOID lpContext)
{
	if( ddsd->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8)
		dx16DRV.modes++;
	return( DDENUMRET_OK);
}

static HRESULT WINAPI addmodes( LPDDSURFACEDESC ddsd, LPVOID lpContext)
{
	if( ddsd->ddpfPixelFormat.dwFlags & DDPF_FOURCC)
	{
//		if (ddsd->ddpfPixelFormat.count == 16)
		{
			//
			// get r,g,b mask for swapbuffer routine....
			//
		  dx16DRV.modelist[dx16DRV.modes].width = ddsd->dwWidth;
		  dx16DRV.modelist[dx16DRV.modes].height = ddsd->dwHeight;
		  dx16DRV.modes++;
		}
	}
	return( DDENUMRET_OK);
}

static void fixcoltab (void)
{
	float ra,ga,ba;
	int i;

	ra = 31.0 / 256.0;
	ga = 63.0 / 256.0;
	ba = 31.0 / 256.0;

	for (i=0;i<256;i++)
	{
		RGB_r[i]=(int)(i*ra) << (5+6);
		RGB_g[i]=(int)(i*ga) << (5);
		RGB_b[i]=(int)(i*ba);
	}

}
static int setup( void)
{
	WNDCLASS wc;

	if ( (dxDLL = LoadLibrary("ddraw.dll")) == NULL)
	{
		error = ERR_NODLL;
		return( FALSE);
	}

  if ( ( _DirectDrawCreate = GetProcAddress( dxDLL, "DirectDrawCreate")) == NULL)
	{
		FreeLibrary( dxDLL);
		error = ERR_INVDLL;
		return( FALSE);
	}

	process = GetModuleHandle( NULL);

	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = process;
	wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = NULL;
	wc.lpszMenuName = "Visual 3D";
	wc.lpszClassName = "Visual 3D";
	RegisterClass( &wc);

//	window = CreateWindowEx( WS_EX_TOPMOST, "Visual 3D", "Visual 3D", WS_POPUP, 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ), NULL, NULL, process, NULL );
	window = CreateWindowEx( WS_EX_TOPMOST, "Visual 3D", "Visual 3D", WS_POPUP, 0, 0, 640,480, NULL,NULL, process, NULL );
	if ( window == NULL)
	{
		FreeLibrary( dxDLL);
		error = ERR_FAILWIN;
		return( FALSE);
	}

	ShowWindow( window, SW_SHOWDEFAULT );
	UpdateWindow( window );

	if ( _DirectDrawCreate( NULL, &dd, NULL) != DD_OK)
	{
		FreeLibrary( dxDLL);
		error = ERR_FAILDDRAW;
		return( FALSE);
	}

	dx16DRV.modes = 0;

//	if( dd->lpVtbl->SetCooperativeLevel( dd, window, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN) != DD_OK )
	if( dd->lpVtbl->SetCooperativeLevel( dd, window, DDSCL_ALLOWREBOOT | DDSCL_NORMAL) != DD_OK )
	{
		dd->lpVtbl->Release( dd);
		DestroyWindow( window);
		FreeLibrary( dxDLL);
		error = ERR_FAILCOOPERATIVE;
		return( FALSE);
	}

	if( dd->lpVtbl->EnumDisplayModes( dd, 0, NULL, NULL, countmodes) != DD_OK)
	{
		dd->lpVtbl->Release( dd);
		DestroyWindow( window);
		FreeLibrary( dxDLL);
		error = ERR_FAILENUM;
		return( FALSE);
	}

	printf("Found %d modes\n", dx16DRV.modes);
	if( dx16DRV.modes == 0)
	{
		dd->lpVtbl->Release( dd);
		DestroyWindow( window);
		FreeLibrary( dxDLL);
		return( FALSE);
	}

	dx16DRV.modelist = (DRVMODE *)xmalloc( dx16DRV.modes * sizeof( DRVMODE));
	dx16DRV.modes = 0;
	dd->lpVtbl->EnumDisplayModes( dd, 0, NULL, NULL, addmodes);

	dd->lpVtbl->SetCooperativeLevel( dd, NULL, DDSCL_NORMAL);
	dd->lpVtbl->Release( dd);
	DestroyWindow( window);
	FreeLibrary( dxDLL);


	fixcoltab ();

	return( TRUE);
}

static int openscr( int width, int height, int config)
{
	WNDCLASS wc;
	DDSURFACEDESC ddsd;

	dx16DRV.width = width;
	dx16DRV.minx = 0;
	dx16DRV.maxx = width - 1;

	dx16DRV.height = height;
	dx16DRV.miny = 0;
	dx16DRV.maxy = height - 1;

	dx16DRV.config = config;

	dx16DRV.palette = (RGB *)xmalloc( 256 * sizeof( RGB));

	if ( (dxDLL = LoadLibrary("ddraw.dll")) == NULL)
	{
		error = ERR_NODLL;
		return( FALSE);
	}

	if ( (_DirectDrawCreate = GetProcAddress( dxDLL, "DirectDrawCreate")) == NULL)
	{
		FreeLibrary( dxDLL);
		error = ERR_INVDLL;
		return( FALSE);
	}

	process = GetModuleHandle( NULL);

	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = process;
	wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = NULL;
	wc.lpszMenuName = "Visual 3D";
	wc.lpszClassName = "Visual 3D";
	RegisterClass( &wc);

//	window = CreateWindowEx( WS_EX_TOPMOST, "Visual 3D", "Visual 3D", WS_POPUP, 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ), NULL, NULL, process, NULL );
	window = CreateWindowEx( WS_EX_TOPMOST, "Visual 3D", "Visual 3D", WS_POPUP, 0, 0, 640,480, NULL,NULL, process, NULL );
	if ( window == NULL)
	{
		FreeLibrary( dxDLL);
		error = ERR_FAILWIN;
		return( FALSE);
	}

	ShowWindow( window, SW_SHOWDEFAULT );
	UpdateWindow( window );

	if ( _DirectDrawCreate( NULL, &dd, NULL) != DD_OK)
	{
		DestroyWindow( window);
		FreeLibrary( dxDLL);
		error = ERR_FAILDDRAW;
		return( FALSE);
	}

//	if( dd->lpVtbl->SetCooperativeLevel( dd, window, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ) != DD_OK )
	if( dd->lpVtbl->SetCooperativeLevel( dd, window, DDSCL_ALLOWREBOOT | DDSCL_NORMAL) != DD_OK )
	{
		dd->lpVtbl->Release( dd);
		DestroyWindow( window);
		FreeLibrary( dxDLL);
		error = ERR_FAILCOOPERATIVE;
		return( FALSE);
	}

	if( dd->lpVtbl->SetDisplayMode( dd, dx16DRV.width, dx16DRV.height, 16 ) != DD_OK )
	{
		dd->lpVtbl->Release( dd);
		DestroyWindow( window);
		FreeLibrary( dxDLL);
		error = ERR_FAILMODE;
		return( FALSE);
	}

	ddsd.dwSize = sizeof( ddsd );
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	if ( dd->lpVtbl->CreateSurface( dd, &ddsd, &showscreen, NULL ) != DD_OK )
	{
		dd->lpVtbl->Release( dd);

		DestroyWindow( window);
		FreeLibrary( dxDLL);
		error = ERR_FAILSURFACE;
		return( FALSE);
	}

	dx16DRV.clearscr( black_col);

	//dd->lpVtbl->CreatePalette( dd, DDPCAPS_8BIT, dxpal, &ddpal, NULL);
	//showscreen->lpVtbl->SetPalette( showscreen, ddpal);


	return( 1);
}

static int reopenscr( int width, int height)
{
	DDSURFACEDESC ddsd;

	showscreen->lpVtbl->Release( showscreen);

	if( dd->lpVtbl->SetDisplayMode( dd, width, height, 16 ) != DD_OK )
	{
		dd->lpVtbl->SetDisplayMode( dd, dx16DRV.width, dx16DRV.height, 16 );
		ddsd.dwSize = sizeof( ddsd );
		ddsd.dwFlags = DDSD_CAPS;
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	 	dd->lpVtbl->CreateSurface( dd, &ddsd, &showscreen, NULL );
		dx16DRV.clearscr( black_col);

//		dd->lpVtbl->CreatePalette( dd, DDPCAPS_8BIT, dxpal, &ddpal, NULL);
//		showscreen->lpVtbl->SetPalette( showscreen, ddpal);

		error = ERR_FAILMODE;
		return( FALSE);
	}

	ddsd.dwSize = sizeof( ddsd );
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	if ( dd->lpVtbl->CreateSurface( dd, &ddsd, &showscreen, NULL ) != DD_OK )
	{
		dd->lpVtbl->SetDisplayMode( dd, dx16DRV.width, dx16DRV.height, 16 );
		ddsd.dwSize = sizeof( ddsd );
		ddsd.dwFlags = DDSD_CAPS;
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	 	dd->lpVtbl->CreateSurface( dd, &ddsd, &showscreen, NULL );
		dx16DRV.clearscr( black_col);

//		dd->lpVtbl->CreatePalette( dd, DDPCAPS_8BIT, dxpal, &ddpal, NULL);
//		showscreen->lpVtbl->SetPalette( showscreen, ddpal);

		error = ERR_FAILSURFACE;
		return( FALSE);
	}

	dx16DRV.width = width;
	dx16DRV.minx = 0;
	dx16DRV.maxx = width - 1;

	dx16DRV.height = height;
	dx16DRV.miny = 0;
	dx16DRV.maxy = height - 1;

	dx16DRV.clearscr( black_col);

	//dd->lpVtbl->CreatePalette( dd, DDPCAPS_8BIT, dxpal, &ddpal, NULL);
	//showscreen->lpVtbl->SetPalette( showscreen, ddpal);

	return( TRUE);
}

static void closescr()
{
	if( mousetimer)
	{
		KillTimer( NULL, mousetimer);
		mousetimer = 0;
	}
	showscreen->lpVtbl->Release( showscreen);
	dd->lpVtbl->Release( dd);
	DestroyWindow( window);
	FreeLibrary( dxDLL);
}

static void setpalette( RGB *newpal)
{
	int i;
/*
	memcpy( dx16DRV.palette, newpal, sizeof(RGB) * 256);
	ddpal->lpVtbl->GetEntries( ddpal, 0, 0, 256, dxpal);
	for (i=0; i<256; i++)
	{
		dxpal[i].peRed = dx16DRV.palette[i].r;
		dxpal[i].peGreen = dx16DRV.palette[i].g;
		dxpal[i].peBlue = dx16DRV.palette[i].b;
	}

	//ddpal->lpVtbl->SetEntries( ddpal, 0, 0, 256, dxpal);
*/
}

static void vsync()
{
	checkmsg();
	if ( windowactive)
		dd->lpVtbl->WaitForVerticalBlank( dd, DDWAITVB_BLOCKBEGIN, NULL);
}

static void clearscr( RGBA col)
{
	DDSURFACEDESC ddsd;
	HRESULT ddrval;
	BYTE *dest;
	int y;
	WORD w;

	w = (RGB_r[col.r] | RGB_g[col.g] | RGB_b[col.b]);

	checkmsg();

	if ( windowactive)
	{
		ddsd.dwSize = sizeof( ddsd );
		ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		if ( ddrval == DDERR_SURFACELOST )
		{
			showscreen->lpVtbl->Restore( showscreen);
			ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		}
		if ( ddrval == DD_OK )
		{
			if ( ddsd.lPitch != dx16DRV.width)
			{
				dest = ddsd.lpSurface;
				for (y=0; y<dx16DRV.height; y++)
				{
					memset4( dest, (w | (w << 16)), dx16DRV.width >> 1);
					dest += ddsd.lPitch;
				}
			}
			else
				memset4( ddsd.lpSurface, (w | (w << 16)), (dx16DRV.width * dx16DRV.height) >> 1);
			showscreen->lpVtbl->Unlock( showscreen, NULL);
		}
	}
}

/* ######################################################################### */

static BUFF *createbuff( int width, int height)
{
	BUFF *buff, *oldactive;
	int i;
	RGBA cl_col = {0,0,0,0};

	if( (buff = (BUFF *)xmalloc( sizeof(BUFF))) == NULL)
	{
		error = ERR_NOMEM;
		return( NULL);
	}

	buff->drv = &dx16DRV;

	if( (buff->image = (RGBA *)xmalloc( sizeof( RGBA) * width * height)) == NULL)
	{
		xfree( buff);
		error = ERR_NOMEM;
		return( NULL);
	}

	if( (buff->ytab = (int *)xmalloc( sizeof( int) * height)) == NULL)
	{
		xfree( buff->image);
		xfree( buff);
		error = ERR_NOMEM;
		return( NULL);
	}

	if( dx16DRV.config & DRVCFG_ZBUFFER)
	{
    if( (buff->zbuffer = (int *)xmalloc( sizeof( int) * width * height)) == NULL)
		{
			xfree( buff->ytab);
			xfree( buff->image);
			xfree( buff);
			error = ERR_NOMEM;
			return( NULL);
		}
	}

	for (i=0; i<height; i++)
		buff->ytab[i] = i * width;	// 16 bit...


	buff->width = width;
	buff->height = height;

	buff->xorigo = width / 2;
	buff->yorigo = height/2;

	buff->minx = 0;
	buff->maxx = width - 1;
	buff->miny = 0;
	buff->maxy = height - 1;

	buff->lighttab = NULL;
	buff->transtab = NULL;

	oldactive = dx16DRV.activebuff;
	dx16DRV.activebuff = buff;
	dx16DRV.clearbuff( cl_col);
	dx16DRV.activebuff = oldactive;

	return( buff);
}

static void destroybuff( BUFF *buff)
{
	if( buff == dx16DRV.activebuff)
		dx16DRV.activebuff = NULL;

	if( buff != NULL)
	{
		if( buff->zbuffer != NULL) xfree( buff->zbuffer);
		if( buff->ytab != NULL) xfree( buff->ytab);
		if( buff->image != NULL) xfree( buff->image);
		xfree( buff);
	}
}

static void setbuff( BUFF *buff)
{
	dx16DRV.activebuff = buff;
}

static void xcopybuff (int x, int y, int dis, int tmp, BYTE *mask)
{
	DDSURFACEDESC ddsd;
	HRESULT ddrval;
	WORD w;
	int i,j;

	checkmsg();

	if (windowactive )
	{
//		ddsd.dwSize = sizeof (ddsd);
//		ddrval = showscreen->lpVtbl->Lock ( showscreen, NULL; &ddsd, DDLOCK_WAIT, NULL);
		ddsd.dwSize = sizeof( ddsd );
		ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		if ( ddrval == DDERR_SURFACELOST )
		{
			showscreen->lpVtbl->Restore( showscreen);
			ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		}
		if ( ddrval == DD_OK )
		{
			if( (x == y == 0) && (dx16DRV.activebuff->width == dx16DRV.width) && (dx16DRV.activebuff->height == dx16DRV.height))
			{
				BYTE *image = (BYTE *)dx16DRV.activebuff->image;
				BYTE *screen = (BYTE *)ddsd.lpSurface;

				for ( j=0; j<dx16DRV.height; j++)
				{
					if( (dx16DRV.width & 0x0f) == 0)
					{
						//memcpy16( screen, image, (2*dx16DRV.width >> 4));
						image += 2*dx16DRV.width;
						screen += ddsd.lPitch;
					}
					else if( (dx16DRV.width & 0x03) == 0)
					{
						//memcpy4( screen, image, dx16DRV.width >> 1);
						image += 2*dx16DRV.width;
						screen += ddsd.lPitch;
					}
					else
					{
						//memcpy4( screen, image, dx16DRV.width >> 1);
						image += 2*dx16DRV.width ^ 0x03;
						screen += 2*dx16DRV.width ^ 0x03;
			/*
						for ( i=0; i<(dx16DRV.width & 0x03); i++)
							*(screen++) = *(image++);
*/
						image += 2*dx16DRV.width & 0x03;
						screen += 2*dx16DRV.width & 0x03 + (ddsd.lPitch - dx16DRV.width);
					}
				}
			}
			else
			{
				int sxs,ixs,xlen;
				int sys,iys,ylen;
				WORD bw,bh;
				BYTE r,g,b;
				BYTE *image;
				WORD *screen,*pscreen;

				// Check X

				bw = dx16DRV.activebuff->width;
				bh = dx16DRV.activebuff->height;


				if ( x >= dx16DRV.minx )
				{
					sxs = x;
					ixs = 0;
					if ( (x + bw /*dx16DRV.activebuff->width*/) <= dx16DRV.maxx)
						xlen = bw; //dx16DRV.activebuff->width;
					else
						xlen = dx16DRV.maxx - x;
				}
				else
				{
					sxs = 0;
					ixs = dx16DRV.minx - x;
					if ( (x + bw/*dx16DRV.activebuff->width*/) <= dx16DRV.maxx)
						xlen = (x + bw /*dx16DRV.activebuff->width*/) - dx16DRV.minx;
					else
						xlen = dx16DRV.maxx - dx16DRV.minx;
				}

				// Check Y

				if ( y >= dx16DRV.miny )
				{
					sys = y;
					iys = 0;
					if ( (y + bh /*dx16DRV.activebuff->height*/) <= dx16DRV.maxy)
						ylen = bh /*dx16DRV.activebuff->height*/;
					else
						ylen = dx16DRV.maxy - y;
				}
				else
				{
					sys = 0;
					iys = dx16DRV.miny - y;
					if ( (y + bh /*dx16DRV.activebuff->height*/) <= dx16DRV.maxy)
						ylen = (y + bh/*dx16DRV.activebuff->height*/) - dx16DRV.miny;
					else
						ylen = dx16DRV.maxy - dx16DRV.miny;
				}

				for ( j=0; j<ylen; j++)
				{
					image = (BYTE *)&dx16DRV.activebuff->image[ ixs + dx16DRV.activebuff->ytab[j + iys]];
					screen = ddsd.lpSurface;
					screen += (sxs + ((ddsd.lPitch>>1) * (j*2 + sys)));
					pscreen = ddsd.lpSurface;
					pscreen += (sxs + ((ddsd.lPitch>>1) * (j*2 + sys+1)));

					//
					// alt2, R&G finns i en blandtabell (256x256) WORD, B finns sj„lv.
					//
					// xor	eax,eax
					// mov	al,[esi+2]		  ; l„s av bl†
					// mov	dx,[ebx+eax*2]
					// mov	ax,[esi]        ; l„s av R o G
					// or   dx,[ebx+eax*2+512]  ; fetch R blandat med G ur 256x256 tabell
					// mov  [edi],dx
					// add	esi,4
					// add  edi,2
					// dec	ecx
					// jnz  loop





					for ( i=0; i<xlen; i++)
					{
						r = image[0];
						g = image[1];
						b = image[2];
						w = (RGB_r[r] | RGB_g[g] | RGB_b[b]);
						*(screen) = w;
						*(screen+1) = w;
						*(pscreen) = w;
						*(pscreen+1) = w;

						screen += 2;
						pscreen += 2;
						image += sizeof (RGBA);
					}

				}
			}

			showscreen->lpVtbl->Unlock( showscreen, NULL);
		}
	}

}
static void copybuff( int x, int y)
{
	DDSURFACEDESC ddsd;
	HRESULT ddrval;
	int i,j;

	checkmsg();

	if ( windowactive)
	{
		ddsd.dwSize = sizeof( ddsd );
		ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		if ( ddrval == DDERR_SURFACELOST )
		{
			showscreen->lpVtbl->Restore( showscreen);
			ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		}
		if ( ddrval == DD_OK )
		{
			if( (x == y == 0) && (dx16DRV.activebuff->width == dx16DRV.width) && (dx16DRV.activebuff->height == dx16DRV.height))
			{
				BYTE *image = (BYTE *)dx16DRV.activebuff->image;
				BYTE *screen = (BYTE *)ddsd.lpSurface;

				for ( j=0; j<dx16DRV.height; j++)
				{
					if( (dx16DRV.width & 0x0f) == 0)
					{
						//memcpy16( screen, image, (2*dx16DRV.width >> 4));
						image += 2*dx16DRV.width;
						screen += ddsd.lPitch;
					}
					else if( (dx16DRV.width & 0x03) == 0)
					{
						//memcpy4( screen, image, dx16DRV.width >> 1);
						image += 2*dx16DRV.width;
						screen += ddsd.lPitch;
					}
					else
					{
						//memcpy4( screen, image, dx16DRV.width >> 1);
						image += 2*dx16DRV.width ^ 0x03;
						screen += 2*dx16DRV.width ^ 0x03;
			/*
						for ( i=0; i<(dx16DRV.width & 0x03); i++)
							*(screen++) = *(image++);
*/
						image += 2*dx16DRV.width & 0x03;
						screen += 2*dx16DRV.width & 0x03 + (ddsd.lPitch - dx16DRV.width);
					}
				}
			}
			else
			{
				int sxs,ixs,xlen;
				int sys,iys,ylen;
				WORD col;
				BYTE r,g,b;
				BYTE *image;
				WORD *screen;

				// Check X

				if ( x >= dx16DRV.minx )
				{
					sxs = x;
					ixs = 0;
					if ( (x + dx16DRV.activebuff->width) <= dx16DRV.maxx)
						xlen = dx16DRV.activebuff->width;
					else
						xlen = dx16DRV.maxx - x;
				}
				else
				{
					sxs = 0;
					ixs = dx16DRV.minx - x;
					if ( (x + dx16DRV.activebuff->width) <= dx16DRV.maxx)
						xlen = (x + dx16DRV.activebuff->width) - dx16DRV.minx;
					else
						xlen = dx16DRV.maxx - dx16DRV.minx;
				}

				// Check Y

				if ( y >= dx16DRV.miny )
				{
					sys = y;
					iys = 0;
					if ( (y + dx16DRV.activebuff->height) <= dx16DRV.maxy)
						ylen = dx16DRV.activebuff->height;
					else
						ylen = dx16DRV.maxy - y;
				}
				else
				{
					sys = 0;
					iys = dx16DRV.miny - y;
					if ( (y + dx16DRV.activebuff->height) <= dx16DRV.maxy)
						ylen = (y + dx16DRV.activebuff->height) - dx16DRV.miny;
					else
						ylen = dx16DRV.maxy - dx16DRV.miny;
				}

				for ( j=0; j<ylen; j++)
				{
					image = (BYTE *)&dx16DRV.activebuff->image[ ixs + dx16DRV.activebuff->ytab[j + iys]];
					screen = ddsd.lpSurface;
					screen += (sxs + ((ddsd.lPitch>>1) * (j + sys)));
					for ( i=0; i<xlen; i++)
					{
						r = image[0];
						g = image[1];
						b = image[2];
						*(screen) =  (RGB_r[r] | RGB_g[g] | RGB_b[b]);

						screen ++;
						image += sizeof (RGBA);
					}

				}
			}

			showscreen->lpVtbl->Unlock( showscreen, NULL);
		}
	}
}

static void xmovebuff (int x, int y, int displace, int tmp, RGBA col)
{
	DDSURFACEDESC ddsd;
	HRESULT ddrval;

	int sxs,ixs,xlen;
	int sys,iys,ylen,ofs;
	BYTE r,g,b;
	register int i,j;
	WORD w,bw,bh;
	void *videoptr;

	//clw = (RGB_r[col.r] | RGB_g[col.g] | RGB_b[col.b]);

	checkmsg();

	if ( windowactive)
	{
		ddsd.dwSize = sizeof( ddsd );
		ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		if ( ddrval == DDERR_SURFACELOST )
		{
			showscreen->lpVtbl->Restore( showscreen);
			ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		}
		if ( ddrval == DD_OK )
		{
	// Check boundaries...

			bw = dx16DRV.activebuff->width;
			bh = dx16DRV.activebuff->height;

			switch (displace)
			{
				case 0 : ofs = 0; break;
				case 1 : ofs = 1; break;
				case 2 : ofs = 1+dx16DRV.width; break;
				case 3 : ofs = dx16DRV.width; break;
			}

			if ( x >= dx16DRV.minx )
			{
				sxs = x;
				ixs = 0;
				if ( (x + bw /*dx16DRV.activebuff->width*/) <= dx16DRV.maxx)
					xlen = bw; //dx16DRV.activebuff->width;
				else
					xlen = dx16DRV.maxx - x;
			}
			else
			{
				sxs = dx16DRV.minx;
				ixs = dx16DRV.minx - x;
				if ( (x + bw/*dx16DRV.activebuff->width*/) <= dx16DRV.maxx)
					xlen = (x + bw /*dx16DRV.activebuff->width*/) - dx16DRV.minx;
				else
					xlen = dx16DRV.maxx - dx16DRV.minx;
			}
			// Check Y

			if ( y >= dx16DRV.miny )
			{
				sys = y;
				iys = 0;
				if ( (y + bh /*dx16DRV.activebuff->height*/) <= dx16DRV.maxy)
					ylen = bh /*dx16DRV.activebuff->height*/;
				else
					ylen = dx16DRV.maxy - y;
			}
			else
			{
				sys = dx16DRV.miny;
				iys = dx16DRV.miny - y;
				if ( (y + bh /*dx16DRV.activebuff->height*/) <= dx16DRV.maxy)
					ylen = (y + bh/*dx16DRV.activebuff->height*/) - dx16DRV.miny;
				else
					ylen = dx16DRV.maxy - dx16DRV.miny;
			}


			if ((dx16DRV.config & DRVCFG_15BITS) || (dx16DRV.config & DRVCFG_16BITS))
			{
				WORD *screen;
				BYTE *image;

				for ( j=0; j<ylen; j++)
				{
					image = (BYTE *)&dx16DRV.activebuff->image[ ixs + dx16DRV.activebuff->ytab[j + iys]];
					screen = ddsd.lpSurface;
					screen += (sxs + ((ddsd.lPitch>>1) * (2*j + sys)));

					for ( i=0; i<xlen; i++)
					{
						r = image[0];
						g = image[1];
						b = image[2];
						w = (RGB_r[r] | RGB_g[g] | RGB_b[b]);
						*(screen) = w;
						*(int *)image=(int)0;
						screen += 2;
						image+=sizeof (RGBA);
					}
				}
			}
/*
			else
				if (dx16DRV.config & DRVCFG_32BITS)
				{
					RGBA *screen,*vptr;
					RGBA *r_image;
					int yofs;

					vptr = (RGBA *) videoptr;

					for ( j=0; j<ylen; j++,yofs += 2*dx16DRV.width)
					{
						r_image = (RGBA *)&dx16DRV.activebuff->image[ ixs + dx16DRV.activebuff->ytab[j + iys]];
						screen = &vptr[ofs+sxs + yofs];
						for ( i=0; i<xlen; i++)
						{
							*(screen) = *(r_image);
							screen += 2;
							*r_image=col;
							r_image++;
						}
					}

				}
*/
			if ( dx16DRV.activebuff->zbuffer != NULL) //  & DRVCFG_ZBUFFER)
		    memset4( dx16DRV.activebuff->zbuffer, 0x7fffffff, (dx16DRV.activebuff->width * dx16DRV.activebuff->height));


			showscreen->lpVtbl->Unlock( showscreen, NULL);
		} // if ddrval
	} // if window active
}

static void clearbuff( RGBA col)
{
	DWORD dcol;
	memcpy4 ((void *)&dcol,(void *)&col,1);
	memset4( dx16DRV.activebuff->image, dcol , dx16DRV.activebuff->width * dx16DRV.activebuff->height);
	if ( dx16DRV.config & DRVCFG_ZBUFFER)
    memset4( dx16DRV.activebuff->zbuffer, 0x7fffffff, (dx16DRV.activebuff->width * dx16DRV.activebuff->height));
}

static void movebuff( int x, int y, RGBA col)
{
	copybuff( x, y);
	clearbuff( col);
}

/* ######################################################################### */

static void _plot( XYZ p, RGBA col)
{
  plot16( dx16DRV.activebuff, p, col);
}

static void _line( XYZ p1, XYZ p2, RGBA col)
{
  line16( dx16DRV.activebuff, p1, p2, col);
}

static void _poly( XYZ p1, XYZ p2, XYZ p3, RGBA col )
{
  poly16( dx16DRV.activebuff, p1, p2, p3, col);
}

static void _tpoly( XYZ p1, XYZ p2, XYZ p3, BYTE *data )
{
  tpoly16 ( dx16DRV.activebuff, p1, p2, p3, data);
}

static void _blit( XYZ p, RGBA *image, int *zimage, int width, int height)
{
  blit16( dx16DRV.activebuff, p, image, zimage, width, height);
}

/* ######################################################################### */

static int mouseinit( int minx, int miny, int maxx, int maxy)
{
  mouse_minx = minx;
  mouse_miny = miny;
  mouse_maxx = maxx;
  mouse_maxy = maxy;

  if( !mousetimer)
		mousetimer = SetTimer( window, 1, 20, NULL); //Start a mouse timer at 50hz

	return( mousetimer);
}

static int mousecallback( void ( *callback)( DRVMOUSE change))
{
	return( TRUE);
}

static void getmousepos( int *x, int *y)
{
  *x = mousex;
  *y = mousey;
}

static void setmousepos( int x, int y)
{
  mousex = x;
  mousey = y;
}

static void getmousediff( int *x, int *y)
{
  *x = gmousexchange;
  *y = gmouseychange;
  gmousexchange = 0;
  gmouseychange = 0;
}

/* ######################################################################### */

static int kbhit()
{
	return( keys);
}

static char getkey()
{
	int i;
	char key;

	if ( keys > 0)
	{
		key = keystack[0];
		keys--;
		for (i=0; i<keys; i++)
			keystack[i] = keystack[i+1];
		return( scan2key[key]);
	}
	else
	{
		while ( keys == 0)
			checkmsg();

		key = keystack[0];
		keys--;
		for (i=0; i<keys; i++)
			keystack[i] = keystack[i+1];
		return( scan2key[key]);
	}
}

/* ######################################################################### */

static char *geterror()
{
	ERRORS olderr = error;
	error = ERR_NOERR;
	switch( olderr)
	{
		case ERR_NOMEM: return("Unable allocate memory");
		case ERR_NODLL: return("Unable to load ddraw.dll");
		case ERR_INVDLL: return("Missing functions in ddraw.dll");
		case ERR_FAILWIN: return("Failed while opening window");
		case ERR_FAILDDRAW: return("Failed while create DirectDraw object");
		case ERR_FAILCOOPERATIVE: return("Unable to set cooperative mode");
		case ERR_FAILENUM: return("Failed while gathering mode information");
		case ERR_FAILMODE: return("Failed while setting mode");
		case ERR_FAILSURFACE: return("Unable to create surface");
		default : return( "Unknown error");
	}
}

/* ######################################################################### */

DRV dx16DRV =
{
	"Windows 95/NT DirectDraw(tm) 16bits Driver",\

	0,
	NULL,

	640,480,
	0,639,0,479,
	1,1,

	0,/*config*/

	NULL,

	NULL,

	setup,
	openscr,
	reopenscr,
	closescr,

	setpalette,
	vsync,
	clearscr,

	createbuff,
	destroybuff,
	setbuff,

	xcopybuff,
	xmovebuff,
	copybuff,
	clearbuff,
	movebuff,

  _plot,
  _line,
  _poly,
  _tpoly,
  _blit,

	mouseinit,
	mousecallback,
  getmousepos,
  setmousepos,
  getmousediff,

	kbhit,
	getkey,

	geterror,
};

