#define _WIN32_WINNT 0x0600
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_DEPRECATE 1

#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
#include <psapi.h>
#include "dxwnd.h"
#include "dxwcore.hpp"
#include "dxhook.h"
#include "glhook.h"
#include "glidehook.h"
#include "msvfwhook.h"
#define DXWDECLARATIONS 1
#include "syslibs.h"
#undef DXWDECLARATIONS
#include "dxhelper.h"
#include "Ime.h"
#include "Winnls32.h"
#include "Mmsystem.h"
#include "disasm.h"
#include "MinHook.h" 

#define SKIPIMEWINDOW TRUE

dxwCore dxw;
dxwSStack dxwss;
dxwWStack dxwws;

typedef char *(*Geterrwarnmessage_Type)(unsigned long, unsigned long);
typedef int (*Preparedisasm_Type)(void);
typedef void (*Finishdisasm_Type)(void);
typedef unsigned long (*Disasm_Type)(const unsigned char *, unsigned long, unsigned long, t_disasm *, int, t_config *, int (*)(tchar *, unsigned long));

Geterrwarnmessage_Type pGeterrwarnmessage;
Preparedisasm_Type pPreparedisasm;
Finishdisasm_Type pFinishdisasm;
Disasm_Type pDisasm;

extern void InitScreenParameters();
extern void *HotPatch(void *, const char *, void *);
extern void *IATPatch(HMODULE, char *, void *, const char *, void *);
void HookModule(HMODULE, int);
static void RecoverScreenMode();
static void LockScreenMode(DWORD, DWORD, DWORD);

extern HANDLE hTraceMutex;

CRITICAL_SECTION TraceCS; 

static char *FlagNames[32]={
	"UNNOTIFY", "EMULATESURFACE", "CLIPCURSOR", "RESETPRIMARY",
	"HOOKDI", "MODIFYMOUSE", "HANDLEEXCEPTIONS", "SAVELOAD",
	"EMULATEBUFFER", "AUTOMATIC", "BLITFROMBACKBUFFER", "SUPPRESSCLIPPING",
	"AUTOREFRESH", "FIXWINFRAME", "HIDEHWCURSOR", "SLOWDOWN",
	"ENABLECLIPPING", "LOCKWINSTYLE", "MAPGDITOPRIMARY", "FIXTEXTOUT",
	"KEEPCURSORWITHIN", "USERGB565", "SUPPRESSDXERRORS", "PREVENTMAXIMIZE",
	"LOCKEDSURFACE", "FIXPARENTWIN", "SWITCHVIDEOMEMORY", "CLIENTREMAPPING",
	"HANDLEALTF4", "LOCKWINPOS", "HOOKCHILDWIN", "MESSAGEPROC"
};

static char *Flag2Names[32]={
	"RECOVERSCREENMODE", "REFRESHONRESIZE", "BACKBUFATTACH", "MODALSTYLE",
	"KEEPASPECTRATIO", "INIT8BPP", "FORCEWINRESIZE", "INIT16BPP",
	"KEEPCURSORFIXED", "DISABLEGAMMARAMP", "DIFFERENTIALMOUSE", "FIXNCHITTEST",
	"LIMITFPS", "SKIPFPS", "SHOWFPS", "HIDEMULTIMONITOR",
	"TIMESTRETCH", "HOOKOPENGL", "WALLPAPERMODE", "SHOWHWCURSOR",
	"GDISTRETCHED", "SHOWFPSOVERLAY", "FAKEVERSION", "FULLRECTBLT",
	"NOPALETTEUPDATE", "SUPPRESSIME", "NOBANNER", "WINDOWIZE",
	"LIMITRESOURCES", "STARTDEBUG", "SETCOMPATIBILITY", "WIREFRAME",
};

static char *Flag3Names[32]={
	"FORCEHOOKOPENGL", "MARKBLIT", "HOOKDLLS", "SUPPRESSD3DEXT",
	"HOOKENABLED", "FIXD3DFRAME", "FORCE16BPP", "BLACKWHITE",
	"MARKLOCK", "SINGLEPROCAFFINITY", "EMULATEREGISTRY", "CDROMDRIVETYPE",
	"NOWINDOWMOVE", "--DISABLEHAL--", "LOCKSYSCOLORS", "GDIEMULATEDC",
	"FULLSCREENONLY", "FONTBYPASS", "YUV2RGB", "RGB2YUV",
	"BUFFEREDIOFIX", "FILTERMESSAGES", "PEEKALLMESSAGES", "SURFACEWARN",
	"ANALYTICMODE", "FORCESHEL", "CAPMASK", "COLORFIX",
	"NODDRAWBLT", "NODDRAWFLIP", "NOGDIBLT", "NOPIXELFORMAT",
};

static char *Flag4Names[32]={
	"NOALPHACHANNEL", "SUPPRESSCHILD", "FIXREFCOUNTER", "SHOWTIMESTRETCH",
	"ZBUFFERCLEAN", "ZBUFFER0CLEAN", "ZBUFFERALWAYS", "DISABLEFOGGING",
	"NOPOWER2FIX", "NOPERFCOUNTER", "BILINEAR2XFILTER", "INTERCEPTRDTSC",
	"LIMITSCREENRES", "NOFILLRECT", "HOOKGLIDE", "HIDEDESKTOP",
	"STRETCHTIMERS", "NOFLIPEMULATION", "NOTEXTURES", "RETURNNULLREF",
	"FINETIMING", "NATIVERES", "SUPPORTSVGA", "SUPPORTHDTV",
	"RELEASEMOUSE", "FRAMECOMPENSATION", "HOTPATCH", "ENABLEHOTKEYS",
	"HOTPATCHALWAYS", "NOD3DRESET", "OVERRIDEREGISTRY", "HIDECDROMEMPTY",
};

static char *Flag5Names[32]={
	"DIABLOTWEAK", "CLEARTARGET", "NOWINPOSCHANGES", "--NOSYSTEMMEMORY--",
	"NOBLT", "--NOSYSTEMEMULATED--", "DOFASTBLT", "AEROBOOST",
	"QUARTERBLT", "NOIMAGEHLP", "BILINEARFILTER", "REPLACEPRIVOPS",
	"REMAPMCI", "TEXTUREHIGHLIGHT", "TEXTUREDUMP", "TEXTUREHACK",
	"TEXTURETRANSP", "NORMALIZEPERFCOUNT", "HYBRIDMODE", "GDICOLORCONV",
	"INJECTSON", "ENABLESONHOOK", "FREEZEINJECTEDSON", "GDIMODE",
	"CENTERTOWIN", "STRESSRESOURCES", "MESSAGEPUMP", "TEXTUREFORMAT", 
	"--GSKYHACK", "LOCKRESERVEDPALETTE", "UNLOCKZORDER", "EASPORTSHACK",
};

static char *Flag6Names[32]={
	"FORCESWAPEFFECT", "LEGACYALLOC", "NODESTROYWINDOW", "NOMOVIES",
	"SUPPRESSRELEASE", "FIXMOVIESCOLOR", "WOW64REGISTRY", "DISABLEMAXWINMODE",
	"FIXPITCH", "POWER2WIDTH", "HIDETASKBAR", "ACTIVATEAPP",
	"NOSYSMEMPRIMARY", "NOSYSMEMBACKBUF", "", "",
	"", "", "", "",
	"", "", "", "",
	"", "", "", "",
	"", "", "", "",
};

static char *TFlagNames[32]={
	"OUTTRACE", "OUTDDRAWTRACE", "OUTWINMESSAGES", "OUTCURSORTRACE",
	"OUTPROXYTRACE", "DXPROXED", "ASSERTDIALOG", "OUTIMPORTTABLE",
	"OUTDEBUG", "OUTREGISTRY", "TRACEHOOKS", "OUTD3DTRACE",
	"OUTDXWINTRACE", "ADDTIMESTAMP", "OUTDEBUGSTRING", "ERASELOGFILE",
	"", "", "", "",
	"", "", "", "",
	"", "", "", "",
	"", "", "", "",
};

char *GetDxWndPath()
{
	static BOOL DoOnce = TRUE;
	static char sFolderPath[MAX_PATH];

	if(DoOnce){
		DWORD dwAttrib;		
		dwAttrib = GetFileAttributes("dxwnd.dll");
		//if (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
		//	MessageBox(0, "DXWND: ERROR can't locate itself", "ERROR", MB_OK | MB_ICONEXCLAMATION);
		//	exit(0);
		//}
		GetModuleFileName(GetModuleHandle("dxwnd"), sFolderPath, MAX_PATH);
		sFolderPath[strlen(sFolderPath)-strlen("dxwnd.dll")] = 0; // terminate the string just before "dxwnd.dll"
		DoOnce = FALSE;
	}

	return sFolderPath;
}

static void OutTraceHeader(FILE *fp)
{
	SYSTEMTIME Time;
	char Version[20+1];
	int i;
	DWORD dword;
	GetLocalTime(&Time);
	GetDllVersion(Version);
	fprintf(fp, "*** DxWnd %s log BEGIN: %02d-%02d-%04d %02d:%02d:%02d ***\n",
		Version, Time.wDay, Time.wMonth, Time.wYear, Time.wHour, Time.wMinute, Time.wSecond);
	fprintf(fp, "*** Flags= ");
	for(i=0, dword=dxw.dwFlags1;  i<32; i++, dword>>=1) if(dword & 0x1) fprintf(fp, "%s ", FlagNames[i]);
	for(i=0, dword=dxw.dwFlags2; i<32; i++, dword>>=1) if(dword & 0x1) fprintf(fp, "%s ", Flag2Names[i]);
	for(i=0, dword=dxw.dwFlags3; i<32; i++, dword>>=1) if(dword & 0x1) fprintf(fp, "%s ", Flag3Names[i]);
	for(i=0, dword=dxw.dwFlags4; i<32; i++, dword>>=1) if(dword & 0x1) fprintf(fp, "%s ", Flag4Names[i]);
	for(i=0, dword=dxw.dwFlags5; i<32; i++, dword>>=1) if(dword & 0x1) fprintf(fp, "%s ", Flag5Names[i]);
	for(i=0, dword=dxw.dwFlags6; i<32; i++, dword>>=1) if(dword & 0x1) fprintf(fp, "%s ", Flag6Names[i]);
	for(i=0, dword=dxw.dwTFlags; i<32; i++, dword>>=1) if(dword & 0x1) fprintf(fp, "%s ", TFlagNames[i]);
	fprintf(fp, "***\n");
}

#define DXWMAXLOGSIZE 4096

void OutTrace(const char *format, ...)
{
	va_list al;
	static char path[MAX_PATH];
	static FILE *fp=NULL; // GHO: thread safe???
	char sBuf[DXWMAXLOGSIZE+1];
	DWORD tFlags;
	static GetTickCount_Type pGetTick;

	// check global log flag
	if(!(dxw.dwTFlags & (OUTTRACE|OUTDEBUGSTRING))) return;
	tFlags = dxw.dwTFlags;
	dxw.dwTFlags = 0x0; // to avoid possible log recursion while loading C runtime libraries!!!

	WaitForSingleObject(hTraceMutex, INFINITE);
	if (fp == NULL){
		char *OpenMode = (tFlags & ERASELOGFILE) ? "w+" : "a+";
		pGetTick = GetTickCount; // save function pointer
		GetCurrentDirectory(MAX_PATH, path);
		strcat(path, "\\dxwnd.log");
		fp = fopen(path, OpenMode);
		if (fp==NULL){ // in case of error (e.g. current dir on unwritable CD unit)... 
			strcpy(path, GetDxWndPath());
			strcat(path, "\\dxwnd.log");
			fp = fopen(path, OpenMode);
		}
		if (fp==NULL)
			return; // last chance: do not log... 
		else 
			OutTraceHeader(fp);
	}
	va_start(al, format);
	//vfprintf(fp, format, al);
	vsprintf_s(sBuf, DXWMAXLOGSIZE, format, al);
	sBuf[DXWMAXLOGSIZE]=0; // just in case of log truncation
	va_end(al);
	if(tFlags & OUTTRACE) {
		if(tFlags & ADDTIMESTAMP) fprintf(fp, "%08.8d: ", (*pGetTick)());
		fputs(sBuf, fp);
		fflush(fp); 
	}
	if(tFlags & OUTDEBUGSTRING) OutputDebugString(sBuf);
	ReleaseMutex(hTraceMutex);

	dxw.dwTFlags = tFlags; // restore settings
}

