#define _CRT_SECURE_NO_WARNINGS
#define INITGUID
//#define FULLHEXDUMP

#include <windows.h>
#include <ddraw.h>
#include "dxwnd.h"
#include "dxhook.h"
#include "ddrawi.h"
#include "dxwcore.hpp"
#include "stdio.h" 
#include "hddraw.h"
#include "dxhelper.h"
#include "syslibs.h"

#define MAXBACKBUFFERS 4

extern void SetPixFmt(LPDDSURFACEDESC2);
extern void GetPixFmt(LPDDSURFACEDESC2);
extern CHAR *LogSurfaceAttributes(LPDDSURFACEDESC2, char *, int);
extern void DumpPixFmt(LPDDSURFACEDESC2);
extern void DescribeSurface(LPDIRECTDRAWSURFACE, int, char *, int);
extern void HookDDSurface(LPDIRECTDRAWSURFACE *, int,  BOOL);
extern void RegisterPixelFormat(int, LPDIRECTDRAWSURFACE);
extern void SetVSyncDelays(int, LPDIRECTDRAW);
extern char *DumpPixelFormat(LPDDSURFACEDESC2);
extern void FixSurfaceCaps(LPDDSURFACEDESC2, int);
extern SetPalette_Type pSetPaletteMethod(int);
extern ReleaseS_Type pReleaseSMethod(int);
extern CreatePalette_Type pCreatePaletteMethod(int);
extern GetGDISurface_Type pGetGDISurfaceMethod(int);

extern int iBakBufferVersion;
extern LPDIRECTDRAWSURFACE lpDDSEmu_Prim;
extern LPDIRECTDRAWSURFACE lpDDSEmu_Back;
extern LPDIRECTDRAWSURFACE lpDDZBuffer;
extern DDSURFACEDESC2 DDSD_Prim;
extern LPDIRECTDRAWPALETTE lpDDP;
extern LPDIRECTDRAWCLIPPER lpddC;
extern LPDIRECTDRAW lpPrimaryDD;
extern int iDDPExtraRefCounter;
extern DWORD dwBackBufferCaps;
extern BOOL bFlippedDC;
extern PALETTEENTRY DefaultSystemPalette[];
extern GetGDISurface_Type pGetGDISurface1, pGetGDISurface2, pGetGDISurface3, pGetGDISurface4, pGetGDISurface7;
extern GetAttachedSurface_Type pGetAttachedSurface1, pGetAttachedSurface2, pGetAttachedSurface3, pGetAttachedSurface4, pGetAttachedSurface7;
extern CreateSurface1_Type pCreateSurface1, pCreateSurface2, pCreateSurface3, pCreateSurface4, pCreateSurface7;
extern ReleaseS_Type pReleaseS1, pReleaseS2, pReleaseS3, pReleaseS4, pReleaseS7;
extern GetPixelFormat_Type pGetPixelFormat1, pGetPixelFormat2, pGetPixelFormat3, pGetPixelFormat4, pGetPixelFormat7;