#ifdef CHECKFORCOMPATIBILITYFLAGS
static BOOL CheckCompatibilityFlags()
{
	typedef DWORD (WINAPI *GetFileVersionInfoSizeA_Type)(LPCSTR, LPDWORD);
	typedef BOOL (WINAPI *GetFileVersionInfoA_Type)(LPCSTR, DWORD, DWORD, LPVOID);
	typedef BOOL (WINAPI *VerQueryValueA_Type)(LPCVOID, LPCSTR, LPVOID, PUINT);
	VerQueryValueA_Type pVerQueryValueA;
	GetFileVersionInfoA_Type pGetFileVersionInfoA;
	GetFileVersionInfoSizeA_Type pGetFileVersionInfoSizeA;

	HMODULE VersionLib;
	DWORD dwMajorVersion, dwMinorVersion;
	DWORD dwHandle = 0;
	int size;
	UINT len = 0;
    VS_FIXEDFILEINFO*   vsfi = NULL;
	OSVERSIONINFO vi;

	if(!(VersionLib=LoadLibrary("Version.dll"))) return FALSE;
	pGetFileVersionInfoA=(GetFileVersionInfoA_Type)GetProcAddress(VersionLib, "GetFileVersionInfoA");
	if(!pGetFileVersionInfoA) return FALSE;
	pGetFileVersionInfoSizeA=(GetFileVersionInfoSizeA_Type)GetProcAddress(VersionLib, "GetFileVersionInfoSizeA");
	if(!pGetFileVersionInfoSizeA) return FALSE;
	pVerQueryValueA=(VerQueryValueA_Type)GetProcAddress(VersionLib, "VerQueryValueA");
	if(!pVerQueryValueA) return FALSE;

	size = (*pGetFileVersionInfoSizeA)("kernel32.dll", &dwHandle);	
	BYTE* VersionInfo = new BYTE[size];
	(*pGetFileVersionInfoA)("kernel32.dll", dwHandle, size, VersionInfo);
    (*pVerQueryValueA)(VersionInfo, "\\", (void**)&vsfi, &len);
	dwMajorVersion=HIWORD(vsfi->dwProductVersionMS);
	dwMinorVersion=LOWORD(vsfi->dwProductVersionMS);
    delete[] VersionInfo;	
	vi.dwOSVersionInfoSize=sizeof(vi);
	GetVersionExA(&vi);
	if((vi.dwMajorVersion!=dwMajorVersion) || (vi.dwMinorVersion!=dwMinorVersion)) {
		MessageBox(NULL, "Compatibility settings detected!", "DxWnd", MB_OK);
		return TRUE;
	}
	return FALSE;
}
#endif

void OutTraceHex(BYTE *bBuf, int iLen)
{
	for(int i=0; i<iLen; i++) OutTrace("%02X ", *(bBuf++));
	OutTrace("\n");
}

static void dx_ToggleLogging()
{
	// toggle LOGGING
	if(dxw.dwTFlags & OUTTRACE){
		OutTraceDW("Toggle logging OFF\n");
		dxw.dwTFlags &= ~OUTTRACE;
	}
	else {
		dxw.dwTFlags |= OUTTRACE;
		OutTraceDW("Toggle logging ON\n");
	}
	GetHookInfo()->isLogging=(dxw.dwTFlags & OUTTRACE);
}

static void SuppressIMEWindow()
{
	OutTraceDW("WindowProc: SUPPRESS IME\n");
	typedef BOOL (WINAPI *ImmDisableIME_Type)(DWORD);
	ImmDisableIME_Type pImmDisableIME;
	HMODULE ImmLib;
	ImmLib=(*pLoadLibraryA)("Imm32");
	if(ImmLib){
		pImmDisableIME=(ImmDisableIME_Type)(*pGetProcAddress)(ImmLib,"ImmDisableIME");
		if(pImmDisableIME)(*pImmDisableIME)(-1);
		FreeLibrary(ImmLib);
	}
}

void HookDlls(HMODULE module)
{
	PIMAGE_NT_HEADERS pnth;
	PIMAGE_IMPORT_DESCRIPTOR pidesc;
	DWORD base, rva;
	PSTR impmodule;
	PIMAGE_THUNK_DATA ptname;
	extern char *SysNames[];

	base=(DWORD)module;
	OutTraceB("HookDlls: base=%x\n", base);
	__try{
		pnth = PIMAGE_NT_HEADERS(PBYTE(base) + PIMAGE_DOS_HEADER(base)->e_lfanew);
		if(!pnth) {
			OutTraceH("HookDlls: ERROR no pnth at %d\n", __LINE__);
			return;
		}
		rva = pnth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
		if(!rva) {
			OutTraceH("HookDlls: ERROR no rva at %d\n", __LINE__);
			return;
		}

		for(pidesc = (PIMAGE_IMPORT_DESCRIPTOR)(base + rva); pidesc->FirstThunk; pidesc++){
			HMODULE DllBase;
			int idx;
			extern HMODULE SysLibs[];

			impmodule = (PSTR)(base + pidesc->Name);

			// skip dxwnd and system dll
			if(!lstrcmpi(impmodule, "DxWnd")) continue; 
			idx=dxw.GetDLLIndex(impmodule);
			if(!lstrcmpi(impmodule,dxw.CustomOpenGLLib))idx=SYSLIBIDX_OPENGL;
			if(idx != -1) {
				DllBase=GetModuleHandle(impmodule);
				SysLibs[idx]=DllBase;
				OutTraceH("HookDlls: system module %s at %x\n", impmodule, DllBase);
				continue;
			}

			OutTraceH("HookDlls: ENTRY timestamp=%x module=%s forwarderchain=%x\n", 
				pidesc->TimeDateStamp, impmodule, pidesc->ForwarderChain);
			if(pidesc->OriginalFirstThunk) {
				ptname = (PIMAGE_THUNK_DATA)(base + (DWORD)pidesc->OriginalFirstThunk);
			}
			else{
				ptname = 0;
				OutTraceH("HookDlls: no PE OFTs - stripped module=%s\n", impmodule);
			}

			DllBase=GetModuleHandle(impmodule);
			if(DllBase) HookModule(DllBase, 0);
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{       
		OutTraceDW("HookDlls: EXCEPTION\n");
	}
	return;
}

void DumpImportTable(HMODULE module)
{
	PIMAGE_NT_HEADERS pnth;
	PIMAGE_IMPORT_DESCRIPTOR pidesc;
	DWORD base, rva;
	PSTR impmodule;
	PIMAGE_THUNK_DATA ptaddr;
	PIMAGE_THUNK_DATA ptname;
	PIMAGE_IMPORT_BY_NAME piname;

	base=(DWORD)module;
	OutTrace("DumpImportTable: base=%x\n", base);
	__try{
		pnth = PIMAGE_NT_HEADERS(PBYTE(base) + PIMAGE_DOS_HEADER(base)->e_lfanew);
		if(!pnth) {
			OutTrace("DumpImportTable: ERROR no pnth at %d\n", __LINE__);
			return;
		}
		rva = pnth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
		if(!rva) {
			OutTrace("DumpImportTable: ERROR no rva at %d\n", __LINE__);
			return;
		}
		pidesc = (PIMAGE_IMPORT_DESCRIPTOR)(base + rva);

		while(pidesc->FirstThunk){
			impmodule = (PSTR)(base + pidesc->Name);
			OutTrace("DumpImportTable: ENTRY timestamp=%x module=%s forwarderchain=%x\n", 
				pidesc->TimeDateStamp, impmodule, pidesc->ForwarderChain);
			if(pidesc->OriginalFirstThunk) {
				ptname = (PIMAGE_THUNK_DATA)(base + (DWORD)pidesc->OriginalFirstThunk);
			}
			else{
				ptname = 0;
				OutTrace("DumpImportTable: no PE OFTs - stripped module=%s\n", impmodule);
			}
			ptaddr = (PIMAGE_THUNK_DATA)(base + (DWORD)pidesc->FirstThunk);
			while(ptaddr->u1.Function){
				OutTrace("addr=%x", ptaddr->u1.Function); 
				ptaddr ++;
				if(ptname){
					if(!IMAGE_SNAP_BY_ORDINAL(ptname->u1.Ordinal)){
						piname = (PIMAGE_IMPORT_BY_NAME)(base + (DWORD)ptname->u1.AddressOfData);
						OutTrace(" hint=%x name=%s", piname->Hint, piname->Name);
						ptname ++;
					}
				}
				OutTrace("\n");
			}
			OutTrace("*** EOT ***\n", ptaddr->u1.Function); 
			pidesc ++;
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{       
		OutTraceDW("DumpImportTable: EXCEPTION\n");
	}
	return;
}

void SetHook(void *target, void *hookproc, void **hookedproc, char *hookname)
{
	void *tmp;
	char msg[81];
	DWORD dwTmp, oldprot;
	static DWORD MinHook=0xFFFFFFFF;
	static DWORD MaxHook=0;
	
	OutTraceH("SetHook: DEBUG target=%x, proc=%x name=%s\n", target, hookproc, hookname);
	// keep track of hooked call range to avoid re-hooking of hooked addresses !!!
	if ((DWORD)hookproc < MinHook) MinHook=(DWORD)hookproc;
	if ((DWORD)hookproc > MaxHook) MaxHook=(DWORD)hookproc;
	dwTmp = *(DWORD *)target;
	if(dwTmp == (DWORD)hookproc) return; // already hooked
	if((dwTmp <= MaxHook) && (dwTmp >= MinHook)) return; // already hooked
	if(dwTmp == 0){
		sprintf(msg,"SetHook ERROR: NULL target for %s\n", hookname);
		OutTraceDW(msg);
		MessageBox(0, msg, "SetHook", MB_OK | MB_ICONEXCLAMATION);
		return; // error condition
	}
	if(!VirtualProtect(target, 4, PAGE_READWRITE, &oldprot)) {
		sprintf(msg,"SetHook ERROR: target=%x err=%d\n", target, GetLastError());
		OutTraceDW(msg);
		if (IsAssertEnabled) MessageBox(0, msg, "SetHook", MB_OK | MB_ICONEXCLAMATION);
		return; // error condition
	}
	*(DWORD *)target = (DWORD)hookproc;
	if(!VirtualProtect(target, 4, oldprot, &oldprot)){
		OutTrace("SetHook: VirtualProtect ERROR target=%x, err=%x\n", target, GetLastError());
		return; // error condition
	}
	if(!FlushInstructionCache(GetCurrentProcess(), target, 4)){
		OutTrace("SetHook: FlushInstructionCache ERROR target=%x, err=%x\n", target, GetLastError());
		return; // error condition
	}
	tmp=(void *)dwTmp;

	if (*hookedproc && *hookedproc!=tmp) {
		sprintf(msg,"SetHook: proc=%s oldhook=%x newhook=%x\n", hookname, hookedproc, tmp);
		OutTraceDW(msg);
		if (IsAssertEnabled) MessageBox(0, msg, "SetHook", MB_OK | MB_ICONEXCLAMATION);
	}
	*hookedproc = tmp;
}

// v2.02.53: thorough scan - the IAT is scanned considering the possibility to have each dll module 
// replicated also many times. It may depend upon the compiling environment? 
// So far, it makes the difference for Dungeon Odissey

void *HookAPI(HMODULE module, char *dll, void *apiproc, const char *apiname, void *hookproc)
{
	if(dxw.dwTFlags & OUTIMPORTTABLE) OutTrace("HookAPI: module=%x dll=%s apiproc=%x apiname=%s hookproc=%x\n", 
		module, dll, apiproc, apiname, hookproc);

	if(!*apiname) { // check
		char *sMsg="HookAPI: NULL api name\n";
		OutTraceE(sMsg);
		if (IsAssertEnabled) MessageBox(0, sMsg, "HookAPI", MB_OK | MB_ICONEXCLAMATION);
		return 0;
	}

	if(dxw.dwFlags4 & HOTPATCHALWAYS) {
		void *orig;
		orig=HotPatch(apiproc, apiname, hookproc);
		if(orig) return orig;
	}

	return IATPatch(module, dll, apiproc, apiname, hookproc);
}

// v.2.1.80: unified positioning logic into CalculateWindowPos routine
// now taking in account for window menus (see "Alien Cabal")

void CalculateWindowPos(HWND hwnd, DWORD width, DWORD height, LPWINDOWPOS wp)
{
	RECT rect, desktop, workarea;
	DWORD dwStyle, dwExStyle;
	int MaxX, MaxY;
	HMENU hMenu;

	switch(dxw.Coordinates){
	case DXW_DESKTOP_CENTER:
		MaxX = dxw.iSizX;
		MaxY = dxw.iSizY;
		if (!MaxX) {
			MaxX = width;
			if(dxw.dwFlags4 & BILINEAR2XFILTER) MaxX <<= 1; // double
		}
		if (!MaxY) {
			MaxY = height;
			if(dxw.dwFlags4 & BILINEAR2XFILTER) MaxY <<= 1; // double
		}
		//GetClientRect(0, &desktop);
		(*pGetClientRect)(GetDesktopWindow(), &desktop);
		rect.left =  (desktop.right - MaxX) / 2;
		rect.top = (desktop.bottom - MaxY) / 2;
		rect.right = rect.left + MaxX;
		rect.bottom = rect.top + MaxY; //v2.02.09
		break;
	case DXW_DESKTOP_WORKAREA:
		(*pSystemParametersInfoA)(SPI_GETWORKAREA, NULL, &workarea, 0);
		rect = workarea;
		break;
	case DXW_DESKTOP_FULL:
		(*pGetClientRect)((*pGetDesktopWindow)(), &workarea);
		rect = workarea;
		break;
	case DXW_SET_COORDINATES:
	default:
		rect.left =  dxw.iPosX;
		rect.top = dxw.iPosY; //v2.02.09
		MaxX = dxw.iSizX;
		MaxY = dxw.iSizY;
		if (!MaxX) MaxX = width;
		if (!MaxY) MaxY = height;
		rect.right = dxw.iPosX + MaxX;
		rect.bottom = dxw.iPosY + MaxY; //v2.02.09
		break;
	}

	RECT UnmappedRect;
	UnmappedRect=rect;
	dwStyle=(*pGetWindowLongA)(hwnd, GWL_STYLE);
	dwExStyle=(*pGetWindowLongA)(hwnd, GWL_EXSTYLE);
	// BEWARE: from MSDN -  If the window is a child window, the return value is undefined. 
	hMenu = (dwStyle & WS_CHILD) ? NULL : GetMenu(hwnd);	
	AdjustWindowRectEx(&rect, dwStyle, (hMenu!=NULL), dwExStyle);
	if (hMenu) __try {CloseHandle(hMenu);} __except(EXCEPTION_EXECUTE_HANDLER){};
	switch(dxw.Coordinates){
	case DXW_DESKTOP_WORKAREA:
	case DXW_DESKTOP_FULL:
		// if there's a menu, reduce height to fit area
		if(rect.top != UnmappedRect.top){
			rect.bottom = rect.bottom - UnmappedRect.top + rect.top;
		}
		break;
	default:
		break;
	}

	// shift down-right so that the border is visible
	// and also update the iPosX,iPosY upper-left coordinates of the client area
	if(rect.left < 0){
		rect.right -= rect.left;
		rect.left = 0;
	}
	if(rect.top < 0){
		rect.bottom -= rect.top;
		rect.top = 0;
	}

	wp->x=rect.left;
	wp->y=rect.top;
	wp->cx=rect.right-rect.left;
	wp->cy=rect.bottom-rect.top;
}

void AdjustWindowPos(HWND hwnd, DWORD width, DWORD height)
{
	WINDOWPOS wp;
	OutTraceDW("AdjustWindowPos: hwnd=%x, size=(%d,%d)\n", hwnd, width, height);
	CalculateWindowPos(hwnd, width, height, &wp);
	OutTraceDW("AdjustWindowPos: fixed pos=(%d,%d) size=(%d,%d)\n", wp.x, wp.y, wp.cx, wp.cy);
	//if(!pSetWindowPos) pSetWindowPos=SetWindowPos;
	//OutTraceDW("pSetWindowPos=%x\n", pSetWindowPos);
	OutTraceDW("hwnd=%x pos=(%d,%d) size=(%d,%d)\n", pSetWindowPos, wp.x, wp.y, wp.cx, wp.cy);
	if(!(*pSetWindowPos)(hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, 0)){
		OutTraceE("AdjustWindowPos: ERROR err=%d at %d\n", GetLastError(), __LINE__);
	}

	if(dxw.dwFlags2 & SUPPRESSIME) SuppressIMEWindow();
	dxw.ShowBanner(hwnd);
	if(dxw.dwFlags4 & HIDEDESKTOP) dxw.HideDesktop(dxw.GethWnd());
	return;
}

void HookWindowProc(HWND hwnd)
{
	WNDPROC pWindowProc;
	pWindowProc = (WNDPROC)(*pGetWindowLongA)(hwnd, GWL_WNDPROC);
	// don't hook twice ....	
	if ((pWindowProc == extWindowProc) ||
		(pWindowProc == extChildWindowProc) ||
		(pWindowProc == extDialogWindowProc)){
		// hooked already !!!
		OutTraceDW("GetWindowLong: hwnd=%x WindowProc HOOK already in place\n", hwnd);
		return;
	}

	// v2.03.22: don't remap WindowProc in case of special address 0xFFFFnnnn. 
	// This makes "The Hulk demo" work avoiding WindowProc recursion and stack overflow
	if (((DWORD)pWindowProc & 0xFFFF0000) == 0xFFFF0000){
		OutTraceDW("GetWindowLong: hwnd=%x WindowProc HOOK %x not updated\n", hwnd, pWindowProc);
		return;
	}

	long lres;
	dxwws.PutProc(hwnd, pWindowProc);
	lres=(*pSetWindowLongA)(hwnd, GWL_WNDPROC, (LONG)extWindowProc);
	OutTraceDW("SetWindowLong: hwnd=%x HOOK WindowProc=%x->%x\n", hwnd, lres, (LONG)extWindowProc);
}

void AdjustWindowFrame(HWND hwnd, DWORD width, DWORD height)
{
	HRESULT res=0;
	LONG style;

	OutTraceDW("AdjustWindowFrame hwnd=%x, size=(%d,%d) coord=%d\n", hwnd, width, height, dxw.Coordinates); 

	dxw.SetScreenSize(width, height);
	if (hwnd==NULL) return;

	switch(dxw.Coordinates){
		case DXW_SET_COORDINATES:
		case DXW_DESKTOP_CENTER:
			style = (dxw.dwFlags2 & MODALSTYLE) ?  0 : WS_OVERLAPPEDWINDOW;
			break;
		case DXW_DESKTOP_WORKAREA:
		case DXW_DESKTOP_FULL:
			style = 0;
			break;
	}

	(*pSetWindowLongA)(hwnd, GWL_STYLE, style);
	(*pSetWindowLongA)(hwnd, GWL_EXSTYLE, 0); 
	(*pShowWindow)(hwnd, SW_SHOWNORMAL);
	OutTraceDW("AdjustWindowFrame hwnd=%x, set style=%s extstyle=0\n", hwnd, (style == 0) ? "0" : "WS_OVERLAPPEDWINDOW"); 
	AdjustWindowPos(hwnd, width, height);

	// fixing windows message handling procedure
	HookWindowProc(hwnd);

	// fixing cursor view and clipping region

	if ((dxw.dwFlags1 & HIDEHWCURSOR) && dxw.IsFullScreen()) while ((*pShowCursor)(0) >= 0);
	if (dxw.dwFlags2 & SHOWHWCURSOR) while((*pShowCursor)(1) < 0);
	if (dxw.dwFlags1 & CLIPCURSOR) {
		OutTraceDW("AdjustWindowFrame: setting clip region\n");
		dxw.SetClipCursor();
	}

	(*pInvalidateRect)(hwnd, NULL, TRUE);
}

LRESULT CALLBACK extDialogWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	static int i=0;
	static WINDOWPOS *wp;
	WNDPROC pWindowProc;

	static int t = -1;
	if (t == -1)
		t = (*pGetTickCount)();
	int tn = (*pGetTickCount)();

	OutTraceW("DEBUG: DialogWinMsg hwnd=%x msg=[0x%x]%s(%x,%x)\n", hwnd, message, ExplainWinMessage(message), wparam, lparam);

	// optimization: don't invalidate too often!
	// 200mSec seems a good compromise.
	if (tn-t > 200) {
		(*pInvalidateRect)(hwnd, NULL, TRUE);
		t=tn;
	}

	pWindowProc=dxwws.GetProc(hwnd);
	if(pWindowProc) return(*pCallWindowProcA)(pWindowProc, hwnd, message, wparam, lparam);
	char *sMsg="ASSERT: DialogWinMsg pWindowProc=NULL !!!\n";
	OutTraceDW(sMsg);
	if (IsAssertEnabled) MessageBox(0, sMsg, "WindowProc", MB_OK | MB_ICONEXCLAMATION);
	return NULL;
}

LRESULT CALLBACK extChildWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	static int i=0;
	static WINDOWPOS *wp;
	WNDPROC pWindowProc;

	OutTraceW("DEBUG: ChildWinMsg hwnd=%x msg=[0x%x]%s(%x,%x)\n", hwnd, message, ExplainWinMessage(message), wparam, lparam);

	if(dxw.Windowize){
		switch(message){
		// Cybermercs: it seems that all game menus are conveniently handled by the WindowProc routine,
		// while the action screen get messages processed by the ChildWindowProc, that needs some different
		// setting ..........
		// Beware: Cybermercs handles some static info about cursor position handling, so that if you resize
		// a menu it doesn't work correctly until you don't change screen.
		case WM_MOUSEMOVE:
		case WM_MOUSEWHEEL:
		case WM_LBUTTONDOWN:
		case WM_LBUTTONUP:
		case WM_LBUTTONDBLCLK:
		case WM_RBUTTONDOWN:
		case WM_RBUTTONUP:
		case WM_RBUTTONDBLCLK:
		case WM_MBUTTONDOWN:
		case WM_MBUTTONUP:
		case WM_MBUTTONDBLCLK:
			if(dxw.dwFlags1 & MODIFYMOUSE){ // mouse processing  
				POINT prev, curr;
				// scale mouse coordinates
				prev.x = LOWORD(lparam);
				prev.y = HIWORD(lparam);
				curr = prev;
				if(message == WM_MOUSEWHEEL){ // v2.02.33 mousewheel fix
					POINT upleft={0,0};
					(*pClientToScreen)(dxw.GethWnd(), &upleft);
					curr = dxw.SubCoordinates(curr, upleft);
				}
				//OutTraceC("ChildWindowProc: hwnd=%x pos XY prev=(%d,%d)\n", hwnd, prev.x, prev.y);
				curr=dxw.FixCursorPos(curr); // Warn! the correction must refer to the main window hWnd, not the current hwnd one !!!
				lparam = MAKELPARAM(curr.x, curr.y); 
				OutTraceC("ChildWindowProc: hwnd=%x pos XY=(%d,%d)->(%d,%d)\n", hwnd, prev.x, prev.y, curr.x, curr.y);
			}
			break;	
		default:
			break;
		}
	}

	pWindowProc=dxwws.GetProc(hwnd);
	
	// v2.02.82: use CallWindowProc that handles WinProc handles
	if(pWindowProc) return(*pCallWindowProcA)(pWindowProc, hwnd, message, wparam, lparam);
	// should never get here ....
	OutTraceDW("ChildWindowProc: no WndProc for CHILD hwnd=%x\n", hwnd);
	return DefWindowProc(hwnd, message, wparam, lparam);
}

static void dx_UpdatePositionLock(HWND hwnd)
{
	RECT rect;
	POINT p={0,0};
	(*pGetClientRect)(hwnd,&rect);
	(*pClientToScreen)(hwnd,&p);
	dxw.dwFlags1 |= LOCKWINPOS;
	OutTraceDW("Toggle position lock ON\n");
	dxw.InitWindowPos(p.x, p.y, rect.right-rect.left, rect.bottom-rect.top);
}

static void dx_TogglePositionLock(HWND hwnd)
{
	// toggle position locking
	if(dxw.dwFlags1 & LOCKWINPOS){
		// unlock
		OutTraceDW("Toggle position lock OFF\n");
		dxw.dwFlags1 &= ~LOCKWINPOS;
	}
	else {
		OutTraceDW("Toggle position lock ON\n");
		dxw.dwFlags1 |= LOCKWINPOS;
		dx_UpdatePositionLock(hwnd);
	}
}

static void dx_ToggleFPS()
{
	if(dxw.dwFlags2 & SHOWFPS){
		dxw.dwFlags2 &= ~SHOWFPS;
		OutTrace("ToggleFPS: SHOWFPS mode OFF\n");
	}
	else {
		dxw.dwFlags2 |= SHOWFPS;
		OutTrace("ToggleFPS: SHOWFPS mode ON\n");
	}
}

LRESULT LastCursorPos;

LRESULT CALLBACK extWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	POINT prev, curr;
	RECT rect;
	static int i=0;
	static int ClipCursorToggleState = 1;
	WNDPROC pWindowProc;
	extern void dxwFixWindowPos(char *, HWND, LPARAM);
	extern LPRECT lpClipRegion;
	static BOOL DoOnce = TRUE;
	static BOOL IsToBeLocked;
	static int LastTimeShift;
	static int SaveTimeShift;
	static BOOL TimeShiftToggle=TRUE;
	extern void DDrawScreenShot();

	if(DoOnce){
		DoOnce=FALSE;
		IsToBeLocked=(dxw.dwFlags1 & LOCKWINPOS);
		LastTimeShift=SaveTimeShift=dxw.TimeShift;
	}

	// v2.1.93: adjust clipping region

	if(IsTraceW){
		char sPos[161];
		sPos[160]=0;
		sPos[0]=0;
		switch(message){
		case WM_WINDOWPOSCHANGING:
		case WM_WINDOWPOSCHANGED:
			LPWINDOWPOS wp;
			wp = (LPWINDOWPOS)lparam;
			sprintf_s(sPos, 160, " pos=(%d,%d) size=(%dx%d) flags=%x(%s)", wp->x, wp->y, wp->cx, wp->cy, wp->flags, ExplainWPFlags(wp->flags));
			break;
		case WM_MOVE:
			sprintf_s(sPos, 160, " pos=(%d,%d)", HIWORD(lparam), LOWORD(lparam));
			break;
		case WM_SIZE:
			static char *modes[5]={"RESTORED", "MINIMIZED", "MAXIMIZED", "MAXSHOW", "MAXHIDE"};
			sprintf_s(sPos, 160, " mode=SIZE_%s size=(%dx%d)", modes[wparam % 5], HIWORD(lparam), LOWORD(lparam));
			break;	
		}
		OutTrace("WindowProc[%x]: WinMsg=[0x%x]%s(%x,%x) %s\n", hwnd, message, ExplainWinMessage(message), wparam, lparam, sPos);
	}

	if(dxw.dwFlags3 & FILTERMESSAGES){
		switch(message){
		case WM_NCMOUSEMOVE:
		case WM_NCLBUTTONDOWN:
		case WM_NCLBUTTONUP:
		case WM_NCLBUTTONDBLCLK:
		case WM_NCRBUTTONDOWN:
		case WM_NCRBUTTONUP:
		case WM_NCRBUTTONDBLCLK:
		case WM_NCMBUTTONDOWN:
		case WM_NCMBUTTONUP:
		case WM_NCMBUTTONDBLCLK:
			OutTraceDW("WindowProc[%x]: SUPPRESS WinMsg=[0x%x]%s(%x,%x)\n", hwnd, message, ExplainWinMessage(message), wparam, lparam);
			return 0;
		}
	}

	if(LastTimeShift != dxw.TimeShift){
		extern void SetVSyncDelays(LPDIRECTDRAW);
		extern LPDIRECTDRAW lpPrimaryDD;
		if(dxw.dwFlags4 & STRETCHTIMERS) dxw.RenewTimers();
		if(lpPrimaryDD) SetVSyncDelays(lpPrimaryDD);
		LastTimeShift=dxw.TimeShift;
	}

	switch(message){
	// v2.02.13: added WM_GETMINMAXINFO/WM_NCCALCSIZE interception - (see Actua Soccer 3 problems...)
	//case WM_NCDESTROY:
	//	return 0;
	case WM_GETMINMAXINFO: 
		if(dxw.dwFlags1 & LOCKWINPOS){
			extern void dxwFixMinMaxInfo(char *, HWND, LPARAM);
			dxwFixMinMaxInfo("WindowProc", hwnd, lparam);
			return 0;
		}
		break;
	case WM_NCCALCSIZE:
	case WM_NCPAINT:
		if((dxw.dwFlags1 & LOCKWINPOS) && (hwnd == dxw.GethWnd()) && dxw.IsFullScreen()){ // v2.02.30: don't alter child and other windows....
			OutTraceDW("WindowProc: %s wparam=%x\n", ExplainWinMessage(message), wparam);
			return (*pDefWindowProcA)(hwnd, message, wparam, lparam);
		}
		break;
	case WM_NCCREATE:
		if(dxw.dwFlags2 & SUPPRESSIME) SuppressIMEWindow();
		break;
	case WM_IME_SETCONTEXT:
	case WM_IME_NOTIFY:
	case WM_IME_CONTROL:
	case WM_IME_COMPOSITIONFULL:
	case WM_IME_SELECT:
	case WM_IME_CHAR:
	case WM_IME_REQUEST:
	case WM_IME_KEYDOWN:
	case WM_IME_KEYUP:
		if(dxw.dwFlags2 & SUPPRESSIME){
			OutTraceDW("WindowProc[%x]: SUPPRESS IME WinMsg=[0x%x]%s(%x,%x)\n", hwnd, message, ExplainWinMessage(message), wparam, lparam);
			return 0;
		}
		break;
	case WM_NCHITTEST:
		if((dxw.dwFlags2 & FIXNCHITTEST) && (dxw.dwFlags1 & MODIFYMOUSE)){ // mouse processing 
			POINT cursor;
			LRESULT ret;
			ret=(*pDefWindowProcA)(hwnd, message, wparam, lparam);
			if (ret==HTCLIENT) {
				cursor.x=LOWORD(lparam);
				cursor.y=HIWORD(lparam);
				dxw.FixNCHITCursorPos(&cursor);
				lparam = MAKELPARAM(cursor.x, cursor.y); 
			}
			else
				return ret;
		}
		break;
	case WM_ERASEBKGND:
		if(dxw.Windowize && dxw.IsDesktop(hwnd)){ 
			OutTraceDW("WindowProc: WM_ERASEBKGND(%x,%x) - suppressed\n", wparam, lparam);
			return 1; // 1 == OK, erased
		}
		break;
	case WM_DISPLAYCHANGE:
		if (dxw.Windowize && (dxw.dwFlags1 & LOCKWINPOS) && dxw.IsFullScreen()){
			OutTraceDW("WindowProc: prevent WM_DISPLAYCHANGE depth=%d size=(%d,%d)\n",
				wparam, LOWORD(lparam), HIWORD(lparam));
			// v2.02.43: unless EMULATESURFACE is set, lock the screen resolution only, but not the color depth!
			if(dxw.dwFlags1 & EMULATESURFACE) return 0;
			// let rparam (color depth) change, but override lparam (screen width & height.)
			lparam = MAKELPARAM((LONG)dxw.GetScreenWidth(), (LONG)dxw.GetScreenHeight());
			//return 0;
		}
		break;
	case WM_WINDOWPOSCHANGING:
	case WM_WINDOWPOSCHANGED:
		if(dxw.Windowize && dxw.IsFullScreen()){
			if(dxw.dwFlags5 & NOWINPOSCHANGES){
				OutTraceDW("WindowProc: %s - suppressed\n", message==WM_WINDOWPOSCHANGED ? "WM_WINDOWPOSCHANGED" : "WM_WINDOWPOSCHANGING");
				return 0;
			}
			extern HWND hControlParentWnd;
			LPWINDOWPOS wp;
			wp = (LPWINDOWPOS)lparam;
			dxwFixWindowPos("WindowProc", hwnd, lparam);
			OutTraceDW("WindowProc: %s fixed size=(%d,%d)\n", 
				(message == WM_WINDOWPOSCHANGED) ? "WM_WINDOWPOSCHANGED" : "WM_WINDOWPOSCHANGING", wp->cx, wp->cy);
			// try to lock main wind & control parent together
			if((message==WM_WINDOWPOSCHANGED) && hControlParentWnd){
				if(dxw.IsDesktop(hwnd)) {
					POINT fo = dxw.GetFrameOffset();
					(*pMoveWindow)(hControlParentWnd, wp->x+fo.x, wp->y+fo.y, wp->cx, wp->cy, TRUE);
				}
			}
			// v2.03.30: in window mode, it seems that the WM_ACTIVATEAPP message is not sent to the main win.
			// this PostMessage call recovers "Thorgal" block at the end of intro movie and "Championship Manager 03 04" cursor
			if((message==WM_WINDOWPOSCHANGED) && (dxw.dwFlags6 & ACTIVATEAPP)){
				PostMessage(hwnd, WM_ACTIVATEAPP, 1, 0);
			}
		}
		break;
	case WM_ENTERSIZEMOVE:
		if(IsToBeLocked){
			dxw.dwFlags1 &= ~LOCKWINPOS;
		}
		while((*pShowCursor)(1) < 0);
		if(dxw.dwFlags1 & CLIPCURSOR) dxw.EraseClipCursor();
		if(dxw.dwFlags1 & ENABLECLIPPING) (*pClipCursor)(NULL);
		break;
	case WM_EXITSIZEMOVE:
		if(IsToBeLocked){
			dxw.dwFlags1 |= LOCKWINPOS;
			dx_UpdatePositionLock(hwnd);
		}
		if((dxw.dwFlags1 & HIDEHWCURSOR) && dxw.IsFullScreen()) while((*pShowCursor)(0) >= 0);
		if(dxw.dwFlags2 & SHOWHWCURSOR) while((*pShowCursor)(1) < 0);
		if(dxw.dwFlags1 & ENABLECLIPPING) extClipCursor(lpClipRegion);
		if(dxw.dwFlags2 & REFRESHONRESIZE) dxw.ScreenRefresh();
		if(dxw.dwFlags4 & HIDEDESKTOP) dxw.HideDesktop(dxw.GethWnd());
		if(dxw.dwFlags5 & CENTERTOWIN) {
			HDC thdc;
			HWND w = dxw.GethWnd();
			RECT client;
			(*pGetClientRect)(w, &client);
			thdc=(*pGDIGetDC)(w);
			if(thdc) (*pGDIBitBlt)(thdc, client.left, client.top, client.right, client.bottom, 0, 0, 0, BLACKNESS);
		}
		break;
	case WM_ACTIVATE:
		// turn DirectInput bActive flag on & off .....
		dxw.bActive = (LOWORD(wparam) == WA_ACTIVE || LOWORD(wparam) == WA_CLICKACTIVE) ? 1 : 0;
	case WM_NCACTIVATE:
		// turn DirectInput bActive flag on & off .....
		if(message == WM_NCACTIVATE) dxw.bActive = wparam;
		(*pSetWindowPos)(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
		if(dxw.dwFlags1 & UNNOTIFY){
			DefWindowProc(hwnd, message, wparam, lparam);
			return false;
		}
		break;
	case WM_NCMOUSEMOVE:
		// Posted to a window when the cursor is moved within the nonclient area of the window. 
		// This message is posted to the window that contains the cursor. 
		// If a window has captured the mouse, this message is not posted.
		// V2.1.90: on nonclient areas the cursor is always shown.
		while((*pShowCursor)(1) < 0);
		break;
	case WM_MOUSEMOVE:
		if(dxw.Windowize){
			prev.x = LOWORD(lparam);
			prev.y = HIWORD(lparam);
			if ((dxw.dwFlags1 & HIDEHWCURSOR) && dxw.IsFullScreen()){
				(*pGetClientRect)(hwnd, &rect);
				if(prev.x >= 0 && prev.x < rect.right && prev.y >= 0 && prev.y < rect.bottom)
					while((*pShowCursor)(0) >= 0);
				else
					while((*pShowCursor)(1) < 0);
			}
			else {
				while((*pShowCursor)(1) < 0);
			}
			if(dxw.dwFlags1 & MODIFYMOUSE){ // mouse processing 
				// scale mouse coordinates
				curr=dxw.FixCursorPos(prev); //v2.02.30
				lparam = MAKELPARAM(curr.x, curr.y); 
				OutTraceC("WindowProc: hwnd=%x pos XY=(%d,%d)->(%d,%d)\n", hwnd, prev.x, prev.y, curr.x, curr.y);
			}
			GetHookInfo()->CursorX=LOWORD(lparam);
			GetHookInfo()->CursorY=HIWORD(lparam);
		}
		break;	
	// fall through cases:
	case WM_MOUSEWHEEL:
	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_LBUTTONDBLCLK:
	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
	case WM_RBUTTONDBLCLK:
	case WM_MBUTTONDOWN:
	case WM_MBUTTONUP:
	case WM_MBUTTONDBLCLK:
		if(dxw.Windowize){
			if((dxw.dwFlags1 & CLIPCURSOR) && ClipCursorToggleState) dxw.SetClipCursor();
			if(dxw.dwFlags1 & MODIFYMOUSE){ // mouse processing 
				// scale mouse coordinates
				prev.x = LOWORD(lparam);
				prev.y = HIWORD(lparam);
				curr = prev;
				if(message == WM_MOUSEWHEEL){ // v2.02.33 mousewheel fix
					POINT upleft={0,0};
					(*pClientToScreen)(dxw.GethWnd(), &upleft);
					curr = dxw.SubCoordinates(curr, upleft);
				}
				curr=dxw.FixCursorPos(curr); //v2.02.30
				lparam = MAKELPARAM(curr.x, curr.y); 
				OutTraceC("WindowProc: hwnd=%x pos XY=(%d,%d)->(%d,%d)\n", hwnd, prev.x, prev.y, curr.x, curr.y);
			}
			GetHookInfo()->CursorX=LOWORD(lparam);
			GetHookInfo()->CursorY=HIWORD(lparam);
		}
		break;	
	case WM_SETFOCUS:
		OutTraceDW("WindowProc: hwnd=%x GOT FOCUS\n", hwnd);
		if (dxw.dwFlags1 & ENABLECLIPPING) extClipCursor(lpClipRegion);
		break;
	case WM_KILLFOCUS:
		OutTraceDW("WindowProc: hwnd=%x LOST FOCUS\n", hwnd);
		if (dxw.dwFlags1 & CLIPCURSOR) dxw.EraseClipCursor();
		if (dxw.dwFlags1 & ENABLECLIPPING) (*pClipCursor)(NULL);
		break;
	case WM_CLOSE:
		// Beware: closing main window does not always mean that the program is about to terminate!!!
		extern void gShowHideTaskBar(BOOL);
		if(dxw.dwFlags6 & CONFIRMONCLOSE){
			OutTraceDW("WindowProc: WM_CLOSE - terminating process?\n");
			if (MessageBoxA(NULL, "Do you really want to exit the game?", "DxWnd", MB_YESNO | MB_TASKMODAL) != IDYES) return FALSE;
		}			
		if(dxw.dwFlags6 & HIDETASKBAR) gShowHideTaskBar(FALSE);
		if(dxw.dwFlags3 & FORCE16BPP) RecoverScreenMode();
		if(dxw.dwFlags6 & TERMINATEONCLOSE) TerminateProcess(GetCurrentProcess(),0);
		break;
	case WM_SYSKEYDOWN:
	case WM_KEYDOWN:
		if(!(dxw.dwFlags4 & ENABLEHOTKEYS)) break;
		OutTraceW("event %s wparam=%x lparam=%x\n", (message==WM_SYSKEYDOWN)?"WM_SYSKEYDOWN":"WM_KEYDOWN", wparam, lparam);
		UINT DxWndKey;
		DxWndKey=dxw.MapKeysConfig(message, lparam, wparam);
		switch (DxWndKey){
		case DXVK_CLIPTOGGLE:
			if(dxw.dwFlags1 & CLIPCURSOR){
				OutTraceDW("WindowProc: WM_SYSKEYDOWN key=%x ToggleState=%x\n",wparam,ClipCursorToggleState);
				ClipCursorToggleState = !ClipCursorToggleState;
				ClipCursorToggleState ? dxw.SetClipCursor() : dxw.EraseClipCursor();
			}
			break;
		case DXVK_REFRESH:
			dxw.ScreenRefresh();
			break;
		case DXVK_LOGTOGGLE:
			dx_ToggleLogging();
			break;
		case DXVK_PLOCKTOGGLE:
			dx_TogglePositionLock(hwnd);
			break;
		case DXVK_FPSTOGGLE:
			dx_ToggleFPS();
			break;
		case DXVK_TIMEFAST:
		case DXVK_TIMESLOW:
			if (dxw.dwFlags2 & TIMESTRETCH) {
				if (DxWndKey == DXVK_TIMESLOW && (dxw.TimeShift <  8)) dxw.TimeShift++;
				if (DxWndKey == DXVK_TIMEFAST && (dxw.TimeShift > -8)) dxw.TimeShift--;
				GetHookInfo()->TimeShift=dxw.TimeShift;
				OutTrace("Time Stretch: shift=%d speed=%s\n", dxw.TimeShift, dxw.GetTSCaption());
			}
			break;
		case DXVK_TIMETOGGLE:
			if (dxw.dwFlags2 & TIMESTRETCH) {
				if(TimeShiftToggle){
					SaveTimeShift=dxw.TimeShift;
					dxw.TimeShift=0;
				}
				else{
					dxw.TimeShift=SaveTimeShift;
				}
				TimeShiftToggle = !TimeShiftToggle;
				GetHookInfo()->TimeShift=dxw.TimeShift;
		}
			break;
		case DXVK_ALTF4:
			if (dxw.dwFlags1 & HANDLEALTF4) {
				OutTraceDW("WindowProc: WM_SYSKEYDOWN(ALT-F4) - terminating process\n");
				TerminateProcess(GetCurrentProcess(),0);
			}
			break;
		case DXVK_PRINTSCREEN:
			DDrawScreenShot();
			break;
		default:
			break;
		}
	default:
		break;
	}
	if (dxw.dwFlags1 & AUTOREFRESH) dxw.ScreenRefresh();

	pWindowProc=dxwws.GetProc(hwnd);
	//OutTraceB("WindowProc: pWindowProc=%x extWindowProc=%x message=%x(%s) wparam=%x lparam=%x\n", 
	//	(*pWindowProc), extWindowProc, message, ExplainWinMessage(message), wparam, lparam);
	if(pWindowProc) {
		LRESULT ret;

		// v2.02.36: use CallWindowProc that handles WinProc handles
		ret=(*pCallWindowProcA)(pWindowProc, hwnd, message, wparam, lparam);

		// save last NCHITTEST cursor position for use with KEEPASPECTRATIO scaling
		if(message==WM_NCHITTEST) LastCursorPos=ret;
		// v2.1.89: if FORCEWINRESIZE add standard processing for the missing WM_NC* messages
		if(dxw.dwFlags2 & FORCEWINRESIZE){ 
			switch(message){
			//case WM_NCHITTEST:
			//case WM_NCPAINT:
			//case WM_NCMOUSEMOVE:
			//case WM_NCCALCSIZE:
			//case WM_NCACTIVATE:
			case WM_SETCURSOR:		// shows a different cursor when moving on borders
			case WM_NCLBUTTONDOWN:	// intercepts mouse down on borders
			case WM_NCLBUTTONUP:	// intercepts mouse up on borders
				ret=(*pDefWindowProcA)(hwnd, message, wparam, lparam);
				break;
			}
		}

		return ret;
	}

	//OutTraceDW("ASSERT: WindowProc mismatch hwnd=%x\n", hwnd);
	// ??? maybe it's a normal condition, whenever you don't have a WindowProc routine
	// like in Commandos 2. Flag it?
	char sMsg[81];
	sprintf(sMsg,"ASSERT: WindowProc mismatch hwnd=%x\n", hwnd);
	OutTraceDW(sMsg);
	if (IsAssertEnabled) MessageBox(0, sMsg, "WindowProc", MB_OK | MB_ICONEXCLAMATION);	
	return (*pDefWindowProcA)(hwnd, message, wparam, lparam);
}

void HookSysLibsInit()
{
	static BOOL DoOnce = FALSE;
	if(DoOnce) return;
	DoOnce=TRUE;

	HookKernel32Init();
	HookUser32Init();
	HookGDI32Init();
	HookImagehlpInit();
}

DEVMODE InitDevMode;

static void SaveScreenMode()
{
	static BOOL DoOnce=FALSE;
	if(DoOnce) return;
	DoOnce=TRUE;
	(*pEnumDisplaySettings)(NULL, ENUM_CURRENT_SETTINGS, &InitDevMode);
	OutTraceDW("DXWND: Initial display mode WxH=(%dx%d) BitsPerPel=%d\n", 
		InitDevMode.dmPelsWidth, InitDevMode.dmPelsHeight, InitDevMode.dmBitsPerPel);
}

static void RecoverScreenMode()
{
	DEVMODE CurrentDevMode;
	BOOL res;
	(*pEnumDisplaySettings)(NULL, ENUM_CURRENT_SETTINGS, &CurrentDevMode);
	OutTraceDW("ChangeDisplaySettings: recover CURRENT WxH=(%dx%d) BitsPerPel=%d TARGET WxH=(%dx%d) BitsPerPel=%d\n", 
		CurrentDevMode.dmPelsWidth, CurrentDevMode.dmPelsHeight, CurrentDevMode.dmBitsPerPel,
		InitDevMode.dmPelsWidth, InitDevMode.dmPelsHeight, InitDevMode.dmBitsPerPel);
	InitDevMode.dmFields = (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT);
	res=(*pChangeDisplaySettingsExA)(NULL, &InitDevMode, NULL, 0, NULL);
	if(res) OutTraceE("ChangeDisplaySettings: ERROR err=%d at %d\n", GetLastError(), __LINE__);
}

void SwitchTo16BPP()
{
	DEVMODE CurrentDevMode;
	BOOL res;
	(*pEnumDisplaySettings)(NULL, ENUM_CURRENT_SETTINGS, &CurrentDevMode);
	OutTraceDW("ChangeDisplaySettings: CURRENT wxh=(%dx%d) BitsPerPel=%d -> 16\n", 
		CurrentDevMode.dmPelsWidth, CurrentDevMode.dmPelsHeight, CurrentDevMode.dmBitsPerPel);
	CurrentDevMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
	CurrentDevMode.dmBitsPerPel = 16;
	res=(*pChangeDisplaySettingsExA)(NULL, &CurrentDevMode, NULL, CDS_UPDATEREGISTRY, NULL);
	if(res) OutTraceE("ChangeDisplaySettings: ERROR err=%d at %d\n", GetLastError(), __LINE__);
}

static void LockScreenMode(DWORD dmPelsWidth, DWORD dmPelsHeight, DWORD dmBitsPerPel)
{
	DEVMODE InitDevMode;
	BOOL res;
	OutTraceDW("ChangeDisplaySettings: LOCK wxh=(%dx%d) BitsPerPel=%d -> wxh=(%dx%d) BitsPerPel=%d\n", 
		InitDevMode.dmPelsWidth, InitDevMode.dmPelsHeight, InitDevMode.dmBitsPerPel,
		dmPelsWidth, dmPelsHeight, dmBitsPerPel);
	if( (dmPelsWidth != InitDevMode.dmPelsWidth) ||
		(dmPelsHeight !=InitDevMode.dmPelsHeight) ||
		(dmBitsPerPel != InitDevMode.dmBitsPerPel)){
		res=(*pChangeDisplaySettingsExA)(NULL, &InitDevMode, NULL, 0, NULL);
		if(res) OutTraceE("ChangeDisplaySettings: ERROR err=%d at %d\n", GetLastError(), __LINE__);
	}
}

static HMODULE LoadDisasm()
{
	HMODULE disasmlib;
	
	disasmlib=(*pLoadLibraryA)("disasm.dll");
	if(!disasmlib) {
		OutTraceDW("DXWND: Load lib=\"%s\" failed err=%d\n", "disasm.dll", GetLastError());
		return NULL;
	}
	pGeterrwarnmessage=(Geterrwarnmessage_Type)(*pGetProcAddress)(disasmlib, "Geterrwarnmessage");
	pPreparedisasm=(Preparedisasm_Type)(*pGetProcAddress)(disasmlib, "Preparedisasm");
	pFinishdisasm=(Finishdisasm_Type)(*pGetProcAddress)(disasmlib, "Finishdisasm");
	pDisasm=(Disasm_Type)(*pGetProcAddress)(disasmlib, "Disasm");
	//OutTraceDW("DXWND: Load disasm.dll ptrs=%x,%x,%x,%x\n", pGeterrwarnmessage, pPreparedisasm, pFinishdisasm, pDisasm);

	return disasmlib;
}

LONG WINAPI myUnhandledExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo)
{
	OutTrace("UnhandledExceptionFilter: exception code=%x flags=%x addr=%x\n",
		ExceptionInfo->ExceptionRecord->ExceptionCode,
		ExceptionInfo->ExceptionRecord->ExceptionFlags,
		ExceptionInfo->ExceptionRecord->ExceptionAddress);
	DWORD oldprot;
	static HMODULE disasmlib = NULL;
	PVOID target = ExceptionInfo->ExceptionRecord->ExceptionAddress;
	switch(ExceptionInfo->ExceptionRecord->ExceptionCode){
	case 0xc0000094: // IDIV reg (Ultim@te Race Pro)
	case 0xc0000095: // DIV by 0 (divide overflow) exception (SonicR)
	case 0xc0000096: // CLI Priviliged instruction exception (Resident Evil), FB (Asterix & Obelix)
	case 0xc000001d: // FEMMS (eXpendable)
	case 0xc0000005: // Memory exception (Tie Fighter)
		int cmdlen;
		t_disasm da;
		if(!disasmlib){
			if (!(disasmlib=LoadDisasm())) return EXCEPTION_CONTINUE_SEARCH;
			(*pPreparedisasm)();
		}
		if(!VirtualProtect(target, 10, PAGE_READWRITE, &oldprot)) return EXCEPTION_CONTINUE_SEARCH; // error condition
		cmdlen=(*pDisasm)((BYTE *)target, 10, 0, &da, 0, NULL, NULL);
		OutTrace("UnhandledExceptionFilter: NOP opcode=%x len=%d\n", *(BYTE *)target, cmdlen);
		memset((BYTE *)target, 0x90, cmdlen); 
		VirtualProtect(target, 10, oldprot, &oldprot);
		if(!FlushInstructionCache(GetCurrentProcess(), target, cmdlen))
			OutTrace("UnhandledExceptionFilter: FlushInstructionCache ERROR target=%x, err=%x\n", target, GetLastError());
		// v2.03.10 skip replaced opcode
		ExceptionInfo->ContextRecord->Eip += cmdlen; // skip ahead op-code length
		return EXCEPTION_CONTINUE_EXECUTION;
		break;
	default:
		return EXCEPTION_CONTINUE_SEARCH;
	}
}

LPTOP_LEVEL_EXCEPTION_FILTER WINAPI extSetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
	OutTraceDW("SetUnhandledExceptionFilter: lpExceptionFilter=%x\n", lpTopLevelExceptionFilter);
	extern LONG WINAPI myUnhandledExceptionFilter(LPEXCEPTION_POINTERS);
	return (*pSetUnhandledExceptionFilter)(myUnhandledExceptionFilter);
}