void InitDSScreenParameters(int dxversion, LPDIRECTDRAWSURFACE lpdds)
{
	HRESULT res;
	DDPIXELFORMAT p;
	DDSURFACEDESC2 ddsd;
	GetPixelFormat_Type pGetPixelFormat;
	switch(dxversion){
		case 1: pGetPixelFormat=pGetPixelFormat1; break;
		case 2: pGetPixelFormat=pGetPixelFormat2; break;
		case 3: pGetPixelFormat=pGetPixelFormat3; break;
		case 4: pGetPixelFormat=pGetPixelFormat4; break;
		case 7: pGetPixelFormat=pGetPixelFormat7; break;
	}
	//OutTrace("dxversion=%d pGetPixelFormat=%x\n", dxversion, pGetPixelFormat);
	p.dwSize=sizeof(DDPIXELFORMAT);
	if(res=(*pGetPixelFormat)(lpdds, &p)){
		OutTraceE("GetPixelFormat: ERROR res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
		return;
	}

	ddsd.ddpfPixelFormat = p;
	OutTraceDW("InitDSScreenParameters: version=%d Actual %s\n", dxversion, DumpPixelFormat(&ddsd));
	dxw.ActualPixelFormat = p;
	SetBltTransformations(dxversion);
	return;
}

static void ClearSurfaceDesc(void *ddsd, int dxversion)
{
	int size;
	size = (dxversion < 4) ? sizeof(DDSURFACEDESC) : sizeof(DDSURFACEDESC2);
	memset(ddsd, 0, size); // Clean all
	((LPDDSURFACEDESC)ddsd)->dwSize = size;
}

static void BuildRealSurfaces(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, int dxversion)
{
	HRESULT res;
	DDSURFACEDESC2 ddsd;

	OutTraceDW("BuildRealSurfaces: lpdd=%x pCreateSurface=%x version=%d\n", lpdd, pCreateSurface, dxversion);
	if(lpDDSEmu_Prim==NULL){
		ClearSurfaceDesc((void *)&ddsd, dxversion);
		ddsd.dwFlags = DDSD_CAPS; 
		// try DDSCAPS_SYSTEMMEMORY first, then suppress it if not supported
		// no, DDSCAPS_SYSTEMMEMORY cause screen flickering while moving the window (and other troubles?)
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
		OutTraceDW("BuildRealSurfaces: %s\n", LogSurfaceAttributes(&ddsd, "[EmuPrim]", __LINE__));
		res=(*pCreateSurface)(lpdd, &ddsd, &lpDDSEmu_Prim, 0);
		if(res==DDERR_PRIMARYSURFACEALREADYEXISTS){
			OutTraceDW("BuildRealSurfaces: ASSERT DDSEmu_Prim already exists\n");
			if(dxw.Windowize){
				// in Windowize mode, the desktop properties are untouched, then the current primary surface can be recycled
				res=(*pGetGDISurfaceMethod(dxversion))(lpdd, &lpDDSEmu_Prim); 
			}
			else {
				// in non-Windowized mode, the primary surface must be released and rebuilt with the proper properties
				res=(*pGetGDISurfaceMethod(dxversion))(lpdd, &lpDDSEmu_Prim); 
				if (lpDDSEmu_Prim) while((*pReleaseSMethod(dxversion))(lpDDSEmu_Prim));
				res=(*pCreateSurface)(lpdd, &ddsd, &lpDDSEmu_Prim, 0);
			}
		}
		if(res){
			OutTraceE("BuildRealSurfaces: CreateSurface ERROR on DDSEmu_Prim res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
			if(res==DDERR_INVALIDPIXELFORMAT) DumpPixFmt(&ddsd);
			return;
		}
		OutTraceDW("BuildRealSurfaces: created new DDSEmu_Prim=%x\n",lpDDSEmu_Prim);
		if(IsDebug) DescribeSurface(lpDDSEmu_Prim, dxversion, "DDSEmu_Prim", __LINE__);
		InitDSScreenParameters(dxversion, lpDDSEmu_Prim);
		dxwss.PopSurface(lpDDSEmu_Prim);

		if (dxw.dwFlags3 & FORCECLIPPER){
			OutTraceDW("BuildRealSurfaces: FORCE SetClipper on primary hwnd=%x lpdds=%x\n", dxw.GethWnd(), lpDDSEmu_Prim);
			res=lpdd->CreateClipper(0, &lpddC, NULL);
			if (res) OutTraceE("BuildRealSurfaces: CreateClipper ERROR res=%x(%s)\n", res, ExplainDDError(res));
			res=lpddC->SetHWnd(0, dxw.GethWnd());
			if (res) OutTraceE("BuildRealSurfaces: SetHWnd ERROR res=%x(%s)\n", res, ExplainDDError(res));
			res=lpDDSEmu_Prim->SetClipper(lpddC);
			if (res) OutTraceE("BuildRealSurfaces: SetClipper ERROR res=%x(%s)\n", res, ExplainDDError(res));
		}
		// can't hook lpDDSEmu_Prim as generic, since the Flip method is unimplemented for a PRIMARY surface!
		// better avoid it or hook just useful methods.
		//if (dxw.dwTFlags & OUTPROXYTRACE) HookDDSurfaceGeneric(&lpDDSEmu_Prim, dxw.dwDDVersion);
	}

	if(lpDDSEmu_Back==NULL){
		ClearSurfaceDesc((void *)&ddsd, dxversion);
		ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
		ddsd.ddsCaps.dwCaps = dwBackBufferCaps;
		ddsd.dwWidth = dxw.GetScreenWidth();
		ddsd.dwHeight = dxw.GetScreenHeight();
		if(dxw.dwFlags4 & BILINEAR2XFILTER){
			// double backbuffer size
			ddsd.dwWidth = dxw.GetScreenWidth() << 1;
			ddsd.dwHeight = dxw.GetScreenHeight() << 1;
		}

		OutTraceDW("BuildRealSurfaces: %s\n", LogSurfaceAttributes(&ddsd, "[EmuBack]", __LINE__));
		res=(*pCreateSurface)(lpdd, &ddsd, &lpDDSEmu_Back, 0);
		if(res) {
			ddsd.ddsCaps.dwCaps &= ~DDSCAPS_SYSTEMMEMORY;
			OutTraceDW("BuildRealSurfaces: %s\n", LogSurfaceAttributes(&ddsd, "[EmuBack]", __LINE__));
			res=(*pCreateSurface)(lpdd, &ddsd, &lpDDSEmu_Back, 0);
		}
		if(res){
			OutTraceE("BuildRealSurfaces: CreateSurface ERROR on DDSEmuBack : res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
			if(res==DDERR_INVALIDPIXELFORMAT) DumpPixFmt(&ddsd);
			return;
		}
		OutTraceDW("BuildRealSurfaces: created new DDSEmu_Back=%x\n", lpDDSEmu_Back);
		if(IsDebug) DescribeSurface(lpDDSEmu_Back, dxversion, "DDSEmu_Back", __LINE__);
		dxwss.PopSurface(lpDDSEmu_Back);
		//if (dxw.dwTFlags & OUTPROXYTRACE) HookDDSurfaceGeneric(&lpDDSEmu_Back, dxversion);
	}
}

void RestoreDDrawSurfaces()
{
	// if it's a ddraw game ....
	if(lpPrimaryDD){
		OutTraceDW("RestoreDDrawSurfaces: rebuilding surfaces for dd session %x\n", lpPrimaryDD);
		ReleaseS_Type pReleaseS;
		CreateSurface_Type pCreateSurface;
		switch(iBakBufferVersion){
			case 1: pCreateSurface=(CreateSurface_Type)pCreateSurface1; pReleaseS=pReleaseS1; break;
			case 2: pCreateSurface=(CreateSurface_Type)pCreateSurface2; pReleaseS=pReleaseS2; break;
			case 3: pCreateSurface=(CreateSurface_Type)pCreateSurface3; pReleaseS=pReleaseS3; break;
			case 4: pCreateSurface=(CreateSurface_Type)pCreateSurface4; pReleaseS=pReleaseS4; break;
			case 7: pCreateSurface=(CreateSurface_Type)pCreateSurface7; pReleaseS=pReleaseS7; break;
		}
		if(lpDDSEmu_Back) while((*pReleaseS)(lpDDSEmu_Back)); lpDDSEmu_Back=0;
		if(lpDDSEmu_Prim) while((*pReleaseS)(lpDDSEmu_Prim)); lpDDSEmu_Prim=0;
		BuildRealSurfaces(lpPrimaryDD, pCreateSurface, iBakBufferVersion);
	}
}

static HRESULT BuildPrimaryEmu(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	DDSURFACEDESC2 ddsd;
	HRESULT res;

	OutTraceDW("BuildPrimaryEmu: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	// emulated primary surface
	memcpy((void *)&ddsd, lpddsd, lpddsd->dwSize);

	// handle the surface attributes before the ddsd.dwFlags gets updated:
	// if a surface desc is NOT specified, build one
	if(!(ddsd.dwFlags & DDSD_PIXELFORMAT)) SetPixFmt((LPDDSURFACEDESC2)&ddsd);
	// then save it
	dxw.VirtualPixelFormat = ddsd.ddpfPixelFormat;

	OutTraceDW("BuildPrimaryEmu: DDSD_PIXELFORMAT color=%d flags=%x\n", dxw.VirtualPixelFormat.dwRGBBitCount, dxw.VirtualPixelFormat.dwFlags);
	ddsd.dwFlags &= ~(DDSD_BACKBUFFERCOUNT|DDSD_REFRESHRATE);
	ddsd.dwFlags |= (DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT);
	ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX|DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM);
	// DDSCAPS_OFFSCREENPLAIN seems required to support the palette in memory surfaces
	ddsd.ddsCaps.dwCaps |= (DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY);
	// on WinXP Fifa 99 doesn't like DDSCAPS_SYSTEMMEMORY cap, so better to leave a way to unset it....
	if(dxw.dwFlags6 & NOSYSMEMPRIMARY) ddsd.ddsCaps.dwCaps &= ~DDSCAPS_SYSTEMMEMORY;

	ddsd.dwWidth = dxw.GetScreenWidth();
	ddsd.dwHeight = dxw.GetScreenHeight();

	// create Primary surface
	OutTraceDW("BuildPrimaryEmu: %s\n", LogSurfaceAttributes(&ddsd, "[Primary]" , __LINE__));
	res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
	if(res){
		OutTraceE("BuildPrimaryEmu: CreateSurface ERROR on DDSPrim res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
		if(res==DDERR_INVALIDPIXELFORMAT) DumpPixFmt(&ddsd);
		return res;
	}
	iBakBufferVersion=dxversion; // v2.03.01

	OutTraceDW("BuildPrimaryEmu: created PRIMARY DDSPrim=%x\n", *lplpdds);
    if(IsDebug) DescribeSurface(*lplpdds, dxversion, "DDSPrim", __LINE__);
	HookDDSurface(lplpdds, dxversion, TRUE);
	// "Hoyle Casino Empire" opens a primary surface and NOT a backbuffer ....

	// build a default System palette and apply it to primary surface
	if((ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) &&
		(dxw.dwFlags6 & SYNCPALETTE)){ 
		if(lpDDP == NULL){
			res=(*pCreatePaletteMethod(dxversion))(lpdd, DDPCAPS_8BIT|DDPCAPS_ALLOW256, DefaultSystemPalette, &lpDDP, NULL);
			if(res) OutTrace("BuildPrimaryEmu: CreatePalette ERROR err=%x at %d\n", res, __LINE__); 
		}
		// this must be done after hooking - who knows why?
		res=(*pSetPaletteMethod(dxversion))(*lplpdds, lpDDP);
		if(res) {
			OutTraceE("BuildPrimaryEmu: SetPalette ERROR err=%x at %d\n", res, __LINE__);
		}
		else iDDPExtraRefCounter++;
	}

	// set a global capability value for surfaces that have to blit to primary
	// DDSCAPS_OFFSCREENPLAIN seems required to support the palette in memory surfaces
	// DDSCAPS_SYSTEMMEMORY makes operations faster, but it is not always good...
	dwBackBufferCaps = (DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY);
	// on WinXP Fifa 99 doesn't like DDSCAPS_SYSTEMMEMORY cap, so better to leave a way to unset it....
	// this is important to avoid that certain D3D operations will abort - see "Forsaken" problem
	if(dxw.dwFlags6 & NOSYSMEMBACKBUF) dwBackBufferCaps = DDSCAPS_OFFSCREENPLAIN;

	if(dxw.dwFlags6 & SHAREDDC) bFlippedDC = TRUE;

	if(dxw.dwFlags5 & GDIMODE) return DD_OK;

	BuildRealSurfaces(lpdd, pCreateSurface, dxversion);
	return DD_OK;
}

static HRESULT BuildPrimaryFlippable(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	DDSURFACEDESC2 ddsd;
	HRESULT res;

	OutTraceDW("BuildPrimaryFlippable: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	// emulated primary surface
	memcpy((void *)&ddsd, lpddsd, lpddsd->dwSize);

	// handle the surface attributes before the ddsd.dwFlags gets updated:
	// if a surface desc is NOT specified, build one
	if(!(ddsd.dwFlags & DDSD_PIXELFORMAT)) SetPixFmt((LPDDSURFACEDESC2)&ddsd);
	// then save it
	dxw.VirtualPixelFormat = ddsd.ddpfPixelFormat;

	OutTraceDW("BuildPrimaryFlippable: DDSD_PIXELFORMAT color=%d flags=%x\n", dxw.VirtualPixelFormat.dwRGBBitCount, dxw.VirtualPixelFormat.dwFlags);

	// dwFlags
	ddsd.dwFlags &= ~(DDSD_REFRESHRATE);
	ddsd.dwFlags |= (DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT|DDSD_BACKBUFFERCOUNT);
	// DDSCAPS_OFFSCREENPLAIN seems required to support the palette in memory surfaces
	ddsd.ddsCaps.dwCaps |= (DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY);

	// dwBackBufferCount: set to at least 1
	if(!(lpddsd->dwFlags & DDSD_BACKBUFFERCOUNT) || (lpddsd->dwBackBufferCount == 0)) ddsd.dwBackBufferCount = 1;

	// dwCaps
	ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_PRIMARYSURFACE|DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM|DDSCAPS_FRONTBUFFER);
	ddsd.ddsCaps.dwCaps |= (DDSCAPS_COMPLEX|DDSCAPS_FLIP|DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY);
	// on WinXP Fifa 99 doesn't like DDSCAPS_SYSTEMMEMORY cap, so better to leave a way to unset it....
	if(dxw.dwFlags6 & NOSYSMEMPRIMARY) ddsd.ddsCaps.dwCaps &= ~DDSCAPS_SYSTEMMEMORY;

	// dwWidth & dwHeight
	ddsd.dwWidth = dxw.GetScreenWidth();
	ddsd.dwHeight = dxw.GetScreenHeight();

	// create Primary surface
	OutTraceDW("BuildPrimaryFlippable: %s\n", LogSurfaceAttributes(&ddsd, "[Primary]" , __LINE__));
	res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
	if(res){
		OutTraceE("BuildPrimaryFlippable: CreateSurface ERROR on DDSPrim res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
		if(res==DDERR_INVALIDPIXELFORMAT) DumpPixFmt(&ddsd);
		return res;
	}
	iBakBufferVersion=dxversion; // v2.03.01

	OutTraceDW("BuildPrimaryFlippable: created PRIMARY DDSPrim=%x\n", *lplpdds);
    if(IsDebug) DescribeSurface(*lplpdds, dxversion, "DDSPrim", __LINE__);
	HookDDSurface(lplpdds, dxversion, TRUE);
	// "Hoyle Casino Empire" opens a primary surface and NOT a backbuffer ....

	// build a default System palette and apply it to primary surface
	if((ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) &&
		(dxw.dwFlags6 & SYNCPALETTE)){ 
		if(lpDDP == NULL){
			res=(*pCreatePaletteMethod(dxversion))(lpdd, DDPCAPS_8BIT|DDPCAPS_ALLOW256, DefaultSystemPalette, &lpDDP, NULL);
			if(res) OutTrace("BuildPrimaryFlippable: CreatePalette ERROR err=%x at %d\n", res, __LINE__); 
		}
		// this must be done after hooking - who knows why?
		res=(*pSetPaletteMethod(dxversion))(*lplpdds, lpDDP);
		if(res) {
			OutTraceE("BuildPrimaryFlippable: SetPalette ERROR err=%x at %d\n", res, __LINE__);
		}
		else iDDPExtraRefCounter++;
	}

	// set a global capability value for surfaces that have to blit to primary
	dwBackBufferCaps = (DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY);
	// on WinXP Fifa 99 doesn't like DDSCAPS_SYSTEMMEMORY cap, so better to leave a way to unset it....
	// this is important to avoid that certain D3D operations will abort - see "Forsaken" problem
	if(dxw.dwFlags6 & NOSYSMEMBACKBUF) dwBackBufferCaps = DDSCAPS_OFFSCREENPLAIN;

	if(dxw.dwFlags6 & SHAREDDC) bFlippedDC = TRUE;

	if(dxw.dwFlags5 & GDIMODE) return DD_OK;

	BuildRealSurfaces(lpdd, pCreateSurface, dxversion);
	return DD_OK;
}

static HRESULT BuildPrimaryFullscreen(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	DDSURFACEDESC2 ddsd;
	HRESULT res;

	OutTraceDW("BuildPrimaryFullscreen: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	// genuine primary surface
	memcpy((void *)&ddsd, lpddsd, lpddsd->dwSize);

	// create Primary surface
	OutTraceDW("BuildPrimaryFullscreen: %s\n", LogSurfaceAttributes(&ddsd, "[Primary]", __LINE__));
	res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
	if(res){
		if (res==DDERR_PRIMARYSURFACEALREADYEXISTS){
			LPDIRECTDRAWSURFACE lpPrim;
			GetGDISurface_Type pGetGDISurface;
			pGetGDISurface = pGetGDISurfaceMethod(dxversion);
			OutTraceE("BuildPrimaryFullscreen: CreateSurface DDERR_PRIMARYSURFACEALREADYEXISTS workaround\n");
			(*pGetGDISurface)(lpPrimaryDD, &lpPrim);
			while ((*pReleaseSMethod(dxversion))(lpPrim));
			res = (*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
		}
		/* fall through */
		if(res){
			OutTraceE("BuildPrimaryFullscreen: CreateSurface ERROR on DDSPrim res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
			if(res==DDERR_INVALIDPIXELFORMAT) DumpPixFmt(&ddsd);
			return res;
		}
	}

	OutTraceDW("BuildPrimaryFullscreen: created PRIMARY DDSPrim=%x\n", *lplpdds);
    if(IsDebug) DescribeSurface(*lplpdds, dxversion, "DDSPrim", __LINE__);

	iBakBufferVersion=dxversion; 
	HookDDSurface(lplpdds, dxversion, TRUE);
	if(dxw.dwFlags1 & CLIPCURSOR) dxw.SetClipCursor();

	return DD_OK;
}

static HRESULT BuildPrimaryDir(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	DDSURFACEDESC2 ddsd;
	HRESULT res;

	OutTraceDW("BuildPrimaryDir: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	// genuine primary surface
	memcpy((void *)&ddsd, lpddsd, lpddsd->dwSize);
	// v2.03.98 - when going to fullscreen mode and no emulation do not alter the capability masks, or in other words ...
	// if doing emulation or in window mode, fix the capability masks (De Morgan docet)
	ddsd.dwFlags &= ~(DDSD_WIDTH|DDSD_HEIGHT|DDSD_BACKBUFFERCOUNT|DDSD_REFRESHRATE|DDSD_PIXELFORMAT);
	ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_FLIP|DDSCAPS_COMPLEX);
	// v2.02.93: don't move primary / backbuf surfaces on systemmemory when 3DDEVICE is requested
	// this impact also on capabilities for temporary surfaces for AERO optimized handling
	// v2.04.08: this seems not always true, so it can be bypassed by ALLOWSYSMEMON3DDEV, just in case.
	// should be tested with "Tomb Raider 3" in no emulation mode
	if(!(dxw.dwFlags8 & ALLOWSYSMEMON3DDEV)){
		if ((lpddsd->dwFlags & DDSD_CAPS) && (lpddsd->ddsCaps.dwCaps & DDSCAPS_3DDEVICE)) 
			ddsd.ddsCaps.dwCaps &= ~DDSCAPS_SYSTEMMEMORY;
	}

	// create Primary surface
	OutTraceDW("BuildPrimaryDir: %s\n", LogSurfaceAttributes(&ddsd, "[Primary]", __LINE__));
	res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
	if(res){
		if (res==DDERR_PRIMARYSURFACEALREADYEXISTS){
			LPDIRECTDRAWSURFACE lpPrim;
			GetGDISurface_Type pGetGDISurface;
			pGetGDISurface = pGetGDISurfaceMethod(dxversion);
			OutTraceE("BuildPrimaryDir: CreateSurface DDERR_PRIMARYSURFACEALREADYEXISTS workaround\n");
			(*pGetGDISurface)(lpPrimaryDD, &lpPrim);
			while ((*pReleaseSMethod(dxversion))(lpPrim));
			res = (*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
		}
		/* fall through */
		if(res){
			OutTraceE("BuildPrimaryDir: CreateSurface ERROR on DDSPrim res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
			if(res==DDERR_INVALIDPIXELFORMAT) DumpPixFmt(&ddsd);
			return res;
		}
	}

	OutTraceDW("BuildPrimaryDir: created PRIMARY DDSPrim=%x\n", *lplpdds);
    if(IsDebug) DescribeSurface(*lplpdds, dxversion, "DDSPrim", __LINE__);

	if(dxw.dwFlags1 & EMULATEBUFFER){
		lpDDSEmu_Prim = *lplpdds;
		dxwss.PopSurface(lpDDSEmu_Prim);

		ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
		// warning: can't create zero sized backbuffer surface !!!!
		ddsd.dwWidth = dxw.GetScreenWidth();
		ddsd.dwHeight = dxw.GetScreenHeight();
		ddsd.ddsCaps.dwCaps = 0;
		if (dxversion >= 4) ddsd.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN;
		OutTraceDW("BuildPrimaryDir: %s\n", LogSurfaceAttributes(&ddsd, "[Dir FixBuf]", __LINE__));
		res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
		if(res){
			OutTraceE("BuildPrimaryDir: CreateSurface ERROR on DDSPrim res=%x(%s) at %d\n",res, ExplainDDError(res), __LINE__);
			return res;
		}
		OutTraceDW("BuildPrimaryDir: created FIX DDSPrim=%x\n", *lplpdds);
		if(IsDebug) DescribeSurface(*lplpdds, dxversion, "DDSPrim(2)", __LINE__);
	}

	iBakBufferVersion=dxversion; // v2.03.37
	HookDDSurface(lplpdds, dxversion, TRUE);
	if(dxw.dwFlags1 & CLIPCURSOR) dxw.SetClipCursor();

	return DD_OK;
}

static HRESULT BuildBackBufferEmu(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	DDSURFACEDESC2 ddsd;
	HRESULT res;

	OutTraceDW("BuildBackBufferEmu: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	// create BackBuffer surface
	memcpy(&ddsd, lpddsd, lpddsd->dwSize);
	ddsd.dwFlags &= ~(DDSD_BACKBUFFERCOUNT|DDSD_REFRESHRATE);
	ddsd.dwFlags |= (DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT);
	ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_BACKBUFFER|DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX|DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM);

	// v2.03.75: if a surface desc is NOT specified, build one. This will allow ZBUF attach.
	if(!(lpddsd->dwFlags & DDSD_PIXELFORMAT)) SetPixFmt((LPDDSURFACEDESC2)&ddsd);

	// DDSCAPS_OFFSCREENPLAIN seems required to support the palette in memory surfaces
	ddsd.ddsCaps.dwCaps |= (DDSCAPS_SYSTEMMEMORY|DDSCAPS_OFFSCREENPLAIN);
	// necessary: Martian Gotic crashes otherwise
	// v2.04.08: this seems no longer true in some cases, so ALLOWSYSMEMON3DDEV will bypass it.
	// Should be tested with "Martian Gotic" and "Dominant Species" SW mode in emulation modes
	if(!(dxw.dwFlags8 & ALLOWSYSMEMON3DDEV)){
		if((ddsd.dwFlags & DDSD_CAPS) && (ddsd.ddsCaps.dwCaps & DDSCAPS_3DDEVICE))
			ddsd.ddsCaps.dwCaps &= ~DDSCAPS_SYSTEMMEMORY;
	}
	// on WinXP Fifa 99 doesn't like DDSCAPS_SYSTEMMEMORY cap, so better to leave a way to unset it....
	if(dxw.dwFlags6 & NOSYSMEMBACKBUF) ddsd.ddsCaps.dwCaps &= ~DDSCAPS_SYSTEMMEMORY;
	ddsd.dwWidth = dxw.GetScreenWidth();
	ddsd.dwHeight = dxw.GetScreenHeight();
	GetPixFmt(&ddsd);

	OutTraceDW("BuildBackBufferEmu: %s\n", LogSurfaceAttributes(&ddsd, "[Backbuf]", __LINE__));
	res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
	if(res) {
		OutTraceE("BuildBackBufferEmu: CreateSurface ERROR res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
		if(res==DDERR_INVALIDPIXELFORMAT) DumpPixFmt(&ddsd);
		return res;
	}

	OutTraceDW("BuildBackBufferEmu: created BACK DDSBack=%x\n", *lplpdds);
    if(IsDebug) DescribeSurface(*lplpdds, dxversion, "DDSBack", __LINE__);
	HookDDSurface(lplpdds, dxversion, FALSE); // added !!!
	iBakBufferVersion=dxversion; // v2.02.31

	if((ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) &&
		(dxw.dwFlags6 & SYNCPALETTE)){ 
		if(lpDDP == NULL){
			res=(*pCreatePaletteMethod(dxversion))(lpdd, DDPCAPS_8BIT|DDPCAPS_ALLOW256, DefaultSystemPalette, &lpDDP, NULL);
			if(res) OutTrace("BuildBackBufferEmu: CreatePalette ERROR err=%x at %d\n", res, __LINE__); 
		}
		// this must be done after hooking - who knows why?
		res=(*pSetPaletteMethod(dxversion))(*lplpdds, lpDDP);
		if(res) {
			OutTraceE("BuildBackBufferEmu: SetPalette ERROR err=%x at %d\n", res, __LINE__);
		}
		else iDDPExtraRefCounter++;
	}

	// V2.1.85/V2.2.34: tricky !!!!
	// When a real backbuffer is created, it has a reference to its frontbuffer.
	// some games (Monopoly 3D) may depend on this setting - i.e. they could close
	// the exceeding references - so this is better be replicated adding an initial
	// reference to the zero count. But you don't have to do this if the backbuffer
	// is created independently by the primary surface.
	(*lplpdds)->AddRef(); // should it be repeated BBCount times????

	return DD_OK;
}

static HRESULT BuildBackBufferFlippable(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	DDSURFACEDESC2 ddsd;
	HRESULT res;

	OutTraceDW("BuildBackBufferFlippable: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	//MessageBox(NULL, "BuildBackBufferFlippable", "DxWnd", MB_OK);
	
	// create BackBuffer surface
	memcpy(&ddsd, lpddsd, lpddsd->dwSize);

	ddsd.dwFlags &= ~(DDSD_BACKBUFFERCOUNT|DDSD_REFRESHRATE);
	ddsd.dwFlags |= (DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT);

	ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_PRIMARYSURFACE|DDSCAPS_FRONTBUFFER|DDSCAPS_BACKBUFFER|DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM);
	ddsd.ddsCaps.dwCaps |= (DDSCAPS_SYSTEMMEMORY|DDSCAPS_OFFSCREENPLAIN);

	ddsd.dwWidth = dxw.GetScreenWidth();
	ddsd.dwHeight = dxw.GetScreenHeight();
	GetPixFmt(&ddsd);

	OutTraceDW("BuildBackBufferFlippable: %s\n", LogSurfaceAttributes(&ddsd, "[Backbuf]", __LINE__));
	res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
	if(res) {
		OutTraceE("BuildBackBufferFlippable: CreateSurface ERROR res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
		if(res==DDERR_INVALIDPIXELFORMAT) DumpPixFmt(&ddsd);
		return res;
	}

	OutTraceDW("BuildBackBufferFlippable: created BACK DDSBack=%x\n", *lplpdds);
    if(IsDebug) DescribeSurface(*lplpdds, dxversion, "DDSBack", __LINE__);
	HookDDSurface(lplpdds, dxversion, FALSE); // added !!!
	iBakBufferVersion=dxversion; // v2.02.31

	if((ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) &&
		(dxw.dwFlags6 & SYNCPALETTE)){ 
		if(lpDDP == NULL){
			res=(*pCreatePaletteMethod(dxversion))(lpdd, DDPCAPS_8BIT|DDPCAPS_ALLOW256, DefaultSystemPalette, &lpDDP, NULL);
			if(res) OutTrace("BuildBackBufferFlippable: CreatePalette ERROR err=%x at %d\n", res, __LINE__); 
		}
		// this must be done after hooking - who knows why?
		res=(*pSetPaletteMethod(dxversion))(*lplpdds, lpDDP);
		if(res) {
			OutTraceE("BuildBackBufferFlippable: SetPalette ERROR err=%x at %d\n", res, __LINE__);
		}
		else iDDPExtraRefCounter++;
	}

	return DD_OK;
}

static HRESULT AttachBackBufferFlippable(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	HRESULT res;
	LPDIRECTDRAWSURFACE lpDDSPrim;
	OutTraceDW("AttachBackBufferFlippable: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);

	// retrieve the attached backbuffer surface and hook it

	if(lpddsd->dwBackBufferCount == 0) return DD_OK; // nothing to retrieve

	GetAttachedSurface_Type pGetAttachedSurface;
	DDSCAPS2 caps;
	switch(dxversion){
		case 1: pGetAttachedSurface = pGetAttachedSurface1; break;
		case 2: pGetAttachedSurface = pGetAttachedSurface2; break;
		case 3: pGetAttachedSurface = pGetAttachedSurface3; break;
		case 4: pGetAttachedSurface = pGetAttachedSurface4; break;
		case 7: pGetAttachedSurface = pGetAttachedSurface7; break;
	}
	memset(&caps, 0, sizeof(caps));
	caps.dwCaps = DDSCAPS_BACKBUFFER;
	lpDDSPrim = dxwss.GetPrimarySurface();
	res = (*pGetAttachedSurface)(lpDDSPrim, (LPDDSCAPS)&caps, lplpdds);
	if(res){
		OutTraceE("AttachBackBufferFlippable: GetAttachedSurface ERROR on DDSPrim res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
		return res;
	}

	OutTraceDW("AttachBackBufferFlippable: retrieved BACK DDSBack=%x\n", *lplpdds);
    if(IsDebug) DescribeSurface(*lplpdds, dxversion, "DDSBack", __LINE__);
	HookDDSurface(lplpdds, dxversion, FALSE); // added !!!
	iBakBufferVersion=dxversion; // v2.02.31

	return DD_OK;
}

static HRESULT BuildBackBufferFullscreen(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	OutTraceDW("BuildBackBufferFullscreen: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	return DD_OK;
}

static HRESULT AttachBackBufferFullscreen(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	OutTraceDW("AttachBackBufferFullscreen: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	return DD_OK;
}

static HRESULT BuildBackBufferDir(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	DDSURFACEDESC2 ddsd;
	HRESULT res;

	OutTraceDW("BuildBackBufferDir: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	// create BackBuffer surface
	// ClearSurfaceDesc((void *)&ddsd, dxversion);
	memcpy(&ddsd, lpddsd, lpddsd->dwSize);
	if(dxw.IsEmulated || dxw.Windowize){
		ddsd.dwFlags &= ~(DDSD_WIDTH|DDSD_HEIGHT|DDSD_BACKBUFFERCOUNT|DDSD_REFRESHRATE|DDSD_PIXELFORMAT);
		ddsd.dwFlags |= (DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH);
		ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX);
		// v2.02.93: don't move primary / backbuf surfaces on systemmemory when 3DDEVICE is requested
		// v2.04.08: possibly no longer true ... to test with "Tomb Raider 3" non emulated modes
		if(dxw.dwFlags8 & ALLOWSYSMEMON3DDEV){
			ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; 
			if (dxversion >= 4) ddsd.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN;
			ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM);
		}
		else {
			if(lpddsd->ddsCaps.dwCaps & DDSCAPS_3DDEVICE) {
				ddsd.ddsCaps.dwCaps &= ~DDSCAPS_SYSTEMMEMORY;
			}
			else {
				ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; 
				if (dxversion >= 4) ddsd.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN;
				ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_VIDEOMEMORY|DDSCAPS_LOCALVIDMEM);
			}
		}
	}
	if(dxw.dwFlags6 & NOSYSMEMBACKBUF) ddsd.ddsCaps.dwCaps &= ~DDSCAPS_SYSTEMMEMORY;
	ddsd.dwWidth = dxw.GetScreenWidth();
	ddsd.dwHeight = dxw.GetScreenHeight();

	if (dxw.dwFlags2 & BACKBUFATTACH) {
		LPDIRECTDRAWSURFACE lpPrim;
		DDSURFACEDESC2 prim;
		GetGDISurface_Type pGetGDISurface;
		pGetGDISurface = pGetGDISurfaceMethod(dxversion);
		(*pGetGDISurface)(lpPrimaryDD, &lpPrim);
		memset(&prim, 0, sizeof(DDSURFACEDESC2));
		prim.dwSize = (dxversion >= 4) ? sizeof(DDSURFACEDESC2) : sizeof(DDSURFACEDESC);
		res=lpPrim->GetSurfaceDesc((DDSURFACEDESC *)&prim);
		(*pReleaseSMethod(dxversion))(lpPrim);
		ddsd.dwWidth = prim.dwWidth;
		ddsd.dwHeight = prim.dwHeight;
		OutTraceDW("BuildBackBufferDir: BMX FIX res=%x(%s) wxh=(%dx%d)\n", res, ExplainDDError(res),ddsd.dwWidth, ddsd.dwHeight);
	}
	OutTraceDW("BuildBackBufferDir: %s\n", LogSurfaceAttributes(&ddsd, "[Backbuf]", __LINE__));
	res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
	if(res) {
		if ((dxw.dwFlags1 & SWITCHVIDEOMEMORY) && (res==DDERR_OUTOFVIDEOMEMORY)){
			OutTraceDW("BuildBackBufferDir: CreateSurface DDERR_OUTOFVIDEOMEMORY ERROR at %d, retry in SYSTEMMEMORY\n", __LINE__);
			ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY; 
			ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; 
			res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, 0);
		}
		if(res){
			OutTraceE("BuildBackBufferDir: CreateSurface ERROR res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
			return res;
		}
	}

	OutTraceDW("BuildBackBufferDir: created BACK DDSBack=%x\n", *lplpdds);
    if(IsDebug) DescribeSurface(*lplpdds, dxversion, "DDSBack", __LINE__);
	HookDDSurface(lplpdds, dxversion, FALSE); // added !!!
	iBakBufferVersion=dxversion; // v2.02.31

	return DD_OK;
}		

static HRESULT BuildGenericEmu(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	DDSURFACEDESC2 ddsd;
	HRESULT res;

	OutTraceDW("BuildGenericEmu: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	memcpy(&ddsd, lpddsd, lpddsd->dwSize); // Copy over ....
	FixSurfaceCaps(&ddsd, dxversion);
	// It looks that DDSCAPS_SYSTEMMEMORY surfaces can perfectly be DDSCAPS_3DDEVICE as well. 
	// For "Risk II" it is necessary that both the primary surface and the offscreen surfaces are generated
	// with the same type, so that assuming an identical lPitch and memcopy-ing from one buffer to the 
	// other is a legitimate operation. 

	if(dxw.dwFlags6 & POWER2WIDTH){ // v2.03.28: POWER2WIDTH to fix "Midtown Madness" in surface emulation mode
		if(((ddsd.dwFlags & (DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH)) == (DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH)) &&
			(ddsd.ddsCaps.dwCaps & DDSCAPS_OFFSCREENPLAIN)
		){
			DWORD dwWidth;
			dwWidth = ((ddsd.dwWidth + 3) >> 2) << 2;
			if(dwWidth != ddsd.dwWidth) OutTraceDW("BuildGenericEmu: POWER2WIDTH fix surface width %d->%d\n", ddsd.dwWidth, dwWidth);
			ddsd.dwWidth = dwWidth;
		}
	}

	res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, pu);
	// v2.04.05: error condition found & fixed on certain platforms for "Dominant Species" only (so far).
	if (((res == DDERR_UNSUPPORTED) || (res == DDERR_UNSUPPORTEDMODE)) && 
		(ddsd.ddsCaps.dwCaps & DDSCAPS_TEXTURE)){
		OutTraceDW("BuildGenericEmu: CreateSurface ERROR res=%x(%s) at %d, TEXTURE->OFFSCREENPLAIN retry\n", res, ExplainDDError(res), __LINE__);
		ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_TEXTURE|DDSCAPS_ALLOCONLOAD);
		ddsd.ddsCaps.dwCaps |= (DDSCAPS_OFFSCREENPLAIN);
		res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, pu);
	}	
	if ((dxw.dwFlags1 & SWITCHVIDEOMEMORY) && (res!=DD_OK)){
		OutTraceDW("BuildGenericEmu: CreateSurface ERROR res=%x(%s) at %d, retry\n", res, ExplainDDError(res), __LINE__);
		ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY;
		ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
		res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, pu);
	}
	if (res) {
		OutTraceE("BuildGenericEmu: CreateSurface ERROR res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
		return res;
	}

	OutTraceDW("BuildGenericEmu: CREATED lpddsd=%x version=%d %s\n", 
		*lplpdds, dxversion, LogSurfaceAttributes(&ddsd, "[Emu Generic]", __LINE__));
		
	// v2.02.66: if 8BPP paletized surface and a primary palette exixts, apply.
	// fixes "Virtua Fighter PC" palette bug
	if(lpDDP && (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8)){
		res=(*pSetPaletteMethod(dxversion))(*lplpdds, lpDDP);
		if(res)
			OutTraceE("BuildGenericEmu: SetPalette ERROR lpdds=%x res=%x(%s) at %d\n", *lplpdds, res, ExplainDDError(res), __LINE__);
		else {
			OutTraceDW("BuildGenericEmu: applied palette lpddp=%x to lpdds=%x\n", lpDDP, *lplpdds);
			iDDPExtraRefCounter++;
		}
	}

	// diagnostic hooks ....
	HookDDSurface(lplpdds, dxversion, FALSE);

	return DD_OK;
}

static HRESULT BuildGenericFlippable(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	DDSURFACEDESC2 ddsd;
	HRESULT res;

	OutTraceDW("BuildGenericFlippable: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	memcpy(&ddsd, lpddsd, lpddsd->dwSize); // Copy over ....
	FixSurfaceCaps(&ddsd, dxversion);

	if(dxw.dwFlags6 & POWER2WIDTH){ // v2.03.28: POWER2WIDTH to fix "Midtown Madness" in surface emulation mode
		if(((ddsd.dwFlags & (DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH)) == (DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH)) &&
			(ddsd.ddsCaps.dwCaps & DDSCAPS_OFFSCREENPLAIN)
		){
			DWORD dwWidth;
			dwWidth = ((ddsd.dwWidth + 3) >> 2) << 2;
			if(dwWidth != ddsd.dwWidth) OutTraceDW("BuildGenericFlippable: POWER2WIDTH fix surface width %d->%d\n", ddsd.dwWidth, dwWidth);
			ddsd.dwWidth = dwWidth;
		}
	}

	res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, pu);
	if ((dxw.dwFlags1 & SWITCHVIDEOMEMORY) && (res!=DD_OK)){
		OutTraceDW("BuildGenericFlippable: CreateSurface ERROR res=%x(%s) at %d, retry\n", res, ExplainDDError(res), __LINE__);
		ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY;
		ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
		res=(*pCreateSurface)(lpdd, &ddsd, lplpdds, pu);
	}
	if (res) {
		OutTraceE("BuildGenericFlippable: CreateSurface ERROR res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
		return res;
	}

	OutTraceDW("BuildGenericFlippable: CREATED lpddsd=%x version=%d %s\n", 
		*lplpdds, dxversion, LogSurfaceAttributes(&ddsd, "[Emu Generic]", __LINE__));
		
	// v2.02.66: if 8BPP paletized surface and a primary palette exixts, apply.
	// fixes "Virtua Fighter PC" palette bug
	if(lpDDP && (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8)){
		res=(*pSetPaletteMethod(dxversion))(*lplpdds, lpDDP);
		if(res)
			OutTraceE("BuildGenericFlippable: SetPalette ERROR on lpdds=%x res=%x(%s) at %d\n", *lplpdds, res, ExplainDDError(res), __LINE__);
		else {
			OutTraceDW("BuildGenericFlippable: applied palette lpddp=%x to lpdds=%x\n", lpDDP, *lplpdds);
			iDDPExtraRefCounter++;
		}
	}

	// diagnostic hooks ....
	HookDDSurface(lplpdds, dxversion, FALSE);

	return DD_OK;
}

static HRESULT BuildGenericDir(LPDIRECTDRAW lpdd, CreateSurface_Type pCreateSurface, LPDDSURFACEDESC2 lpddsd, int dxversion, LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	HRESULT res;

	OutTraceDW("BuildGenericDir: lpdd=%x pCreateSurface=%x lpddsd=%x version=%d\n", lpdd, pCreateSurface, lpddsd, dxversion);
	OutTraceDW("BuildGenericDir: %s\n", LogSurfaceAttributes(lpddsd, "[Dir Generic]", __LINE__));

	res = (*pCreateSurface)(lpdd, lpddsd, lplpdds, 0); 
	if(res){
		// v2.02.60: Ref. game Incoming GOG release, post by Marek, error DDERR_UNSUPPORTED while trying to create ZBUFFER surface 
		if ((dxw.dwFlags1 & SWITCHVIDEOMEMORY) && ((res==DDERR_OUTOFVIDEOMEMORY)||(res==DDERR_UNSUPPORTED))){
			OutTraceDW("BuildGenericDir: CreateSurface ERROR res=%x(%s) at %d, retry\n", res, ExplainDDError(res), __LINE__);
			lpddsd->ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY;
			lpddsd->ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
			res = (*pCreateSurface)(lpdd, lpddsd, lplpdds, 0); 
		}
		if(res){
			OutTraceE("BuildGenericDir: CreateSurface ERROR res=%x(%s) at %d\n", res, ExplainDDError(res), __LINE__);
			return res;
		}
	}

	OutTraceDW("BuildGenericDir: CREATED lpddsd=%x version=%d %s\n", 
		*lplpdds, dxversion, LogSurfaceAttributes(lpddsd, "[Dir Generic]", __LINE__));

	// hooks ....
	HookDDSurface(lplpdds, dxversion, FALSE);

	return DD_OK;
}

HRESULT WINAPI extCreateSurface(int dxversion, CreateSurface_Type pCreateSurface, LPDIRECTDRAW lpdd, DDSURFACEDESC2 *lpddsd,
	LPDIRECTDRAWSURFACE *lplpdds, void *pu)
{
	HRESULT res;
	DDSURFACEDESC2 ddsd;
	LPDIRECTDRAWSURFACE lpDDSPrim;
	LPDIRECTDRAWSURFACE lpDDSBack = NULL;
	DWORD CurFlags;
	int TargetSize;
	typedef HRESULT (*BuildSurface_Type)(LPDIRECTDRAW, CreateSurface_Type, LPDDSURFACEDESC2, int, LPDIRECTDRAWSURFACE *, void *);
	static BuildSurface_Type BuildPrimary = 0;
	static BuildSurface_Type BuildBackBuffer = 0;
	static BuildSurface_Type AttachBackBuffer = 0;
	static BuildSurface_Type BuildGeneric = 0;
	static enum  {
		PRIMARY_FULLSCREEN = 0,
		PRIMARY_DIRECT,
		PRIMARY_FLIPPABLE,	
		PRIMARY_EMULATED
	} SurfaceMode;

	OutTraceDDRAW("CreateSurface(%d): lpdd=%x %s\n", 
		dxversion, lpdd, LogSurfaceAttributes(lpddsd, "[CreateSurface]", __LINE__));
	
	// v2.03.95.fx1 - deleted: some texture handling REQUIRES a proper FourCC codec. 
	// maybe it could be suppressed by a dedicated config. flag and on primary surfaces only?
	//lpddsd->ddpfPixelFormat.dwFourCC = 0;

	// if not initialized yet ....
	if(BuildPrimary == 0){ 
		char *sLabel;
		SurfaceMode = PRIMARY_FULLSCREEN;
		if(dxw.IsEmulated || dxw.Windowize){
			SurfaceMode = (dxw.dwFlags1 & EMULATESURFACE) ? 
				((dxw.dwFlags6 & FLIPEMULATION) ? 
					PRIMARY_EMULATED : 
					PRIMARY_FLIPPABLE) : 
				PRIMARY_DIRECT;
		}

		switch(SurfaceMode)	{
			case PRIMARY_FULLSCREEN:
				BuildPrimary = BuildPrimaryFullscreen;
				BuildBackBuffer = BuildBackBufferFullscreen;
				AttachBackBuffer = AttachBackBufferFullscreen;
				BuildGeneric = BuildGenericDir;
				sLabel="FULLSCR";
				break;
			case PRIMARY_DIRECT: 
				BuildPrimary = BuildPrimaryDir;
				BuildBackBuffer = BuildBackBufferDir;
				AttachBackBuffer = BuildBackBufferDir;
				BuildGeneric = BuildGenericDir;
				sLabel="DIRECT";
				break;
			case PRIMARY_FLIPPABLE:
				BuildPrimary = BuildPrimaryFlippable;
				BuildBackBuffer = BuildBackBufferFlippable;
				AttachBackBuffer = AttachBackBufferFlippable;
				BuildGeneric = BuildGenericFlippable;
				sLabel="FLIPPABLE";
				break;
			case PRIMARY_EMULATED:
				BuildPrimary = BuildPrimaryEmu;
				BuildBackBuffer = BuildBackBufferEmu;
				AttachBackBuffer = BuildBackBufferEmu;
				BuildGeneric = BuildGenericEmu;
				sLabel="EMULATED";
				break;	
		}
		OutTraceDW("CreateSurface: MODE INITIALIZED mode=%d(%s)\n", SurfaceMode, sLabel);
	}

	// check for lpddsd->dwSize value
	TargetSize=(dxversion<4)?sizeof(DDSURFACEDESC):sizeof(DDSURFACEDESC2);
	if(lpddsd->dwSize != TargetSize){
		char sMsg[81];
		sprintf_s(sMsg,80, "CreateSurface: ASSERT bad dwSize=%d dxversion=%d\n", 
			lpddsd->dwSize, dxversion);
		OutTraceDW(sMsg);
		if(IsAssertEnabled) MessageBox(0, sMsg, "CreateSurface", MB_OK | MB_ICONEXCLAMATION);
		return DDERR_INVALIDPARAMS;
	}

	//GHO workaround (needed for WarWind, Rogue Spear):
	if (lpddsd->dwFlags && !(lpddsd->dwFlags & 0x1)){
		OutTraceDW("CreateSurface: fixing illegal dwFlags value: %x -> %x\n",
			lpddsd->dwFlags, (lpddsd->dwFlags | DDSD_CAPS));
		lpddsd->dwFlags |= DDSD_CAPS;
	}

	memcpy(&ddsd, lpddsd, lpddsd->dwSize); // Copy

	// v2.02.38: this is odd: in "Star Force Deluxe" there is no PRIMARY surface, but a surface with 
	// 0 flags and 0 capabilities serves for this purpose. Is it a side-effect of old ddraw releases?
	if((dxversion == 1) && ((ddsd.dwFlags & ~DDSD_BACKBUFFERCOUNT) == 0)){ // Star Force Deluxe
		ddsd.dwFlags = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH;
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
		//if(dxw.VirtualPixelFormat.dwRGBBitCount == 8) ddsd.ddsCaps.dwCaps |= DDSCAPS_PALETTE;
	}

	// creation of the primary surface....
	if(ddsd.dwFlags & DDSD_CAPS && ddsd.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE){
		dxwss.ClearSurfaceList(); // v2.03.83: "Daytona USA" would saturate the list otherwise
		SetVSyncDelays(dxversion, lpdd);
		GetHookInfo()->Height=(short)dxw.GetScreenHeight();
		GetHookInfo()->Width=(short)dxw.GetScreenWidth();
		GetHookInfo()->ColorDepth=(short)dxw.VirtualPixelFormat.dwRGBBitCount;
		GetHookInfo()->DXVersion=dxversion;
		lpPrimaryDD = lpdd; // v2.1.87
		memcpy(&DDSD_Prim, lpddsd, sizeof(DDSD_Prim)); // v2.02.37

		// beware of the different behaviour between older and newer directdraw releases...
		if(dxversion >= 4){
			if (lpDDSEmu_Back) while(lpDDSEmu_Back->Release());
			if (lpDDSEmu_Prim) while(lpDDSEmu_Prim->Release());
			if (ddsd.dwFlags & DDSD_BACKBUFFERCOUNT) { // Praetorians !!!!
				lpDDSBack = dxwss.GetBackBufferSurface();
				if (lpDDSBack) {
					if(dxw.dwFlags6 & FLIPEMULATION) while(lpDDSBack->Release());
					dxwss.PopSurface(lpDDSBack);
					lpDDSBack = NULL;
				}
			}
		}
		lpDDSEmu_Back=NULL;
		lpDDSEmu_Prim=NULL;

		int BBCount=0; // or 1 ??
		if (ddsd.dwFlags & DDSD_BACKBUFFERCOUNT) BBCount=ddsd.dwBackBufferCount;
		if ((BBCount > 0) && (iBakBufferVersion < 4)){ 
			lpDDSBack=NULL;
			OutTraceDW("CreateSurface: backbuffer cleared - BackBufferCount=%d\n", BBCount);
		}

		if (BBCount > MAXBACKBUFFERS){
			char sMsg[81];
			sprintf_s(sMsg, 80, "CreateSurface: BackBufferCount=%d\n", BBCount);
			OutTraceDW(sMsg);
			if (IsAssertEnabled) MessageBox(0, sMsg, "CreateSurface", MB_OK | MB_ICONEXCLAMATION);
			// recover ...
			BBCount = MAXBACKBUFFERS;
		}

		// build emulated primary surface, real primary and backbuffer surfaces
		CurFlags=ddsd.dwFlags;
		res=BuildPrimary(lpdd, pCreateSurface, lpddsd, dxversion, lplpdds, NULL);
		if(res) return res;
		lpDDSPrim = *lplpdds;
		dxwss.PushPrimarySurface(lpDDSPrim, dxversion, lpddsd->ddsCaps.dwCaps);
		RegisterPixelFormat(dxversion, lpDDSPrim);

		if (BBCount){
			DWORD dwCaps;
			// build emulated backbuffer surface
			res=AttachBackBuffer(lpdd, pCreateSurface, lpddsd, dxversion, &lpDDSBack, NULL);
			if(res) return res;
			// here we try to guess what sort of capabilities would expose a built-in backbuffer surface
			dwCaps = lpddsd->ddsCaps.dwCaps;
			dwCaps &= ~DDSCAPS_PRIMARYSURFACE;
			dwCaps |= (DDSCAPS_BACKBUFFER|DDSCAPS_VIDEOMEMORY);
			dxwss.PushBackBufferSurface(lpDDSBack, dxversion, dwCaps);
		}

		if(IsTraceDDRAW){
			char sInfo[256+1];
			sprintf(sInfo, "CreateSurface: created DDSPrim=%x DDSBack=%x", lpDDSPrim, lpDDSBack);
			if(dxw.IsEmulated) sprintf(sInfo, "%s DDSEmu_Prim=%x", sInfo, lpDDSEmu_Prim);
			if(dxw.dwFlags1 & EMULATESURFACE) sprintf(sInfo, "%s DDSEmu_Back=%x", sInfo, lpDDSEmu_Back);
			strcat(sInfo, "\n");
			OutTrace(sInfo);
		}
 
		// rebuild the clipper area
		if(dxw.dwFlags1 & CLIPCURSOR) dxw.SetClipCursor();

		// v2.2.64: added extra ref needed to preserve ddraw session for later use. Is it a ddraw1 legacy?
		// seems to fix problems in "Warhammer 40K Rites Of War" that uses a ddraw session after reaching 0 refcount.
		// v2.2.84: avoid the extra referenced in non windowed mode since it causes the window shift reported by gsky916
		// for Wind Fantasy SP.
		// v2.3.59: same extra reference is needed by "Wahammer Chaos Gate" that uses ddraw interface release 2
		// v2.3.72: fixed previous fix: condition is <=2, not >=2 ! 
		// Be aware that it may perhaps become <=3, if we get the same problem elsewhere
		// v2.3.96: deleted the if(Windowized) condition: AddRef is needed also in fullscreen mode.
		if(dxw.dwDDVersion<=2) lpdd->AddRef();

		return DD_OK;
	}

	// a request for a separate (not complex) backbuffer to attach later on, maybe.
	if ((ddsd.dwFlags & DDSD_CAPS) && (ddsd.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER)){
		if (lpDDSBack) {
			OutTraceDW("CreateSurface: returning current DDSBack=%x\n", lpDDSBack);
			*lplpdds = lpDDSBack;
			return DD_OK;
		}

		res=BuildBackBuffer(lpdd, pCreateSurface, lpddsd, dxversion, lplpdds, NULL);
		if(res == DD_OK) dxwss.PushBackBufferSurface(*lplpdds, dxversion, lpddsd->ddsCaps.dwCaps);
		return res;
	}

	// if nothing else, it's a generic/zbuffer surface
	// Note: should DxWnd trace a memory 3D surface that will be used as reference surface for D3D CreateDevice?

	res=BuildGeneric(lpdd, pCreateSurface, lpddsd, dxversion, lplpdds, pu);
	if(!res) {
		dxwss.PopSurface(*lplpdds);
		if(lpddsd->ddsCaps.dwCaps & DDSCAPS_ZBUFFER) {
			// v2.03.82: save ZBUFFER capabilities for later fix in D3D CreateDevice
			OutTraceDW("CreateSurface: lpDDZBuffer=%x save ZBUFFER caps=%x(%s)\n", *lplpdds, lpddsd->ddsCaps.dwCaps, ExplainDDSCaps(lpddsd->ddsCaps.dwCaps));
			lpDDZBuffer = *lplpdds;
			// save surface size expressed in bytes
			extern int ZBufferSize;
			ZBufferSize = lpddsd->dwWidth * lpddsd->dwHeight * (lpddsd->ddpfPixelFormat.dwZBufferBitDepth >> 3); 
			dxwss.PushZBufferSurface(*lplpdds, dxversion, lpddsd->ddsCaps.dwCaps);
			OutTraceDW("CreateSurface: ZBufferSize=%d\n", ZBufferSize);
		}
		else {
			if(lpddsd->ddsCaps.dwCaps & DDSCAPS_3DDEVICE) {
			OutTraceDW("CreateSurface: lpDD3DRef=%x save 3DREF caps=%x(%s)\n", *lplpdds, lpddsd->ddsCaps.dwCaps, ExplainDDSCaps(lpddsd->ddsCaps.dwCaps));
				dxwss.Push3DRefSurface(*lplpdds, dxversion, lpddsd->ddsCaps.dwCaps);
			}
		}
	}

	return res;
}