//#define USEPERFCOUNTERS

unsigned __int64 inline GetRDTSC() {
   __asm {
      ; Flush the pipeline
      XOR eax, eax
      CPUID
      ; Get RDTSC counter in edx:eax
      RDTSC
   }
}

LONG CALLBACK Int3Handler(PEXCEPTION_POINTERS ExceptionInfo)
{
	// Vectored Handler routine to intercept INT3 opcodes replacing RDTSC
 	if (ExceptionInfo->ExceptionRecord->ExceptionCode == 0x80000003){
		LARGE_INTEGER myPerfCount;
#ifdef USEPERFCOUNTERS
		if(!pQueryPerformanceCounter) pQueryPerformanceCounter=QueryPerformanceCounter;
		(*pQueryPerformanceCounter)(&myPerfCount);
#else
	   __asm {
		  ; Flush the pipeline
		  XOR eax, eax
		  CPUID
		  ; Get RDTSC counter in edx:eax
		  RDTSC 
		  mov myPerfCount.LowPart, eax
		  mov myPerfCount.HighPart, edx
	   }
#endif
		//myPerfCount = dxw.StretchCounter(myPerfCount);
		myPerfCount = dxw.StretchLargeCounter(myPerfCount);
		OutTraceDW("Int3Handler: INT3 at=%x tick=%x RDTSC=%x:%x\n", 
			ExceptionInfo->ExceptionRecord->ExceptionAddress, (*pGetTickCount)(), myPerfCount.HighPart, myPerfCount.LowPart);

		ExceptionInfo->ContextRecord->Edx = myPerfCount.HighPart;
		ExceptionInfo->ContextRecord->Eax = myPerfCount.LowPart;
		ExceptionInfo->ContextRecord->Eip++; // skip ahead one-byte ( jump over 0xCC op-code )
		ExceptionInfo->ContextRecord->Eip++; // skip ahead one-byte ( jump over 0x90 op-code )
		return EXCEPTION_CONTINUE_EXECUTION;
	}
	return EXCEPTION_CONTINUE_SEARCH;
}

void HookExceptionHandler(void)
{
	void *tmp;
	HMODULE base;

	OutTraceDW("Set exception handlers\n");
	base=GetModuleHandle(NULL);
	pSetUnhandledExceptionFilter = SetUnhandledExceptionFilter;
	//v2.1.75 override default exception handler, if any....
	LONG WINAPI myUnhandledExceptionFilter(LPEXCEPTION_POINTERS);
	tmp = HookAPI(base, "KERNEL32.dll", UnhandledExceptionFilter, "UnhandledExceptionFilter", myUnhandledExceptionFilter);
	// so far, no need to save the previous handler, but anyway...
	tmp = HookAPI(base, "KERNEL32.dll", SetUnhandledExceptionFilter, "SetUnhandledExceptionFilter", extSetUnhandledExceptionFilter);
	if(tmp) pSetUnhandledExceptionFilter = (SetUnhandledExceptionFilter_Type)tmp;

	SetErrorMode(SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS);
	(*pSetUnhandledExceptionFilter)((LPTOP_LEVEL_EXCEPTION_FILTER)myUnhandledExceptionFilter);
}

void HookModule(HMODULE base, int dxversion)
{
	HookKernel32(base);
	HookUser32(base);
	HookOle32(base);
	HookWinMM(base);
	//if(dxw.dwFlags2 & SUPPRESSIME) HookImeLib(module);
	HookGDI32(base);
	if(dxw.dwFlags1 & HOOKDI) HookDirectInput(base, dxversion);
	if(dxw.dwTargetDDVersion != HOOKDDRAWNONE) HookDirectDraw(base, dxversion);
	HookDirect3D(base, dxversion);
	HookDirect3D7(base, dxversion);
	if(dxw.dwFlags2 & HOOKOPENGL) HookOpenGLLibs(base, dxw.CustomOpenGLLib); 
	if(dxw.dwFlags4 & HOOKGLIDE) HookGlideLibs(base); 
	if( (dxw.dwFlags3 & EMULATEREGISTRY) || 
		(dxw.dwFlags4 & OVERRIDEREGISTRY) || 
		(dxw.dwTFlags & OUTREGISTRY)) HookAdvApi32(base);
	HookMSV4WLibs(base); // -- used by Aliens & Amazons demo: what for?
	HookAVIFil32(base);
	//HookSmackW32(base);
	//HookDirectSound(base);
	//HookComDlg32(base);
}

#define USEWINNLSENABLE

void DisableIME()
{
	BOOL res;
	HMODULE hm;
	hm=GetModuleHandle("User32");
	if(hm==NULL){
		OutTrace("DisableIME: GetModuleHandle(User32) ERROR err=%d\n", GetLastError());
		return;
	}
	// here, GetProcAddress may be not hooked yet!
	if(!pGetProcAddress) pGetProcAddress=GetProcAddress;
#ifdef USEWINNLSENABLE
	typedef BOOL (WINAPI *WINNLSEnableIME_Type)(HWND, BOOL);
	WINNLSEnableIME_Type pWINNLSEnableIME;
	pWINNLSEnableIME=(WINNLSEnableIME_Type)(*pGetProcAddress)(hm, "WINNLSEnableIME");
	OutTrace("DisableIME: GetProcAddress(WINNLSEnableIME)=%x\n", pWINNLSEnableIME);
	if(!pWINNLSEnableIME) return;
	SetLastError(0);
	res=(*pWINNLSEnableIME)(NULL, FALSE);
	OutTrace("IME previous state=%x error=%d\n", res, GetLastError());
#else
	typedef LRESULT (WINAPI *SendIMEMessage_Type)(HWND, LPARAM);
	SendIMEMessage_Type pSendIMEMessage;
	//pSendIMEMessage=(SendIMEMessage_Type)(*pGetProcAddress)(hm, "SendIMEMessage");
	pSendIMEMessage=(SendIMEMessage_Type)(*pGetProcAddress)(hm, "SendIMEMessageExA");
	OutTrace("DisableIME: GetProcAddress(SendIMEMessage)=%x\n", pSendIMEMessage);
	if(!pSendIMEMessage) return;
	HGLOBAL imeh;
	IMESTRUCT *imes;
	imeh=GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, sizeof(IMESTRUCT));
	imes=(IMESTRUCT *)imeh;
	//imes->fnc=IME_SETLEVEL;
	imes->fnc=7;
	imes->wParam=1;
	SetLastError(0);
	res=(*pSendIMEMessage)(dxw.GethWnd(), (LPARAM)imeh);
	OutTrace("res=%x error=%d\n", res, GetLastError());
#endif
}

void SetSingleProcessAffinity(void)
{
	int i;
	DWORD ProcessAffinityMask, SystemAffinityMask;
	if(!GetProcessAffinityMask(GetCurrentProcess(), &ProcessAffinityMask, &SystemAffinityMask))
		OutTraceE("GetProcessAffinityMask: ERROR err=%d\n", GetLastError());
	OutTraceDW("Process affinity=%x\n", ProcessAffinityMask);
	for (i=0; i<(8 * sizeof(DWORD)); i++){
		if (ProcessAffinityMask & 0x1) break;
		ProcessAffinityMask >>= 1;
	}
	OutTraceDW("First process affinity bit=%d\n", i);
	ProcessAffinityMask &= 0x1;
	for (; i; i--) ProcessAffinityMask <<= 1;
	OutTraceDW("Process affinity=%x\n", ProcessAffinityMask);
	if (!SetProcessAffinityMask(GetCurrentProcess(), ProcessAffinityMask))
		OutTraceE("SetProcessAffinityMask: ERROR err=%d\n", GetLastError());
}

extern HHOOK hMouseHook;

LRESULT CALLBACK MessageHook(int code, WPARAM wParam, LPARAM lParam)
{
	static BOOL SizeMoving = FALSE;

	if(code == HC_ACTION){
		if(dxw.IsFullScreen()){
			MSG *msg;
			msg = (MSG *)lParam;
			OutTraceC("MessageHook: hwnd=%x message=%d(%s) remove=%d pt=(%d,%d)\n", 
				msg->hwnd, msg->message, ExplainWinMessage(msg->message), msg->wParam, msg->pt.x, msg->pt.y);

			switch(msg->message){
				case WM_ENTERSIZEMOVE: SizeMoving = TRUE; break;
				case WM_EXITSIZEMOVE: SizeMoving = FALSE; break;
			}

			// do not try to fix coordinates for points outside the client area!!
			// in theory, that should primarily depend on the message type, but this is the way it works ...
			// do not do the fixing also when in sizemove mode
			RECT client;
			client=dxw.GetUnmappedScreenRect();
			if ((SizeMoving) ||
				(msg->pt.x < client.left)		||
				(msg->pt.y < client.top)		||
				(msg->pt.x > client.right)		||
				(msg->pt.y > client.bottom)){
				// do nothing
			}
			else {
				// fix the message point coordinates
				POINT upleft={0,0};
				(*pClientToScreen)(dxw.GethWnd(), &upleft);
				msg->pt = dxw.SubCoordinates(msg->pt, upleft);
				msg->pt=dxw.FixCursorPos(msg->pt);
				// beware: needs fix for mousewheel?
				if((msg->message <= WM_MOUSELAST) && (msg->message >= WM_MOUSEFIRST)) msg->lParam = MAKELPARAM(msg->pt.x, msg->pt.y); 
				OutTraceC("MessageHook: fixed lparam/pt=(%d,%d)\n", msg->pt.x, msg->pt.y);
				GetHookInfo()->CursorX=(short)msg->pt.x;
				GetHookInfo()->CursorY=(short)msg->pt.y;
			}
		}
	}
	return CallNextHookEx(hMouseHook, code, wParam, lParam);
}

static void ReplaceRDTSC()
{
	typedef BOOL (WINAPI *GetModuleInformation_Type)(HANDLE, HMODULE, LPMODULEINFO, DWORD);
	HMODULE disasmlib;
	unsigned char *opcodes;
	t_disasm da;
	MODULEINFO mi;
	HMODULE psapilib;
	GetModuleInformation_Type pGetModuleInformation;
	DWORD dwSegSize;
	DWORD oldprot;

	if (!(disasmlib=LoadDisasm())) return;

	// getting segment size
	psapilib=(*pLoadLibraryA)("psapi.dll");
	if(!psapilib) {
		OutTraceDW("DXWND: Load lib=\"%s\" failed err=%d\n", "psapi.dll", GetLastError());
		return;
	}
	pGetModuleInformation=(GetModuleInformation_Type)(*pGetProcAddress)(psapilib, "GetModuleInformation");
	(*pGetModuleInformation)(GetCurrentProcess(), GetModuleHandle(NULL), &mi, sizeof(mi));
	dwSegSize = mi.SizeOfImage;
	FreeLibrary(psapilib);

	(*pPreparedisasm)();
	opcodes = (unsigned char *)mi.lpBaseOfDll;
	unsigned int offset = 0;
	BOOL cont = TRUE;
	OutTraceDW("DXWND: opcode starting at addr=%x size=%x\n", opcodes, dwSegSize);
	while (cont) {
		int cmdlen = 0;
		__try{
			cmdlen=(*pDisasm)(opcodes+offset,20,offset,&da,0,NULL,NULL);
			//OutTrace("offset=%x opcode=%x\n", offset, *(opcodes+offset));
		}
		__except (EXCEPTION_EXECUTE_HANDLER){
			OutTrace("exception at offset=%x\n", offset);
			if(opcodes+offset < mi.EntryPoint) {
				offset = (unsigned char *)mi.EntryPoint - (unsigned char *)mi.lpBaseOfDll;
				OutTraceDW("DXWND: opcode resuming at addr=%x\n", opcodes+offset);
				continue;
			}
			else
				cont=FALSE;
		}		
		if (cmdlen==0) break;
		// search for RDTSC opcode 0x0F31
		if((*(opcodes+offset) == 0x0F) && (*(opcodes+offset+1) == 0x31)){
			OutTraceDW("DXWND: RDTSC opcode found at addr=%x\n", (opcodes+offset));
			if(!VirtualProtect((LPVOID)(opcodes+offset), 4, PAGE_READWRITE, &oldprot)) {
				OutTrace("VirtualProtect ERROR: target=%x err=%d at %d\n", opcodes+offset, GetLastError(), __LINE__);
				return; // error condition
			}
			*(opcodes+offset) = 0xCC;	// __asm INT3
			*(opcodes+offset+1) = 0x90; // __asm NOP
			if(!VirtualProtect((LPVOID)(opcodes+offset), 4, oldprot, &oldprot)){
				OutTrace("VirtualProtect ERROR; target=%x, err=%d at %d\n", opcodes+offset, GetLastError(), __LINE__);
				return; // error condition
			}
		}
		// search for RDTSCP opcode 0x0F01F9
		if((*(opcodes+offset) == 0x0F) && (*(opcodes+offset+1) == 0x01) && (*(opcodes+offset+2) == 0xF9)){
			OutTraceDW("DXWND: RDTSC opcode found at addr=%x\n", (opcodes+offset));
			if(!VirtualProtect((LPVOID)(opcodes+offset), 4, PAGE_READWRITE, &oldprot)) {
				OutTrace("VirtualProtect ERROR: target=%x err=%d at %d\n", opcodes+offset, GetLastError(), __LINE__);
				return; // error condition
			}
			*(opcodes+offset) = 0xCC;	// __asm INT3
			*(opcodes+offset+1) = 0x90; // __asm NOP
			*(opcodes+offset+2) = 0x90; // __asm NOP
			if(!VirtualProtect((LPVOID)(opcodes+offset), 4, oldprot, &oldprot)){
				OutTrace("VirtualProtect ERROR; target=%x, err=%d at %d\n", opcodes+offset, GetLastError(), __LINE__);
				return; // error condition
			}
		}
		offset+=cmdlen; 
		if((offset+0x10) > (int)mi.lpBaseOfDll + dwSegSize) break; // skip last 16 bytes, just in case....
	}

	return;
	(*pFinishdisasm)();
	FreeLibrary(disasmlib);
}

static void ReplacePrivilegedOps()
{
	typedef BOOL (WINAPI *GetModuleInformation_Type)(HANDLE, HMODULE, LPMODULEINFO, DWORD);
	HMODULE disasmlib;
	unsigned char *opcodes;
	t_disasm da;
	MODULEINFO mi;
	HMODULE psapilib;
	GetModuleInformation_Type pGetModuleInformation;
	DWORD dwSegSize;
	DWORD oldprot;

	if (!(disasmlib=LoadDisasm())) return;

	// getting segment size
	psapilib=(*pLoadLibraryA)("psapi.dll");
	if(!psapilib) {
		OutTraceDW("DXWND: Load lib=\"%s\" failed err=%d\n", "psapi.dll", GetLastError());
		return;
	}
	pGetModuleInformation=(GetModuleInformation_Type)(*pGetProcAddress)(psapilib, "GetModuleInformation");
	(*pGetModuleInformation)(GetCurrentProcess(), GetModuleHandle(NULL), &mi, sizeof(mi));
	dwSegSize = mi.SizeOfImage;
	FreeLibrary(psapilib);

	(*pPreparedisasm)();
	opcodes = (unsigned char *)mi.lpBaseOfDll;
	unsigned int offset = 0;
	BOOL cont = TRUE;
	OutTraceDW("DXWND: opcode starting at addr=%x size=%x\n", opcodes, dwSegSize);
	while (cont) {
		int cmdlen = 0;
		__try{
			cmdlen=(*pDisasm)(opcodes+offset,20,offset,&da,0,NULL,NULL);
			//OutTrace("offset=%x opcode=%x\n", offset, *(opcodes+offset));
		}
		__except (EXCEPTION_EXECUTE_HANDLER){
			OutTrace("exception at offset=%x\n", offset);
			if(opcodes+offset < mi.EntryPoint) {
				offset = (unsigned char *)mi.EntryPoint - (unsigned char *)mi.lpBaseOfDll;
				OutTraceDW("DXWND: opcode resuming at addr=%x\n", opcodes+offset);
				continue;
			}
			else
				cont=FALSE;
		}		
		if (cmdlen==0) break;
		// search for IN opcode 0xEC (IN AL, DX)
		if(*(opcodes+offset) == 0xEC){
			OutTraceDW("DXWND: IN opcode found at addr=%x\n", (opcodes+offset));
			if(!VirtualProtect((LPVOID)(opcodes+offset), 8, PAGE_READWRITE, &oldprot)) {
				OutTrace("VirtualProtect ERROR: target=%x err=%d at %d\n", opcodes+offset, GetLastError(), __LINE__);
				return; // error condition
			}
			*(opcodes+offset) = 0x90;	// __asm NOP
			if((*(opcodes+offset+1) == 0xA8) && 
				((*(opcodes+offset+3) == 0x75) || (*(opcodes+offset+3) == 0x74))) { // both JNZ and JZ
				OutTraceDW("DXWND: IN loop found at addr=%x\n", (opcodes+offset));
				memset((opcodes+offset+1), 0x90, 4); // Ubik I/O loop
				offset+=4;
			}
			if(!VirtualProtect((LPVOID)(opcodes+offset), 8, oldprot, &oldprot)){
				OutTrace("VirtualProtect ERROR; target=%x, err=%d at %d\n", opcodes+offset, GetLastError(), __LINE__);
				return; // error condition
			}
		}
		offset+=cmdlen; 
		if((offset+0x10) > (int)mi.lpBaseOfDll + dwSegSize) break; // skip last 16 bytes, just in case....
	}

	return;
	(*pFinishdisasm)();
	FreeLibrary(disasmlib);
}

#ifdef CREATEDESKTOP
HWND hDesktopWindow = NULL;
#endif

// Message poller: its only purpose is to keep sending messages to the main window
// so that the message loop is kept running. It is a trick necessary to play 
// smack videos with smackw32.dll and AUTOREFRESH mode set
DWORD WINAPI MessagePoller(LPVOID lpParameter)
{
	#define DXWREFRESHINTERVAL 20
	while(TRUE){
		Sleep(DXWREFRESHINTERVAL);
		SendMessage(dxw.GethWnd(), WM_NCHITTEST, 0, 0);
	}
    return 0;
}

static void MemoryReserve()
{
	VirtualAlloc((LPVOID)0x4000000, 0x04000000, MEM_RESERVE, PAGE_READWRITE);
	VirtualAlloc((LPVOID)0x5000000, 0x00F00000, MEM_RESERVE, PAGE_READWRITE);
	VirtualAlloc((LPVOID)0x6000000, 0x00F00000, MEM_RESERVE, PAGE_READWRITE);
	VirtualAlloc((LPVOID)0x7000000, 0x00F00000, MEM_RESERVE, PAGE_READWRITE);
	VirtualAlloc((LPVOID)0x8000000, 0x00F00000, MEM_RESERVE, PAGE_READWRITE);
}

void HookInit(TARGETMAP *target, HWND hwnd)
{
	HMODULE base;
	char *sModule;
	char sModuleBuf[60+1];
	char sSourcePath[MAX_PATH+1];
	static char *dxversions[14]={
		"Automatic", "DirectX1~6", "", "", "", "", "", 
		"DirectX7", "DirectX8", "DirectX9", "DirectX10", "DirectX11", "None", ""
	};
	static char *Resolutions[]={
		"unlimited", "320x200", "400x300", "640x480", "800x600", "1024x768", "1280x960", "" // terminator
	};

	dxw.InitTarget(target);

	// reserve legacy memory segments
	if(dxw.dwFlags6 & LEGACYALLOC) MemoryReserve();

	// add the DxWnd install dir to the search path, to make all included dll linkable
	DWORD dwAttrib;		
	dwAttrib = GetFileAttributes("dxwnd.dll");
	//if (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
	//	MessageBox(0, "DXWND: ERROR can't locate itself", "ERROR", MB_OK | MB_ICONEXCLAMATION);
	//	exit(0);
	//}
	GetModuleFileName(GetModuleHandle("dxwnd"), sSourcePath, MAX_PATH);
	sSourcePath[strlen(sSourcePath)-strlen("dxwnd.dll")] = 0; // terminate the string just before "dxwnd.dll"
	SetDllDirectory(sSourcePath);

	if(dxw.dwFlags1 & AUTOMATIC) dxw.dwFlags1 |= EMULATESURFACE; // if AUTOMATIC, try this first!
	if(dxw.dwFlags5 & HYBRIDMODE) {
		// special mode settings ....
		dxw.dwFlags1 |= EMULATESURFACE;
		// dxw.dwFlags5 |= NOSYSTEMEMULATED;
		dxw.dwFlags2 |= SETCOMPATIBILITY;
		dxw.dwFlags5 &= ~(BILINEARFILTER | AEROBOOST); 
	}
	if(dxw.dwFlags5 & GDIMODE) dxw.dwFlags1 |= EMULATESURFACE;
	if(dxw.dwFlags5 & STRESSRESOURCES) dxw.dwFlags5 |= LIMITRESOURCES;

	if(hwnd){ // v2.02.32: skip this when in code injection mode.
		// v2.1.75: is it correct to set hWnd here?
		//dxw.SethWnd(hwnd);
		dxw.hParentWnd=GetParent(hwnd);
		dxw.hChildWnd=hwnd;
		// v2.02.31: set main win either this one or the parent!
		dxw.SethWnd((dxw.dwFlags1 & FIXPARENTWIN) ? GetParent(hwnd) : hwnd);
		if(dxw.dwFlags4 & ENABLEHOTKEYS) dxw.MapKeysInit();
	}

#ifdef CREATEDESKTOP
	if(CREATEDESKTOP){
		if (!hDesktopWindow){
			static ATOM aClass;
			WNDCLASSEX WndClsEx;
			HINSTANCE hinst=NULL;
			hinst=GetModuleHandle(NULL);
			WndClsEx.cbSize        = sizeof(WNDCLASSEX);
			WndClsEx.style         = 0;
			WndClsEx.lpfnWndProc   = DefWindowProc;
			WndClsEx.cbClsExtra    = 0;
			WndClsEx.cbWndExtra    = 0;
			WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
			WndClsEx.hCursor       = NULL;
			WndClsEx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
			//WndClsEx.hbrBackground = CreateSolidBrush(RGB(200,0,0));
			WndClsEx.lpszMenuName  = NULL;
			WndClsEx.lpszClassName = "dxwnd:desktop";
			WndClsEx.hInstance     = hinst;
			WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
		
			aClass=RegisterClassEx(&WndClsEx);

			//HWND hParent = (*pGetDesktopWindow)();
			HWND hParent = GetDesktopWindow(); // not hooked yet !
			hDesktopWindow=CreateWindowEx(0, "dxwnd:desktop", "DxWnd Desktop", 0, 0, 0, 0, 0, hParent, NULL, hinst, NULL);
			if(hDesktopWindow){
				MoveWindow(hDesktopWindow, target->posx, target->posy, target->sizx, target->sizy, TRUE);
				OutTraceDW("created desktop emulation: hwnd=%x\n", hDesktopWindow);
			}
			else{
				OutTraceE("CreateWindowEx ERROR: err=%d at %d\n", GetLastError(), __LINE__);
			}
		}
	}
#endif

	if(IsTraceDW){
		char sInfo[1024];
		strcpy(sInfo, "");
		if(hwnd) sprintf(sInfo, " hWnd=%x(hdc=%x) dxw.hParentWnd=%x(hdc=%x) desktop=%x(hdc=%x)", 
			hwnd, GetDC(hwnd), dxw.hParentWnd, GetDC(dxw.hParentWnd), GetDesktopWindow(), GetDC(GetDesktopWindow()));
		OutTrace("HookInit: path=\"%s\" module=\"%s\" dxversion=%s pos=(%d,%d) size=(%d,%d)%s\n", 
			target->path, target->module, dxversions[dxw.dwTargetDDVersion], 
			target->posx, target->posy, target->sizx, target->sizy, sInfo);
		if (dxw.dwFlags4 & LIMITSCREENRES) OutTrace("HookInit: max resolution=%s\n", (dxw.MaxScreenRes<6)?Resolutions[dxw.MaxScreenRes]:"unknown");
	}

	{
		// Beware: for some strange & mysterious reason, this call makes Warcraft II and other games
		// work better, avoiding something that resembles a black palette (no blit errors, but the
		// whole screen black!!) and an AERO rupture.
		PIXELFORMATDESCRIPTOR pfd;
		DescribePixelFormat(GetDC(GetDesktopWindow()), 1, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
	}

	if (hwnd && IsDebug){
		DWORD dwStyle, dwExStyle;
		char ClassName[81];
		char WinText[81];
		dwStyle=GetWindowLong(dxw.GethWnd(), GWL_STYLE);
		dwExStyle=GetWindowLong(dxw.GethWnd(), GWL_EXSTYLE);
		GetClassName(dxw.GethWnd(), ClassName, sizeof(ClassName));
		GetWindowText(dxw.GethWnd(), WinText, sizeof(WinText));
		OutTrace("HookInit: dxw.hChildWnd=%x class=\"%s\" text=\"%s\" style=%x(%s) exstyle=%x(%s)\n", 
			dxw.hChildWnd, ClassName, WinText, dwStyle, ExplainStyle(dwStyle), dwExStyle, ExplainExStyle(dwExStyle));
		dwStyle=GetWindowLong(dxw.hParentWnd, GWL_STYLE);
		dwExStyle=GetWindowLong(dxw.hParentWnd, GWL_EXSTYLE);
		GetClassName(dxw.hParentWnd, ClassName, sizeof(ClassName));
		GetWindowText(dxw.hParentWnd, WinText, sizeof(WinText));
		OutTrace("HookInit: dxw.hParentWnd=%x class=\"%s\" text=\"%s\" style=%x(%s) exstyle=%x(%s)\n", 
			dxw.hParentWnd, ClassName, WinText, dwStyle, ExplainStyle(dwStyle), dwExStyle, ExplainExStyle(dwExStyle));
		OutTrace("HookInit: target window pos=(%d,%d) size=(%d,%d)\n", dxw.iPosX, dxw.iPosY, dxw.iSizX, dxw.iSizY);
		dxw.DumpDesktopStatus();
	}

	if (SKIPIMEWINDOW) {
		char ClassName[8+1];
		GetClassName(hwnd, ClassName, sizeof(ClassName));
		if(!strcmp(ClassName, "IME")){
			dxw.hChildWnd=GetParent(hwnd);
			dxw.hParentWnd=GetParent(dxw.hChildWnd);
			if (dxw.dwFlags2 & SUPPRESSIME) DestroyWindow(hwnd);
			// v2.02.31: set main win either this one or the parent!
			dxw.SethWnd((dxw.dwFlags1 & FIXPARENTWIN) ? dxw.hParentWnd : dxw.hChildWnd);
			hwnd = dxw.GethWnd();
			if(hwnd) OutTraceDW("HookInit: skipped IME window. current hWnd=%x(hdc=%x) dxw.hParentWnd=%x(hdc=%x)\n", 
				hwnd, GetDC(hwnd), dxw.hParentWnd, GetDC(dxw.hParentWnd));		
		}
	}

#ifdef CREATEDESKTOP
	if(CREATEDESKTOP){
		if (hDesktopWindow){
			OutTraceDW("HookInit: set new parent=%x to main win=%x\n", hDesktopWindow, dxw.hChildWnd);
			SetParent(dxw.hChildWnd, hDesktopWindow);
			dxw.hParentWnd = hDesktopWindow;
		}
	}
#endif

#ifdef CHECKFORCOMPATIBILITYFLAGS
	CheckCompatibilityFlags(); // v2.02.83 Check for change of OS release
#endif

	HookSysLibsInit(); // this just once...

	base=GetModuleHandle(NULL);
	if (dxw.dwFlags3 & SINGLEPROCAFFINITY) SetSingleProcessAffinity();
	if (dxw.dwFlags4 & INTERCEPTRDTSC) AddVectoredExceptionHandler(1, Int3Handler); // 1 = first call, 0 = call last
	if (dxw.dwFlags1 & HANDLEEXCEPTIONS) HookExceptionHandler();
	if (dxw.dwTFlags & OUTIMPORTTABLE) DumpImportTable(base);
	if (dxw.dwFlags2 & SUPPRESSIME) DisableIME();

	if (dxw.dwFlags4 & INTERCEPTRDTSC) ReplaceRDTSC();
	if (dxw.dwFlags5 & REPLACEPRIVOPS) ReplacePrivilegedOps();

	if (dxw.dwTFlags & DXPROXED){
		HookDDProxy(base, dxw.dwTargetDDVersion);
		return;
	}

	// make InitPosition used for both DInput and DDraw
	if(dxw.Windowize){
		InitPosition(target->initx, target->inity,
			target->minx, target->miny, target->maxx, target->maxy);
		dxw.InitWindowPos(target->posx, target->posy, target->sizx, target->sizy);
	}

	OutTraceB("HookInit: base hmodule=%x\n", base);
	HookModule(base, dxw.dwTargetDDVersion);
	if (dxw.dwFlags3 & HOOKDLLS) HookDlls(base);

	strncpy(sModuleBuf, dxw.gsModules, 60);
	sModule=strtok(sModuleBuf," ;");
	while (sModule) {
		base=(*pLoadLibraryA)(sModule);
		if(!base){
			OutTraceE("HookInit: LoadLibrary ERROR module=%s err=%d\n", sModule, GetLastError());
		}
		else {
			OutTraceDW("HookInit: hooking additional module=%s base=%x\n", sModule, base);
			if (dxw.dwTFlags & OUTIMPORTTABLE) DumpImportTable(base);
			HookModule(base, dxw.dwTargetDDVersion);
		}
		sModule=strtok(NULL," ;");
	}

	SaveScreenMode();
	if(dxw.dwFlags2 & RECOVERSCREENMODE) RecoverScreenMode();
	if(dxw.dwFlags3 & FORCE16BPP) SwitchTo16BPP();

	if (dxw.dwFlags1 & MESSAGEPROC){
		extern HINSTANCE hInst;
		typedef HHOOK (WINAPI *SetWindowsHookEx_Type)(int, HOOKPROC, HINSTANCE, DWORD);
		extern SetWindowsHookEx_Type pSetWindowsHookEx;
		hMouseHook=(*pSetWindowsHookEx)(WH_GETMESSAGE, MessageHook, hInst, GetCurrentThreadId());
		if(hMouseHook==NULL) OutTraceE("SetWindowsHookEx WH_GETMESSAGE failed: error=%d\n", GetLastError());
	}

	InitScreenParameters();
	if(hwnd) HookWindowProc(hwnd);
	// in fullscreen mode, messages seem to reach and get processed by the parent window
	if((!dxw.Windowize) && hwnd) HookWindowProc(GetParent(hwnd));

	// initialize window: if
	// 1) not in injection mode (hwnd != 0) and
	// 2) in Windowed mode and
	// 3) supposedly in fullscreen mode (dxw.IsFullScreen()) and
	// 4) configuration ask for a overlapped bordered window (dxw.dwFlags1 & FIXWINFRAME) then
	// update window styles: just this window or, when FIXPARENTWIN is set, the father one as well.

	if (hwnd && dxw.Windowize && dxw.IsFullScreen() && (dxw.dwFlags1 & FIXWINFRAME)) {
		dxw.FixWindowFrame(dxw.hChildWnd);
		AdjustWindowPos(dxw.hChildWnd, target->sizx, target->sizy);
		if(dxw.dwFlags1 & FIXPARENTWIN) {
			dxw.FixWindowFrame(dxw.hParentWnd);
			AdjustWindowPos(dxw.hParentWnd, target->sizx, target->sizy);
		}
	}

	if (dxw.dwFlags1 & AUTOREFRESH) 
		CreateThread(NULL, 0, MessagePoller, NULL, 0, NULL);
}

LPCSTR ProcToString(LPCSTR proc)
{
	static char sBuf[24+1];
	if(((DWORD)proc & 0xFFFF0000) == 0){
		sprintf_s(sBuf, 24, "#0x%x", proc);
		return sBuf;
	}
	else
		return proc;
}

FARPROC RemapLibrary(LPCSTR proc, HMODULE hModule, HookEntry_Type *Hooks)
{
	void *remapped_addr;
	for(; Hooks->APIName; Hooks++){
		if (!strcmp(proc,Hooks->APIName)){
			if((((dxw.dwFlags4 & HOTPATCH) && (Hooks->HookStatus == HOOK_HOT_CANDIDATE)) ||  // hot patch candidate still to process - or
				((dxw.dwFlags4 & HOTPATCHALWAYS) && (Hooks->HookStatus != HOOK_HOT_LINKED)))){ // force hot patch and not already hooked
											 
			if(!Hooks->OriginalAddress) {
					Hooks->OriginalAddress=(*pGetProcAddress)(hModule, Hooks->APIName);
					if(!Hooks->OriginalAddress) continue;
				}

				remapped_addr = HotPatch(Hooks->OriginalAddress, Hooks->APIName, Hooks->HookerAddress);
				if(remapped_addr == (void *)1) { // should never go here ...
					Hooks->HookStatus = HOOK_HOT_LINKED;
					continue; // already hooked
				}
				if(remapped_addr) {
					Hooks->HookStatus = HOOK_HOT_LINKED;
					if(Hooks->StoreAddress) *(Hooks->StoreAddress) = (FARPROC)remapped_addr;
				}			
			}
			if(Hooks->HookStatus == HOOK_HOT_LINKED) {
				OutTraceDW("GetProcAddress: hot patched proc=%s addr=%x\n", ProcToString(proc), Hooks->HookerAddress);
				return Hooks->HookerAddress;
			}
			if (Hooks->StoreAddress) *(Hooks->StoreAddress)=(*pGetProcAddress)(hModule, proc);
			OutTraceDW("GetProcAddress: hooking proc=%s addr=%x->%x\n", 
				ProcToString(proc), (Hooks->StoreAddress) ? *(Hooks->StoreAddress) : 0, Hooks->HookerAddress);
			return Hooks->HookerAddress;
		}
	}
	return NULL;
}

void HookLibrary(HMODULE hModule, HookEntry_Type *Hooks, char *DLLName)
{
	HMODULE hDLL = NULL;
	//OutTrace("HookLibrary: hModule=%x dll=%s\n", hModule, DLLName);
	for(; Hooks->APIName; Hooks++){
		//tmp = HookAPI(hModule, DLLName, Hooks->HookStatus, Hooks->OriginalAddress, Hooks->APIName, Hooks->HookerAddress);
		//if(tmp && Hooks->StoreAddress) *(Hooks->StoreAddress) = (FARPROC)tmp;

		void *remapped_addr;

		if(Hooks->HookStatus == HOOK_HOT_LINKED) continue; // skip any hot-linked entry

		//if(dxw.dwTFlags & OUTIMPORTTABLE) OutTrace("HookAPI: module=%x dll=%s apiproc=%x apiname=%s hookproc=%x\n", 
		//	module, dll, apiproc, apiname, hookproc);

		//if(!*apiname) { // check
		//	char *sMsg="HookAPI: NULL api name\n";
		//	OutTraceE(sMsg);
		//	if (IsAssertEnabled) MessageBox(0, sMsg, "HookAPI", MB_OK | MB_ICONEXCLAMATION);
		//	continue;
		//}

		if((((dxw.dwFlags4 & HOTPATCH) && (Hooks->HookStatus == HOOK_HOT_CANDIDATE)) ||  // hot patch candidate still to process - or
			((dxw.dwFlags4 & HOTPATCHALWAYS) && (Hooks->HookStatus != HOOK_HOT_LINKED))) // force hot patch and not already hooked
			&&
			Hooks->StoreAddress){							 // and save ptr available
			// Hot Patch - beware! This way yo're likely to hook unneeded libraries.
			if(!Hooks->OriginalAddress) {
				if(!hDLL) {
					hDLL = (*pLoadLibraryA)(DLLName);
					if(!hDLL) {
						OutTrace("HookLibrary: LoadLibrary failed on DLL=%s err=%x\n", DLLName, GetLastError());
						continue;
					}
				}
				Hooks->OriginalAddress=(*pGetProcAddress)(hDLL, Hooks->APIName);
				if(!Hooks->OriginalAddress) {
					OutTraceB("HookLibrary: GetProcAddress failed on API=%s err=%x\n", Hooks->APIName, GetLastError());
					continue;
				}
			}
			remapped_addr = HotPatch(Hooks->OriginalAddress, Hooks->APIName, Hooks->HookerAddress);
			if(remapped_addr == (void *)1) { // should never go here ...
				Hooks->HookStatus = HOOK_HOT_LINKED;
				continue; // already hooked
			}
			if(remapped_addr) {
				Hooks->HookStatus = HOOK_HOT_LINKED;
				*(Hooks->StoreAddress) = (FARPROC)remapped_addr;
				continue;
			}
		}

		remapped_addr = IATPatch(hModule, DLLName, Hooks->OriginalAddress, Hooks->APIName, Hooks->HookerAddress);
		if(remapped_addr)  {
			Hooks->HookStatus = HOOK_IAT_LINKED;
			if (Hooks->StoreAddress) *(Hooks->StoreAddress) = (FARPROC)remapped_addr;
		}
	}
}

void PinLibrary(HookEntry_Type *Hooks, char *DLLName)
{
	HMODULE hModule = NULL;
	hModule = (*pLoadLibraryA)(DLLName);
	if(!hModule) {
		OutTrace("PinLibrary: LoadLibrary failed on DLL=%s err=%x\n", DLLName, GetLastError());
		return;
	}
	for(; Hooks->APIName; Hooks++){
		if (Hooks->StoreAddress) *(Hooks->StoreAddress) = (*pGetProcAddress)(hModule, Hooks->APIName);
	}
}

BOOL IsHotPatched(HookEntry_Type *Hooks, char *ApiName)
{
	for(; Hooks->APIName; Hooks++){
		if(!strcmp(Hooks->APIName, ApiName)) return (Hooks->HookStatus == HOOK_HOT_LINKED);
	}
	return FALSE;
}

void HookLibInit(HookEntry_Type *Hooks)
{
	for(; Hooks->APIName; Hooks++)
		if (Hooks->StoreAddress) *(Hooks->StoreAddress) = Hooks->OriginalAddress;
}
