From 059c4c1beece4e58c15aa641f4e976744ca24ccc Mon Sep 17 00:00:00 2001 From: gho tik Date: Wed, 14 May 2014 12:39:12 -0400 Subject: [PATCH] v2_02_74_src Former-commit-id: d25a0c5093bd9c2bcdadb44ee972847e770e3615 --- Include/dxwnd.h | 3 + build/dxwnd.dll | 4 +- build/dxwnd.exe | 2 +- build/dxwnd.ini | 10 +- build/exports/Age of Empires II.dxw | 27 ++ build/exports/Age of Empires.dxw | 27 ++ build/exports/Doom 3.dxw | 27 ++ ...Little Big Adventure 2 (Windows patch).dxw | 27 ++ build/exports/Star Wars Episode I Racer.dxw | 3 +- build/exports/Tails Gear.dxw | 27 ++ build/exports/Wacky Races.dxw | 27 ++ build/exports/Wind Fantasy SP.dxw | 27 ++ build/exports/dxwnd.ini | 5 + build/readme-relnotes.txt | 20 +- dll/advapi.cpp | 14 +- dll/ddraw.cpp | 22 +- dll/dxhelper.cpp | 16 +- dll/dxhelper.h | 1 + dll/dxhook.cpp | 216 +++++++------- dll/dxhook.h | 10 + dll/dxwcore.cpp | 79 ++++- dll/dxwcore.hpp | 15 + dll/dxwnd.cpp | 5 +- dll/dxwnd.vs2008.suo | Bin 288768 -> 43520 bytes dll/dxwnd.vs2008.vcproj | 8 + dll/gdi32.cpp | 166 +++++------ dll/glide.cpp | 12 +- dll/hotpatch.cpp | 54 ++++ dll/iatpatch.cpp | 91 ++++++ dll/imelib.cpp | 18 +- dll/kernel32.cpp | 64 ++-- dll/msvfw.cpp | 2 +- dll/ole32.cpp | 8 +- dll/opengl.cpp | 38 +-- dll/syslibs.h | 13 + dll/user32.cpp | 275 +++++++++++------- dll/winmm.cpp | 12 +- dll/wintrust.cpp | 4 +- host/Resource.h | 6 +- host/TabDebug.cpp | 1 + host/TabInput.cpp | 3 + host/TabProgram.cpp | 3 +- host/TabWindow.cpp | 1 - host/TargetDlg.cpp | 4 + host/TargetDlg.h | 3 + host/dxwndhost.aps | Bin 172352 -> 173008 bytes host/dxwndhost.rc | 33 ++- host/dxwndhost.vs2008.suo | Bin 167424 -> 169472 bytes host/dxwndhostView.cpp | 6 + 49 files changed, 1011 insertions(+), 428 deletions(-) create mode 100644 build/exports/Age of Empires II.dxw create mode 100644 build/exports/Age of Empires.dxw create mode 100644 build/exports/Doom 3.dxw create mode 100644 build/exports/Little Big Adventure 2 (Windows patch).dxw create mode 100644 build/exports/Tails Gear.dxw create mode 100644 build/exports/Wacky Races.dxw create mode 100644 build/exports/Wind Fantasy SP.dxw create mode 100644 build/exports/dxwnd.ini create mode 100644 dll/hotpatch.cpp create mode 100644 dll/iatpatch.cpp diff --git a/Include/dxwnd.h b/Include/dxwnd.h index a479495..9cc4aca 100644 --- a/Include/dxwnd.h +++ b/Include/dxwnd.h @@ -139,6 +139,9 @@ #define SUPPORTHDTV 0x00800000 // Supports HDTV standard resolutions #define RELEASEMOUSE 0x01000000 // GetCursorPos returns centered coordinates when the mouse cursor is moved outside the window #define FRAMECOMPENSATION 0x02000000 // Compensate for frame border adding x,y displacement of (x) left border and (y) window title bar +#define HOTPATCH 0x04000000 // Use hot patching technique to handle obfuscated IAT +#define ENABLEHOTKEYS 0x08000000 // Enables hot keys +#define HOTPATCHALWAYS 0x10000000 // Force hot patching to every call // logging Tflags DWORD: #define OUTTRACE 0x00000001 // enables tracing to dxwnd.log in general diff --git a/build/dxwnd.dll b/build/dxwnd.dll index 3065a8c..c94aabe 100644 --- a/build/dxwnd.dll +++ b/build/dxwnd.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2402376ce446d1f0d06c36f3d7cfdda81e9052c03dde6bdcb0532bf9b5fc67ef -size 475648 +oid sha256:5b7f9b28cc1d19e29759f70cf2f5187a21319a31a0d8bd24d36c3314161d958c +size 483328 diff --git a/build/dxwnd.exe b/build/dxwnd.exe index 3a9a89f..e470991 100644 --- a/build/dxwnd.exe +++ b/build/dxwnd.exe @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e351b8cb36d1cfc02f5a113555e75bc72da15b5054595777b85b4fac2d993b6d +oid sha256:d705eb95e5834c95b4be2e8639c08df2c027df4edec2b0e3f2247e9f05da5d67 size 539136 diff --git a/build/dxwnd.ini b/build/dxwnd.ini index 56f6475..b74c8f2 100644 --- a/build/dxwnd.ini +++ b/build/dxwnd.ini @@ -1,5 +1,11 @@ +[keymapping] +timetoggle=0x72 +altf4=0x73 +timeslow=0x74 +timefast=0x75 + [window] -posx=930 -posy=192 +posx=50 +posy=50 sizx=320 sizy=200 diff --git a/build/exports/Age of Empires II.dxw b/build/exports/Age of Empires II.dxw new file mode 100644 index 0000000..13d078c --- /dev/null +++ b/build/exports/Age of Empires II.dxw @@ -0,0 +1,27 @@ +[target] +title0=Age of Empires II +path0=D:\Games\Age of Empires 2\empires2.exe +launchpath0= +module0= +opengllib0= +ver0=0 +coord0=0 +flag0=134218272 +flagg0=1207959552 +flagh0=20 +flagi0=4 +tflag0=0 +initx0=0 +inity0=0 +minx0=0 +miny0=0 +maxx0=0 +maxy0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 diff --git a/build/exports/Age of Empires.dxw b/build/exports/Age of Empires.dxw new file mode 100644 index 0000000..7eb4bf9 --- /dev/null +++ b/build/exports/Age of Empires.dxw @@ -0,0 +1,27 @@ +[target] +title0=Age of Empires +path0=D:\Games\Age of Empires\Empires.exe +launchpath0= +module0= +opengllib0= +ver0=0 +coord0=0 +flag0=134218272 +flagg0=1207959552 +flagh0=20 +flagi0=4 +tflag0=0 +initx0=0 +inity0=0 +minx0=0 +miny0=0 +maxx0=0 +maxy0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 diff --git a/build/exports/Doom 3.dxw b/build/exports/Doom 3.dxw new file mode 100644 index 0000000..9147f33 --- /dev/null +++ b/build/exports/Doom 3.dxw @@ -0,0 +1,27 @@ +[target] +title0=Doom 3 +path0=D:\Games\Doom 3\Doom3.exe +launchpath0= +module0= +opengllib0=OpenGL32.dll +ver0=9 +coord0=0 +flag0=134217760 +flagg0=1207959552 +flagh0=65557 +flagi0=69206020 +tflag0=6211 +initx0=0 +inity0=0 +minx0=0 +miny0=0 +maxx0=0 +maxy0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 diff --git a/build/exports/Little Big Adventure 2 (Windows patch).dxw b/build/exports/Little Big Adventure 2 (Windows patch).dxw new file mode 100644 index 0000000..0ef3bac --- /dev/null +++ b/build/exports/Little Big Adventure 2 (Windows patch).dxw @@ -0,0 +1,27 @@ +[target] +title0=Little Big Adventure 2 (Windows patch) +path0=C:\LBA2\TWINSEN.EXE +launchpath0= +module0= +opengllib0= +ver0=0 +coord0=0 +flag0=134218336 +flagg0=1480654848 +flagh0=84 +flagi0=4718592 +tflag0=6211 +initx0=0 +inity0=0 +minx0=0 +miny0=0 +maxx0=0 +maxy0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=1 +maxres0=-1 diff --git a/build/exports/Star Wars Episode I Racer.dxw b/build/exports/Star Wars Episode I Racer.dxw index b1a9c5a..edc9f2c 100644 --- a/build/exports/Star Wars Episode I Racer.dxw +++ b/build/exports/Star Wars Episode I Racer.dxw @@ -8,7 +8,7 @@ coord0=0 flag0=679493669 flagg0=1210056720 flagh0=8212 -flagi0=20 +flagi0=2097172 tflag0=0 initx0=0 inity0=0 @@ -24,3 +24,4 @@ maxfps0=0 initts0=0 winver0=0 maxres0=0 +launchpath0= diff --git a/build/exports/Tails Gear.dxw b/build/exports/Tails Gear.dxw new file mode 100644 index 0000000..a22cad6 --- /dev/null +++ b/build/exports/Tails Gear.dxw @@ -0,0 +1,27 @@ +[target] +title0=Tails Gear +path0=D:\Games\TailsGear078\DT_MAIN.exe +launchpath0= +module0= +opengllib0= +ver0=0 +coord0=0 +flag0=134217760 +flagg0=1207959552 +flagh0=20 +flagi0=4194308 +tflag0=0 +initx0=0 +inity0=0 +minx0=0 +miny0=0 +maxx0=0 +maxy0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 diff --git a/build/exports/Wacky Races.dxw b/build/exports/Wacky Races.dxw new file mode 100644 index 0000000..ad47700 --- /dev/null +++ b/build/exports/Wacky Races.dxw @@ -0,0 +1,27 @@ +[target] +title0=Wacky Races +path0=D:\Games\Wacky Races\WPC.EXE +launchpath0= +module0= +opengllib0= +ver0=0 +coord0=0 +flag0=134217760 +flagg0=134217728 +flagh0=20 +flagi0=4194308 +tflag0=64 +initx0=0 +inity0=0 +minx0=0 +miny0=0 +maxx0=0 +maxy0=0 +posx0=50 +posy0=50 +sizx0=800 +sizy0=600 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 diff --git a/build/exports/Wind Fantasy SP.dxw b/build/exports/Wind Fantasy SP.dxw new file mode 100644 index 0000000..434beba --- /dev/null +++ b/build/exports/Wind Fantasy SP.dxw @@ -0,0 +1,27 @@ +[target] +title0=Wind Fantasy SP +path0=D:\Games\Wind Fantasy SP\WF_SP.exe +launchpath0= +module0= +opengllib0= +ver0=0 +coord0=0 +flag0=134480416 +flagg0=1207959552 +flagh0=20 +flagi0=4194308 +tflag0=64 +initx0=0 +inity0=0 +minx0=0 +miny0=0 +maxx0=0 +maxy0=0 +posx0=50 +posy0=50 +sizx0=1280 +sizy0=960 +maxfps0=0 +initts0=0 +winver0=0 +maxres0=-1 diff --git a/build/exports/dxwnd.ini b/build/exports/dxwnd.ini new file mode 100644 index 0000000..c739d0d --- /dev/null +++ b/build/exports/dxwnd.ini @@ -0,0 +1,5 @@ +[window] +posx=1289 +posy=149 +sizx=465 +sizy=382 diff --git a/build/readme-relnotes.txt b/build/readme-relnotes.txt index 2b8f111..91e0e53 100644 --- a/build/readme-relnotes.txt +++ b/build/readme-relnotes.txt @@ -474,4 +474,22 @@ fix: fixed Launch field used with "Use DLL injection" flag fix: somehow improved "Use DLL injection" to avoid blocked tasks and allow exception handling v2.02.73 -to be written .... \ No newline at end of file +Add: preliminary support for (unchecked) "Run in window" flag. Now if the flag is unchecked, DxWnd does NOT force the program to run in a window. Still, some rendering modes are not working perfectly. +add: support for Unicode DefWindowProcW API call +fixed some differences in D3D CreateDevice vs. CreateDeviceEx handling +added "Window frame compensation" input flag to set the cursor position properly in "Sub Commando" +fix: properly handled the case of NULL WindowProc routine in CreateDialog/Indirect API calls. Makes "LEGO Stunt Rally" working. +added support for Unicode SystemParametersInfoW API call + +v2.02.74 +Add: API hot patching, thank to aqrit precious support. Now Doom 3 is played in window. +hooked GetCursorInfo call +fix: recalculate vsync timers in case of time stretching +fix: log failure for CreateWindowExW wchar api +Add: configurable keyboard commands +Add: timestretch toggle command +Add: "Enable hotkeys" flag to enable/disable hotkeys +fix: eliminated dialogs with timer warning messages (to be investigated...) +fix: moved "no banner" flag to main tab again +fix: potential string violation, possible cause for flash crash? + diff --git a/dll/advapi.cpp b/dll/advapi.cpp index 3ea6f4b..4937dec 100644 --- a/dll/advapi.cpp +++ b/dll/advapi.cpp @@ -8,13 +8,13 @@ #include "dxhelper.h" static HookEntry_Type Hooks[]={ - {"RegOpenKeyExA", NULL, (FARPROC *)&pRegOpenKeyEx, (FARPROC)extRegOpenKeyEx}, - {"RegCloseKey", NULL, (FARPROC *)&pRegCloseKey, (FARPROC)extRegCloseKey}, - {"RegQueryValueExA", NULL, (FARPROC *)&pRegQueryValueEx, (FARPROC)extRegQueryValueEx}, - {"RegCreateKeyA", NULL, (FARPROC *)&pRegCreateKey, (FARPROC)extRegCreateKey}, - {"RegCreateKeyExA", NULL, (FARPROC *)&pRegCreateKeyEx, (FARPROC)extRegCreateKeyEx}, - {"RegSetValueExA", NULL, (FARPROC *)&pRegSetValueEx, (FARPROC)extRegSetValueEx}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "RegOpenKeyExA", NULL, (FARPROC *)&pRegOpenKeyEx, (FARPROC)extRegOpenKeyEx}, + {HOOK_IAT_CANDIDATE, "RegCloseKey", NULL, (FARPROC *)&pRegCloseKey, (FARPROC)extRegCloseKey}, + {HOOK_IAT_CANDIDATE, "RegQueryValueExA", NULL, (FARPROC *)&pRegQueryValueEx, (FARPROC)extRegQueryValueEx}, + {HOOK_IAT_CANDIDATE, "RegCreateKeyA", NULL, (FARPROC *)&pRegCreateKey, (FARPROC)extRegCreateKey}, + {HOOK_IAT_CANDIDATE, "RegCreateKeyExA", NULL, (FARPROC *)&pRegCreateKeyEx, (FARPROC)extRegCreateKeyEx}, + {HOOK_IAT_CANDIDATE, "RegSetValueExA", NULL, (FARPROC *)&pRegSetValueEx, (FARPROC)extRegSetValueEx}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; void HookAdvApi32(HMODULE module) diff --git a/dll/ddraw.cpp b/dll/ddraw.cpp index 8db12d4..1015ead 100644 --- a/dll/ddraw.cpp +++ b/dll/ddraw.cpp @@ -16,6 +16,8 @@ #include "dxhelper.h" #include "syslibs.h" +extern BOOL IsChangeDisplaySettingsHotPatched; + // DirectDraw API HRESULT WINAPI extDirectDrawCreate(GUID FAR *, LPDIRECTDRAW FAR *, IUnknown FAR *); HRESULT WINAPI extDirectDrawCreateEx(GUID FAR *, LPDIRECTDRAW FAR *, REFIID, IUnknown FAR *); @@ -263,13 +265,13 @@ static void SetPixFmt(LPDDSURFACEDESC2); static void GetPixFmt(LPDDSURFACEDESC2); static HookEntry_Type ddHooks[]={ - {"DirectDrawCreate", (FARPROC)NULL, (FARPROC *)&pDirectDrawCreate, (FARPROC)extDirectDrawCreate}, - {"DirectDrawCreateEx", (FARPROC)NULL, (FARPROC *)&pDirectDrawCreateEx, (FARPROC)extDirectDrawCreateEx}, - {"DirectDrawEnumerateA", (FARPROC)NULL, (FARPROC *)&pDirectDrawEnumerate, (FARPROC)extDirectDrawEnumerate}, - {"DirectDrawEnumerateExA", (FARPROC)NULL, (FARPROC *)&pDirectDrawEnumerateEx, (FARPROC)extDirectDrawEnumerateEx}, - //{"DirectDrawEnumerateW", (FARPROC)NULL, (FARPROC *)&pDirectDrawEnumerateW, (FARPROC)extDirectDrawCreate}, - //{"DirectDrawEnumerateExW", (FARPROC)NULL, (FARPROC *)&pDirectDrawEnumerateExW, (FARPROC)extDirectDrawCreate}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "DirectDrawCreate", (FARPROC)NULL, (FARPROC *)&pDirectDrawCreate, (FARPROC)extDirectDrawCreate}, + {HOOK_IAT_CANDIDATE, "DirectDrawCreateEx", (FARPROC)NULL, (FARPROC *)&pDirectDrawCreateEx, (FARPROC)extDirectDrawCreateEx}, + {HOOK_IAT_CANDIDATE, "DirectDrawEnumerateA", (FARPROC)NULL, (FARPROC *)&pDirectDrawEnumerate, (FARPROC)extDirectDrawEnumerate}, + {HOOK_IAT_CANDIDATE, "DirectDrawEnumerateExA", (FARPROC)NULL, (FARPROC *)&pDirectDrawEnumerateEx, (FARPROC)extDirectDrawEnumerateEx}, + //{HOOK_IAT_CANDIDATE, "DirectDrawEnumerateW", (FARPROC)NULL, (FARPROC *)&pDirectDrawEnumerateW, (FARPROC)extDirectDrawCreate}, + //{HOOK_IAT_CANDIDATE, "DirectDrawEnumerateExW", (FARPROC)NULL, (FARPROC *)&pDirectDrawEnumerateExW, (FARPROC)extDirectDrawCreate}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; FARPROC Remap_ddraw_ProcAddress(LPCSTR proc, HMODULE hModule) @@ -1871,8 +1873,10 @@ HRESULT WINAPI extSetDisplayMode(int version, LPDIRECTDRAW lpdd, (*pGetDisplayMode)(lpdd, (LPDDSURFACEDESC)&ddsd); if(dxw.Windowize){ - dwwidth = ddsd.dwWidth; - dwheight = ddsd.dwHeight; + if(!IsChangeDisplaySettingsHotPatched){ + dwwidth = ddsd.dwWidth; + dwheight = ddsd.dwHeight; + } OutTraceDW("SetDisplayMode: fixing colordepth current=%d required=%d size=(%dx%d)\n", ddsd.ddpfPixelFormat.dwRGBBitCount, dwbpp, dwwidth, dwheight); } diff --git a/dll/dxhelper.cpp b/dll/dxhelper.cpp index abfe26e..300b1ce 100644 --- a/dll/dxhelper.cpp +++ b/dll/dxhelper.cpp @@ -1679,4 +1679,18 @@ char *ExplainRenderstateValue(DWORD Value) default : p="???"; break; } return p; -} \ No newline at end of file +} + +char *ExplainWfPFlags(DWORD c) +{ + static char eb[128]; + unsigned int l; + strcpy(eb,"CWP_"); + if(c & CWP_SKIPDISABLED) strcat(eb, "SKIPDISABLED+"); + if(c & CWP_SKIPINVISIBLE) strcat(eb, "SKIPINVISIBLE+"); + if(c & CWP_SKIPTRANSPARENT) strcat(eb, "SKIPTRANSPARENT+"); + l=strlen(eb); + if (l>strlen("CWP_")) eb[l-1]=0; // delete last '+' if any + else strcpy(eb,"CWP_ALL"); + return(eb); +} diff --git a/dll/dxhelper.h b/dll/dxhelper.h index a9a3e8d..75917ea 100644 --- a/dll/dxhelper.h +++ b/dll/dxhelper.h @@ -48,3 +48,4 @@ extern char *ExplainPaletteUse(UINT); extern char *ExplainRasterCaps(DWORD); extern char *ExplainD3DRenderState(DWORD); extern char *ExplainRenderstateValue(DWORD); +extern char *ExplainWfPFlags(DWORD); diff --git a/dll/dxhook.cpp b/dll/dxhook.cpp index 4d7619d..0528e20 100644 --- a/dll/dxhook.cpp +++ b/dll/dxhook.cpp @@ -34,6 +34,8 @@ 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); @@ -84,8 +86,8 @@ static char *Flag4Names[32]={ "LIMITSCREENRES", "NOFILLRECT", "HOOKGLIDE", "HIDEDESKTOP", "STRETCHTIMERS", "NOFLIPEMULATION", "NOTEXTURES", "RETURNNULLREF", "FINETIMING", "NATIVERES", "SUPPORTSVGA", "SUPPORTHDTV", - "RELEASEMOUSE", "", "", "", - "", "", "", "", + "RELEASEMOUSE", "FRAMECOMPENSATION", "HOTPATCH", "ENABLEHOTKEYS", + "HOTPATCHALWAYS", "", "", "", }; static char *TFlagNames[32]={ @@ -354,16 +356,6 @@ void SetHook(void *target, void *hookproc, void **hookedproc, char *hookname) void *HookAPI(HMODULE module, char *dll, void *apiproc, const char *apiname, void *hookproc) { - 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; - DWORD oldprotect; - void *org; - if(dxw.dwTFlags & OUTIMPORTTABLE) OutTrace("HookAPI: module=%x dll=%s apiproc=%x apiname=%s hookproc=%x\n", module, dll, apiproc, apiname, hookproc); @@ -374,87 +366,13 @@ void *HookAPI(HMODULE module, char *dll, void *apiproc, const char *apiname, voi return 0; } - base = (DWORD)module; - org = 0; // by default, ret = 0 => API not found - - __try{ - pnth = PIMAGE_NT_HEADERS(PBYTE(base) + PIMAGE_DOS_HEADER(base)->e_lfanew); - if(!pnth) { - OutTraceH("HookAPI: ERROR no PNTH at %d\n", __LINE__); - return 0; - } - rva = pnth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; - if(!rva) { - OutTraceH("HookAPI: ERROR no RVA at %d\n", __LINE__); - return 0; - } - pidesc = (PIMAGE_IMPORT_DESCRIPTOR)(base + rva); - - while(pidesc->FirstThunk){ - impmodule = (PSTR)(base + pidesc->Name); - if(!lstrcmpi(dll, impmodule)) { - //OutTraceH("HookAPI: dll=%s found at %x\n", dll, impmodule); - - ptaddr = (PIMAGE_THUNK_DATA)(base + (DWORD)pidesc->FirstThunk); - ptname = (pidesc->OriginalFirstThunk) ? (PIMAGE_THUNK_DATA)(base + (DWORD)pidesc->OriginalFirstThunk) : NULL; - - //if((apiproc==NULL) && (ptname==NULL)){ - // OutTraceH("HookAPI: unreacheable api=%s dll=%s\n", apiname, dll); - // return 0; - //} - - while(ptaddr->u1.Function){ - if (ptname){ - if(!IMAGE_SNAP_BY_ORDINAL(ptname->u1.Ordinal)){ - piname = (PIMAGE_IMPORT_BY_NAME)(base + (DWORD)ptname->u1.AddressOfData); - //OutTrace("examining by ptname name=%s\n", (char *)piname->Name); - if(!lstrcmpi(apiname, (char *)piname->Name)) break; - } - } - if (apiproc){ - // log not working !!! - //piname = (PIMAGE_IMPORT_BY_NAME)(base + (DWORD)ptaddr->u1.AddressOfData); - //OutTrace("examining by addr name=%s\n", (char *)piname->Name); - if(ptaddr->u1.Function == (DWORD)apiproc) break; - } - ptaddr ++; - if (ptname) ptname ++; - } - - if(ptaddr->u1.Function) { - org = (void *)ptaddr->u1.Function; - if(org == hookproc) return 0; // already hooked - - if(!VirtualProtect(&ptaddr->u1.Function, 4, PAGE_EXECUTE_READWRITE, &oldprotect)) { - OutTraceDW("HookAPI: VirtualProtect error %d at %d\n", GetLastError(), __LINE__); - return 0; - } - ptaddr->u1.Function = (DWORD)hookproc; - if(!VirtualProtect(&ptaddr->u1.Function, 4, oldprotect, &oldprotect)) { - OutTraceDW("HookAPI: VirtualProtect error %d at %d\n", GetLastError(), __LINE__); - return 0; - } - if (!FlushInstructionCache(GetCurrentProcess(), &ptaddr->u1.Function, 4)) { - OutTraceDW("HookAPI: FlushInstructionCache error %d at %d\n", GetLastError(), __LINE__); - return 0; - } - OutTraceH("HookAPI hook=%s address=%x->%x\n", apiname, org, hookproc); - - return org; - } - } - pidesc ++; - } - if(!pidesc->FirstThunk) { - OutTraceH("HookAPI: PE unreferenced function %s:%s\n", dll, apiname); - return 0; - } + if(dxw.dwFlags4 & HOTPATCHALWAYS) { + void *orig; + orig=HotPatch(apiproc, apiname, hookproc); + if(orig) return orig; } - __except(EXCEPTION_EXECUTE_HANDLER) - { - OutTraceDW("HookAPI: EXCEPTION hook=%s:%s Hook Failed.\n", dll, apiname); - } - return org; + + return IATPatch(module, dll, apiproc, apiname, hookproc); } // v.2.1.80: unified positioning logic into CalculateWindowPos routine @@ -751,11 +669,13 @@ LRESULT CALLBACK extWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lp static BOOL DoOnce = TRUE; static BOOL IsToBeLocked; static int LastTimeShift; + static int SaveTimeShift; + static BOOL TimeShiftToggle=TRUE; if(DoOnce){ DoOnce=FALSE; IsToBeLocked=(dxw.dwFlags1 & LOCKWINPOS); - LastTimeShift=dxw.TimeShift; + LastTimeShift=SaveTimeShift=dxw.TimeShift; } // v2.1.93: adjust clipping region @@ -799,8 +719,11 @@ LRESULT CALLBACK extWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lp } } - if(dxw.dwFlags4 & STRETCHTIMERS){ - if(LastTimeShift != dxw.TimeShift) dxw.RenewTimers(); + if(LastTimeShift != dxw.TimeShift){ + extern void SetVSyncDelays(LPDIRECTDRAW); + extern LPDIRECTDRAW lpPrimaryDD; + if(dxw.dwFlags4 & STRETCHTIMERS) dxw.RenewTimers(); + SetVSyncDelays(lpPrimaryDD); LastTimeShift=dxw.TimeShift; } @@ -987,39 +910,54 @@ LRESULT CALLBACK extWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lp // TerminateProcess(GetCurrentProcess(),0); // break; case WM_SYSKEYDOWN: - OutTraceW("event WM_SYSKEYDOWN wparam=%x lparam=%x\n", wparam, lparam); - switch (wparam){ - case VK_F12: + 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(); } - case VK_F11: + break; + case DXVK_REFRESH: dxw.ScreenRefresh(); break; - case VK_F10: + case DXVK_LOGTOGGLE: dx_ToggleLogging(); break; - case VK_F9: + case DXVK_PLOCKTOGGLE: dx_TogglePositionLock(hwnd); break; - //case VK_F8: - // dx_ToggleDC(); - // break; - case VK_F7: + case DXVK_FPSTOGGLE: dx_ToggleFPS(); break; - case VK_F6: - case VK_F5: + case DXVK_TIMEFAST: + case DXVK_TIMESLOW: if (dxw.dwFlags2 & TIMESTRETCH) { - if (wparam == VK_F5 && (dxw.TimeShift < 8)) dxw.TimeShift++; - if (wparam == VK_F6 && (dxw.TimeShift > -8)) dxw.TimeShift--; - OutTrace("Time Stretch: shift=%d speed=%s\n", dxw.TimeShift, dxw.GetTSCaption()); + 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 VK_F4: + 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); @@ -1105,7 +1043,7 @@ static void RecoverScreenMode() CurrentDevMode.dmPelsWidth, CurrentDevMode.dmPelsHeight, CurrentDevMode.dmBitsPerPel, InitDevMode.dmPelsWidth, InitDevMode.dmPelsHeight, InitDevMode.dmBitsPerPel); InitDevMode.dmFields = (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT); - res=(*pChangeDisplaySettingsA)(&InitDevMode, 0); + res=(*pChangeDisplaySettingsExA)(NULL, &InitDevMode, NULL, 0, NULL); if(res) OutTraceE("ChangeDisplaySettings: ERROR err=%d at %d\n", GetLastError(), __LINE__); } @@ -1118,7 +1056,7 @@ void SwitchTo16BPP() CurrentDevMode.dmPelsWidth, CurrentDevMode.dmPelsHeight, CurrentDevMode.dmBitsPerPel); CurrentDevMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; CurrentDevMode.dmBitsPerPel = 16; - res=(*pChangeDisplaySettingsA)(&CurrentDevMode, CDS_UPDATEREGISTRY); + res=(*pChangeDisplaySettingsExA)(NULL, &CurrentDevMode, NULL, CDS_UPDATEREGISTRY, NULL); if(res) OutTraceE("ChangeDisplaySettings: ERROR err=%d at %d\n", GetLastError(), __LINE__); } @@ -1132,7 +1070,7 @@ static void LockScreenMode(DWORD dmPelsWidth, DWORD dmPelsHeight, DWORD dmBitsPe if( (dmPelsWidth != InitDevMode.dmPelsWidth) || (dmPelsHeight !=InitDevMode.dmPelsHeight) || (dmBitsPerPel != InitDevMode.dmBitsPerPel)){ - res=(*pChangeDisplaySettingsA)(&InitDevMode, 0); + res=(*pChangeDisplaySettingsExA)(NULL, &InitDevMode, NULL, 0, NULL); if(res) OutTraceE("ChangeDisplaySettings: ERROR err=%d at %d\n", GetLastError(), __LINE__); } } @@ -1615,13 +1553,57 @@ FARPROC RemapLibrary(LPCSTR proc, HMODULE hModule, HookEntry_Type *Hooks) void HookLibrary(HMODULE hModule, HookEntry_Type *Hooks, char *DLLName) { - void *tmp; for(; Hooks->APIName; Hooks++){ - tmp = HookAPI(hModule, DLLName, Hooks->OriginalAddress, Hooks->APIName, Hooks->HookerAddress); - if(tmp && Hooks->StoreAddress) *(Hooks->StoreAddress) = (FARPROC)tmp; + //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->OriginalAddress && Hooks->StoreAddress)){ // API address and save ptr available + // Hot Patch + 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; + } } } +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++) diff --git a/dll/dxhook.h b/dll/dxhook.h index 2e776ee..75df21e 100644 --- a/dll/dxhook.h +++ b/dll/dxhook.h @@ -35,7 +35,16 @@ extern FARPROC Remap_vfw_ProcAddress(LPCSTR, HMODULE); extern FARPROC Remap_AdvApi32_ProcAddress(LPCSTR, HMODULE); extern FARPROC Remap_Glide_ProcAddress(LPCSTR, HMODULE); +typedef enum { + HOOK_IAT_CANDIDATE = 0, + HOOK_HOT_CANDIDATE, + HOOK_IAT_LINKED, + HOOK_HOT_LINKED +} HookEntry_Status; + + typedef struct { + HookEntry_Status HookStatus; char *APIName; FARPROC OriginalAddress; FARPROC *StoreAddress; @@ -45,3 +54,4 @@ typedef struct { extern FARPROC RemapLibrary(LPCSTR, HMODULE, HookEntry_Type *); extern void HookLibrary(HMODULE, HookEntry_Type *, char *); extern void HookLibInit(HookEntry_Type *); +extern BOOL IsHotPatched(HookEntry_Type *, char *); diff --git a/dll/dxwcore.cpp b/dll/dxwcore.cpp index 827c689..fa756bd 100644 --- a/dll/dxwcore.cpp +++ b/dll/dxwcore.cpp @@ -100,6 +100,7 @@ void dxwCore::InitTarget(TARGETMAP *target) pTimeShifter = TimeShifterCoarse; pTimeShifter64 = TimeShifter64Coarse; } + if(dwFlags4 & ENABLEHOTKEYS) MapKeysInit(); } void dxwCore::SetScreenSize(void) @@ -1531,14 +1532,16 @@ void dxwCore::PopTimer(UINT uTimerId) // this should never happen, unless there are more than 1 timer! char msg[256]; sprintf(msg,"PopTimer: TimerType=%x last=%x\n", TIMER_TYPE_WINMM, TimerEvent.dwTimerType); - MessageBox(0, msg, "PopTimer", MB_OK | MB_ICONEXCLAMATION); + //MessageBox(0, msg, "PopTimer", MB_OK | MB_ICONEXCLAMATION); + OutTraceE(msg); return; } if(uTimerId != TimerEvent.t.uTimerId){ // this should never happen, unless there are more than 1 timer! char msg[256]; sprintf(msg,"PopTimer: TimerId=%x last=%x\n", uTimerId, TimerEvent.t.uTimerId); - MessageBox(0, msg, "PopTimer", MB_OK | MB_ICONEXCLAMATION); + //MessageBox(0, msg, "PopTimer", MB_OK | MB_ICONEXCLAMATION); + OutTraceE(msg); return; } TimerEvent.dwTimerType = TIMER_TYPE_NONE; @@ -1551,14 +1554,16 @@ void dxwCore::PopTimer(HWND hWnd, UINT_PTR nIDEvent) // this should never happen, unless there are more than 1 timer! char msg[256]; sprintf(msg,"PopTimer: TimerType=%x last=%x\n", TIMER_TYPE_WINMM, TimerEvent.dwTimerType); - MessageBox(0, msg, "PopTimer", MB_OK | MB_ICONEXCLAMATION); + //MessageBox(0, msg, "PopTimer", MB_OK | MB_ICONEXCLAMATION); + OutTraceE(msg); return; } if(nIDEvent != TimerEvent.t.nIDEvent){ // this should never happen, unless there are more than 1 timer! char msg[256]; sprintf(msg,"PopTimer: TimerId=%x last=%x\n", nIDEvent, TimerEvent.t.nIDEvent); - MessageBox(0, msg, "PopTimer", MB_OK | MB_ICONEXCLAMATION); + //MessageBox(0, msg, "PopTimer", MB_OK | MB_ICONEXCLAMATION); + OutTraceE(msg); return; } TimerEvent.dwTimerType = TIMER_TYPE_NONE; @@ -1645,3 +1650,69 @@ BOOL dxwCore::CheckScreenResolution(unsigned int w, unsigned int h) } return TRUE; } + +#ifdef COMPATIBLEMODE +void dxwCore::MapKeysInit(){} + +UINT dxwCore::MapKeysConfig(UINT message, LPARAM lparam, WPARAM wparam) +{ + int vkey; + char *caption; + if(message!=WM_SYSKEYDOWN) return DXVK_NONE; + switch(wparam){ + case VK_F12: vkey=DXVK_CLIPTOGGLE; caption="CLIPCURSORTOGGLE"; break; + case VK_F11: vkey=DXVK_REFRESH; caption="REFRESH"; break; + case VK_F10: vkey=DXVK_LOGTOGGLE; caption="LOGTOGGLE"; break; + case VK_F9: vkey=DXVK_PLOCKTOGGLE; caption="POSITIONLOCKTOGGLE"; break; + case VK_F7: vkey=DXVK_FPSTOGGLE; caption="FPSTOGGLE"; break; + case VK_F6: vkey=DXVK_TIMEFAST; caption="TIMESTRETCHFAST"; break; + case VK_F5: vkey=DXVK_TIMESLOW; caption="TIMESTRETCHSLOW"; break; + case VK_F4: vkey=DXVK_ALTF4; caption="ALTF4"; break; + default: vkey=DXVK_NONE; break; + } + if(vkey) OutTraceDW("MapKeysConfig: ret=%x(%s)\n", vkey); + return vkey; +} +#else +UINT VKeyConfig[DXVK_SIZE]; + +void dxwCore::MapKeysInit() + +{ + char InitPath[MAX_PATH]; + char *p; + DWORD dwAttrib; + + dwAttrib = GetFileAttributes("dxwnd.dll"); + if (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) return; + GetModuleFileName(GetModuleHandle("dxwnd"), InitPath, MAX_PATH); + p=&InitPath[strlen(InitPath)-strlen("dxwnd.dll")]; + strcpy(p, "dxwnd.ini"); + VKeyConfig[DXVK_NONE]=DXVK_NONE; + VKeyConfig[DXVK_CLIPTOGGLE]= GetPrivateProfileInt("keymapping", "cliptoggle", 0, InitPath); + VKeyConfig[DXVK_REFRESH]= GetPrivateProfileInt("keymapping", "refresh", 0, InitPath); + VKeyConfig[DXVK_LOGTOGGLE]= GetPrivateProfileInt("keymapping", "logtoggle", 0, InitPath); + VKeyConfig[DXVK_PLOCKTOGGLE]= GetPrivateProfileInt("keymapping", "plocktoggle", 0, InitPath); + VKeyConfig[DXVK_FPSTOGGLE]= GetPrivateProfileInt("keymapping", "fpstoggle", 0, InitPath); + VKeyConfig[DXVK_TIMEFAST]= GetPrivateProfileInt("keymapping", "timefast", 0, InitPath); + VKeyConfig[DXVK_TIMESLOW]= GetPrivateProfileInt("keymapping", "timeslow", 0, InitPath); + VKeyConfig[DXVK_TIMETOGGLE]= GetPrivateProfileInt("keymapping", "timetoggle", 0, InitPath); + VKeyConfig[DXVK_ALTF4]= GetPrivateProfileInt("keymapping", "altf4", 0x73, InitPath); + + int idx; + for(idx=1; idx. #include "dxwnd.h" #include "dxwcore.hpp" -#define VERSION "2.02.73" +#define VERSION "2.02.74" #define DDTHREADLOCK 1 @@ -212,9 +212,10 @@ LRESULT CALLBACK HookProc(int ncode, WPARAM wparam, LPARAM lparam) void InjectHook() { - char name[MAX_PATH]; + char name[MAX_PATH+1]; int i; GetModuleFileName(0, name, MAX_PATH); + name[MAX_PATH]=0; // terminator for(i = 0; name[i]; i ++) name[i] = tolower(name[i]); for(i = 0; pMapping[i].path[0]; i ++){ if(!strncmp(name, pMapping[i].path, strlen(name))){ diff --git a/dll/dxwnd.vs2008.suo b/dll/dxwnd.vs2008.suo index ce9e13368e46de673f0faf32ec2688713f5c5ebe..547d9d921e30cf3e46776bf6dc63394717a828c5 100644 GIT binary patch literal 43520 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 DHcV%r literal 288768 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󯮯${FBw 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 + + + + diff --git a/dll/gdi32.cpp b/dll/gdi32.cpp index 705a153..1117be6 100644 --- a/dll/gdi32.cpp +++ b/dll/gdi32.cpp @@ -37,118 +37,118 @@ ExtTextOutA_Type pExtTextOutA = NULL; static HookEntry_Type Hooks[]={ - {"GetDeviceCaps", (FARPROC)GetDeviceCaps, (FARPROC *)&pGDIGetDeviceCaps, (FARPROC)extGetDeviceCaps}, - {"ScaleWindowExtEx", (FARPROC)ScaleWindowExtEx, (FARPROC *)&pGDIScaleWindowExtEx, (FARPROC)extScaleWindowExtEx}, - {"SaveDC", (FARPROC)SaveDC, (FARPROC *)&pGDISaveDC, (FARPROC)extGDISaveDC}, - {"RestoreDC", (FARPROC)RestoreDC, (FARPROC *)&pGDIRestoreDC, (FARPROC)extGDIRestoreDC}, - {"AnimatePalette", (FARPROC)AnimatePalette, (FARPROC *)&pAnimatePalette, (FARPROC)extAnimatePalette}, - {"CreatePalette", (FARPROC)CreatePalette, (FARPROC *)&pGDICreatePalette, (FARPROC)extGDICreatePalette}, - {"SelectPalette", (FARPROC)SelectPalette, (FARPROC *)&pGDISelectPalette, (FARPROC)extSelectPalette}, - {"RealizePalette", (FARPROC)RealizePalette, (FARPROC *)&pGDIRealizePalette, (FARPROC)extRealizePalette}, - {"GetSystemPaletteEntries", (FARPROC)GetSystemPaletteEntries, (FARPROC *)&pGDIGetSystemPaletteEntries, (FARPROC)extGetSystemPaletteEntries}, - {"SetSystemPaletteUse", (FARPROC)SetSystemPaletteUse, (FARPROC *)&pSetSystemPaletteUse, (FARPROC)extSetSystemPaletteUse}, - {"StretchDIBits", (FARPROC)StretchDIBits, (FARPROC *)&pStretchDIBits, (FARPROC)extStretchDIBits}, - //{"SetDIBitsToDevice", (FARPROC)NULL, (FARPROC *)&pSetDIBitsToDevice, (FARPROC)extSetDIBitsToDevice}, - //{"CreateCompatibleBitmap", (FARPROC)NULL, (FARPROC *)&pCreateCompatibleBitmap, (FARPROC)extCreateCompatibleBitmap}, - //{"SetMapMode", (FARPROC)NULL, (FARPROC *)NULL, (FARPROC)extSetMapMode}, - {"SetPixelFormat", (FARPROC)NULL, (FARPROC *)&pGDISetPixelFormat, (FARPROC)extGDISetPixelFormat}, - {"GetPixelFormat", (FARPROC)NULL, (FARPROC *)&pGDIGetPixelFormat, (FARPROC)extGDIGetPixelFormat}, - {"ChoosePixelFormat", (FARPROC)NULL, (FARPROC *)&pChoosePixelFormat, (FARPROC)extChoosePixelFormat}, - {"DescribePixelFormat", (FARPROC)NULL, (FARPROC *)&pDescribePixelFormat, (FARPROC)extDescribePixelFormat}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "GetDeviceCaps", (FARPROC)GetDeviceCaps, (FARPROC *)&pGDIGetDeviceCaps, (FARPROC)extGetDeviceCaps}, + {HOOK_IAT_CANDIDATE, "ScaleWindowExtEx", (FARPROC)ScaleWindowExtEx, (FARPROC *)&pGDIScaleWindowExtEx, (FARPROC)extScaleWindowExtEx}, + {HOOK_IAT_CANDIDATE, "SaveDC", (FARPROC)SaveDC, (FARPROC *)&pGDISaveDC, (FARPROC)extGDISaveDC}, + {HOOK_IAT_CANDIDATE, "RestoreDC", (FARPROC)RestoreDC, (FARPROC *)&pGDIRestoreDC, (FARPROC)extGDIRestoreDC}, + {HOOK_IAT_CANDIDATE, "AnimatePalette", (FARPROC)AnimatePalette, (FARPROC *)&pAnimatePalette, (FARPROC)extAnimatePalette}, + {HOOK_IAT_CANDIDATE, "CreatePalette", (FARPROC)CreatePalette, (FARPROC *)&pGDICreatePalette, (FARPROC)extGDICreatePalette}, + {HOOK_IAT_CANDIDATE, "SelectPalette", (FARPROC)SelectPalette, (FARPROC *)&pGDISelectPalette, (FARPROC)extSelectPalette}, + {HOOK_IAT_CANDIDATE, "RealizePalette", (FARPROC)RealizePalette, (FARPROC *)&pGDIRealizePalette, (FARPROC)extRealizePalette}, + {HOOK_IAT_CANDIDATE, "GetSystemPaletteEntries", (FARPROC)GetSystemPaletteEntries, (FARPROC *)&pGDIGetSystemPaletteEntries, (FARPROC)extGetSystemPaletteEntries}, + {HOOK_IAT_CANDIDATE, "SetSystemPaletteUse", (FARPROC)SetSystemPaletteUse, (FARPROC *)&pSetSystemPaletteUse, (FARPROC)extSetSystemPaletteUse}, + {HOOK_IAT_CANDIDATE, "StretchDIBits", (FARPROC)StretchDIBits, (FARPROC *)&pStretchDIBits, (FARPROC)extStretchDIBits}, + //{HOOK_IAT_CANDIDATE, "SetDIBitsToDevice", (FARPROC)NULL, (FARPROC *)&pSetDIBitsToDevice, (FARPROC)extSetDIBitsToDevice}, + //{HOOK_IAT_CANDIDATE, "CreateCompatibleBitmap", (FARPROC)NULL, (FARPROC *)&pCreateCompatibleBitmap, (FARPROC)extCreateCompatibleBitmap}, + //{HOOK_IAT_CANDIDATE, "SetMapMode", (FARPROC)NULL, (FARPROC *)NULL, (FARPROC)extSetMapMode}, + {HOOK_IAT_CANDIDATE, "SetPixelFormat", (FARPROC)NULL, (FARPROC *)&pGDISetPixelFormat, (FARPROC)extGDISetPixelFormat}, + {HOOK_IAT_CANDIDATE, "GetPixelFormat", (FARPROC)NULL, (FARPROC *)&pGDIGetPixelFormat, (FARPROC)extGDIGetPixelFormat}, + {HOOK_IAT_CANDIDATE, "ChoosePixelFormat", (FARPROC)NULL, (FARPROC *)&pChoosePixelFormat, (FARPROC)extChoosePixelFormat}, + {HOOK_IAT_CANDIDATE, "DescribePixelFormat", (FARPROC)NULL, (FARPROC *)&pDescribePixelFormat, (FARPROC)extDescribePixelFormat}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type RemapHooks[]={ - {"SetViewportOrgEx", (FARPROC)SetViewportOrgEx, (FARPROC *)&pSetViewportOrgEx, (FARPROC)extSetViewportOrgEx}, // needed in ShowBanner - {"SetViewportExtEx", (FARPROC)NULL, (FARPROC *)&pSetViewportExtEx, (FARPROC)extSetViewportExtEx}, - {"GetViewportOrgEx", (FARPROC)NULL, (FARPROC *)&pGetViewportOrgEx, (FARPROC)extGetViewportOrgEx}, - {"GetWindowOrgEx", (FARPROC)NULL, (FARPROC *)&pGetWindowOrgEx, (FARPROC)extGetWindowOrgEx}, - {"SetWindowOrgEx", (FARPROC)NULL, (FARPROC *)&pSetWindowOrgEx, (FARPROC)extSetWindowOrgEx}, - {"GetCurrentPositionEx", (FARPROC)NULL, (FARPROC *)&pGetCurrentPositionEx, (FARPROC)extGetCurrentPositionEx}, - {"SetDIBitsToDevice", (FARPROC)NULL, (FARPROC *)&pSetDIBitsToDevice, (FARPROC)extSetDIBitsToDevice}, // does the stretching - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "SetViewportOrgEx", (FARPROC)SetViewportOrgEx, (FARPROC *)&pSetViewportOrgEx, (FARPROC)extSetViewportOrgEx}, // needed in ShowBanner + {HOOK_IAT_CANDIDATE, "SetViewportExtEx", (FARPROC)NULL, (FARPROC *)&pSetViewportExtEx, (FARPROC)extSetViewportExtEx}, + {HOOK_IAT_CANDIDATE, "GetViewportOrgEx", (FARPROC)NULL, (FARPROC *)&pGetViewportOrgEx, (FARPROC)extGetViewportOrgEx}, + {HOOK_IAT_CANDIDATE, "GetWindowOrgEx", (FARPROC)NULL, (FARPROC *)&pGetWindowOrgEx, (FARPROC)extGetWindowOrgEx}, + {HOOK_IAT_CANDIDATE, "SetWindowOrgEx", (FARPROC)NULL, (FARPROC *)&pSetWindowOrgEx, (FARPROC)extSetWindowOrgEx}, + {HOOK_IAT_CANDIDATE, "GetCurrentPositionEx", (FARPROC)NULL, (FARPROC *)&pGetCurrentPositionEx, (FARPROC)extGetCurrentPositionEx}, + {HOOK_IAT_CANDIDATE, "SetDIBitsToDevice", (FARPROC)NULL, (FARPROC *)&pSetDIBitsToDevice, (FARPROC)extSetDIBitsToDevice}, // does the stretching + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type ScaledHooks[]={ - {"Rectangle", (FARPROC)Rectangle, (FARPROC *)&pGDIRectangle, (FARPROC)extRectangle}, - {"TextOutA", (FARPROC)TextOutA, (FARPROC *)&pGDITextOutA, (FARPROC)extTextOutA}, - {"GetClipBox", (FARPROC)NULL, (FARPROC *)&pGDIGetClipBox, (FARPROC)extGetClipBox}, - {"GetRegionBox", (FARPROC)NULL, (FARPROC *)&pGDIGetRegionBox, (FARPROC)extGetRegionBox}, - {"Polyline", (FARPROC)NULL, (FARPROC *)&pPolyline, (FARPROC)extPolyline}, - {"PolyBezierTo", (FARPROC)NULL, (FARPROC *)&pPolyBezierTo, (FARPROC)extPolyBezierTo}, - {"PolylineTo", (FARPROC)NULL, (FARPROC *)&pPolylineTo, (FARPROC)extPolylineTo}, - {"PolyDraw", (FARPROC)NULL, (FARPROC *)&pPolyDraw, (FARPROC)extPolyDraw}, - {"MoveToEx", (FARPROC)NULL, (FARPROC *)&pMoveToEx, (FARPROC)extMoveToEx}, - {"ArcTo", (FARPROC)NULL, (FARPROC *)&pArcTo, (FARPROC)extArcTo}, - {"LineTo", (FARPROC)NULL, (FARPROC *)&pLineTo, (FARPROC)extLineTo}, - {"SetPixel", (FARPROC)NULL, (FARPROC *)&pSetPixel, (FARPROC)extSetPixel}, - {"Ellipse", (FARPROC)NULL, (FARPROC *)&pEllipse, (FARPROC)extEllipse}, - {"Polygon", (FARPROC)NULL, (FARPROC *)&pPolygon, (FARPROC)extPolygon}, - {"Arc", (FARPROC)NULL, (FARPROC *)&pArc, (FARPROC)extArc}, + {HOOK_IAT_CANDIDATE, "Rectangle", (FARPROC)Rectangle, (FARPROC *)&pGDIRectangle, (FARPROC)extRectangle}, + {HOOK_IAT_CANDIDATE, "TextOutA", (FARPROC)TextOutA, (FARPROC *)&pGDITextOutA, (FARPROC)extTextOutA}, + {HOOK_IAT_CANDIDATE, "GetClipBox", (FARPROC)NULL, (FARPROC *)&pGDIGetClipBox, (FARPROC)extGetClipBox}, + {HOOK_IAT_CANDIDATE, "GetRegionBox", (FARPROC)NULL, (FARPROC *)&pGDIGetRegionBox, (FARPROC)extGetRegionBox}, + {HOOK_IAT_CANDIDATE, "Polyline", (FARPROC)NULL, (FARPROC *)&pPolyline, (FARPROC)extPolyline}, + {HOOK_IAT_CANDIDATE, "PolyBezierTo", (FARPROC)NULL, (FARPROC *)&pPolyBezierTo, (FARPROC)extPolyBezierTo}, + {HOOK_IAT_CANDIDATE, "PolylineTo", (FARPROC)NULL, (FARPROC *)&pPolylineTo, (FARPROC)extPolylineTo}, + {HOOK_IAT_CANDIDATE, "PolyDraw", (FARPROC)NULL, (FARPROC *)&pPolyDraw, (FARPROC)extPolyDraw}, + {HOOK_IAT_CANDIDATE, "MoveToEx", (FARPROC)NULL, (FARPROC *)&pMoveToEx, (FARPROC)extMoveToEx}, + {HOOK_IAT_CANDIDATE, "ArcTo", (FARPROC)NULL, (FARPROC *)&pArcTo, (FARPROC)extArcTo}, + {HOOK_IAT_CANDIDATE, "LineTo", (FARPROC)NULL, (FARPROC *)&pLineTo, (FARPROC)extLineTo}, + {HOOK_IAT_CANDIDATE, "SetPixel", (FARPROC)NULL, (FARPROC *)&pSetPixel, (FARPROC)extSetPixel}, + {HOOK_IAT_CANDIDATE, "Ellipse", (FARPROC)NULL, (FARPROC *)&pEllipse, (FARPROC)extEllipse}, + {HOOK_IAT_CANDIDATE, "Polygon", (FARPROC)NULL, (FARPROC *)&pPolygon, (FARPROC)extPolygon}, + {HOOK_IAT_CANDIDATE, "Arc", (FARPROC)NULL, (FARPROC *)&pArc, (FARPROC)extArc}, // commented out since they alter text on screen...... (see Imperialism II difficulty level menu) - //{"CreateEllipticRgn", (FARPROC)NULL, (FARPROC *)&pCreateEllipticRgn, (FARPROC)extCreateEllipticRgn}, - //{"CreateEllipticRgnIndirect", (FARPROC)NULL, (FARPROC *)&pCreateEllipticRgnIndirect, (FARPROC)extCreateEllipticRgnIndirect}, - //{"CreateRectRgn", (FARPROC)NULL, (FARPROC *)&pCreateRectRgn, (FARPROC)extCreateRectRgn}, - //{"CreateRectRgnIndirect", (FARPROC)NULL, (FARPROC *)&pCreateRectRgnIndirect, (FARPROC)extCreateRectRgnIndirect}, - //{"CreatePolygonRgn", (FARPROC)NULL, (FARPROC *)&pCreatePolygonRgn, (FARPROC)extCreatePolygonRgn}, + //{HOOK_IAT_CANDIDATE, "CreateEllipticRgn", (FARPROC)NULL, (FARPROC *)&pCreateEllipticRgn, (FARPROC)extCreateEllipticRgn}, + //{HOOK_IAT_CANDIDATE, "CreateEllipticRgnIndirect", (FARPROC)NULL, (FARPROC *)&pCreateEllipticRgnIndirect, (FARPROC)extCreateEllipticRgnIndirect}, + //{HOOK_IAT_CANDIDATE, "CreateRectRgn", (FARPROC)NULL, (FARPROC *)&pCreateRectRgn, (FARPROC)extCreateRectRgn}, + //{HOOK_IAT_CANDIDATE, "CreateRectRgnIndirect", (FARPROC)NULL, (FARPROC *)&pCreateRectRgnIndirect, (FARPROC)extCreateRectRgnIndirect}, + //{HOOK_IAT_CANDIDATE, "CreatePolygonRgn", (FARPROC)NULL, (FARPROC *)&pCreatePolygonRgn, (FARPROC)extCreatePolygonRgn}, // same as emulated GDI ... - {"CreateCompatibleDC", (FARPROC)CreateCompatibleDC, (FARPROC *)&pGDICreateCompatibleDC, (FARPROC)extGDICreateCompatibleDC}, - {"DeleteDC", (FARPROC)DeleteDC, (FARPROC *)&pGDIDeleteDC, (FARPROC)extGDIDeleteDC}, - {"CreateDCA", (FARPROC)CreateDCA, (FARPROC *)&pGDICreateDC, (FARPROC)extGDICreateDC}, + {HOOK_IAT_CANDIDATE, "CreateCompatibleDC", (FARPROC)CreateCompatibleDC, (FARPROC *)&pGDICreateCompatibleDC, (FARPROC)extGDICreateCompatibleDC}, + {HOOK_IAT_CANDIDATE, "DeleteDC", (FARPROC)DeleteDC, (FARPROC *)&pGDIDeleteDC, (FARPROC)extGDIDeleteDC}, + {HOOK_IAT_CANDIDATE, "CreateDCA", (FARPROC)CreateDCA, (FARPROC *)&pGDICreateDC, (FARPROC)extGDICreateDC}, // CreateDCW ..... - {"BitBlt", (FARPROC)BitBlt, (FARPROC *)&pGDIBitBlt, (FARPROC)extGDIBitBlt}, - {"StretchBlt", (FARPROC)StretchBlt, (FARPROC *)&pGDIStretchBlt, (FARPROC)extGDIStretchBlt}, - {"PatBlt", (FARPROC)PatBlt, (FARPROC *)&pGDIPatBlt, (FARPROC)extGDIPatBlt}, - {"MaskBlt", (FARPROC)NULL, (FARPROC *)&pMaskBlt, (FARPROC)extMaskBlt}, - {"ExtTextOutA", (FARPROC)ExtTextOutA, (FARPROC *)&pExtTextOutA, (FARPROC)extExtTextOutA}, - {"ExtTextOutW", (FARPROC)ExtTextOutW, (FARPROC *)&pExtTextOutW, (FARPROC)extExtTextOutW}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "BitBlt", (FARPROC)BitBlt, (FARPROC *)&pGDIBitBlt, (FARPROC)extGDIBitBlt}, + {HOOK_IAT_CANDIDATE, "StretchBlt", (FARPROC)StretchBlt, (FARPROC *)&pGDIStretchBlt, (FARPROC)extGDIStretchBlt}, + {HOOK_IAT_CANDIDATE, "PatBlt", (FARPROC)PatBlt, (FARPROC *)&pGDIPatBlt, (FARPROC)extGDIPatBlt}, + {HOOK_IAT_CANDIDATE, "MaskBlt", (FARPROC)NULL, (FARPROC *)&pMaskBlt, (FARPROC)extMaskBlt}, + {HOOK_IAT_CANDIDATE, "ExtTextOutA", (FARPROC)ExtTextOutA, (FARPROC *)&pExtTextOutA, (FARPROC)extExtTextOutA}, + {HOOK_IAT_CANDIDATE, "ExtTextOutW", (FARPROC)ExtTextOutW, (FARPROC *)&pExtTextOutW, (FARPROC)extExtTextOutW}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type EmulateHooks[]={ // useless CreateCompatibleDC: it maps VirtualHDC on top of VirtualHDC, then does nothing, unless when asked to operate on 0!.... - //{"CreateCompatibleDC", (FARPROC)CreateCompatibleDC, (FARPROC *)&pGDICreateCompatibleDC, (FARPROC)extEMUCreateCompatibleDC}, + //{HOOK_IAT_CANDIDATE, "CreateCompatibleDC", (FARPROC)CreateCompatibleDC, (FARPROC *)&pGDICreateCompatibleDC, (FARPROC)extEMUCreateCompatibleDC}, // useless DeleteDC: it's just a proxy - //{"DeleteDC", (FARPROC)DeleteDC, (FARPROC *)&pGDIDeleteDC, (FARPROC)extGDIDeleteDC}, - {"CreateDCA", (FARPROC)CreateDCA, (FARPROC *)&pGDICreateDC, (FARPROC)extGDICreateDC}, + //{HOOK_IAT_CANDIDATE, "DeleteDC", (FARPROC)DeleteDC, (FARPROC *)&pGDIDeleteDC, (FARPROC)extGDIDeleteDC}, + {HOOK_IAT_CANDIDATE, "CreateDCA", (FARPROC)CreateDCA, (FARPROC *)&pGDICreateDC, (FARPROC)extGDICreateDC}, // CreateDCW ..... - {"GetObjectType", (FARPROC)GetObjectType, (FARPROC *)&pGetObjectType, (FARPROC)extGetObjectType}, - {"GetClipBox", (FARPROC)NULL, (FARPROC *)&pGDIGetClipBox, (FARPROC)extGetClipBox}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "GetObjectType", (FARPROC)GetObjectType, (FARPROC *)&pGetObjectType, (FARPROC)extGetObjectType}, + {HOOK_IAT_CANDIDATE, "GetClipBox", (FARPROC)NULL, (FARPROC *)&pGDIGetClipBox, (FARPROC)extGetClipBox}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type DDHooks[]={ - {"CreateCompatibleDC", (FARPROC)CreateCompatibleDC, (FARPROC *)&pGDICreateCompatibleDC, (FARPROC)extDDCreateCompatibleDC}, - {"DeleteDC", (FARPROC)DeleteDC, (FARPROC *)&pGDIDeleteDC, (FARPROC)extDDDeleteDC}, - {"CreateDCA", (FARPROC)CreateDCA, (FARPROC *)&pGDICreateDC, (FARPROC)extDDCreateDC}, - {"BitBlt", (FARPROC)BitBlt, (FARPROC *)&pGDIBitBlt, (FARPROC)extDDBitBlt}, - {"StretchBlt", (FARPROC)StretchBlt, (FARPROC *)&pGDIStretchBlt, (FARPROC)extDDStretchBlt}, - {"GetClipBox", (FARPROC)NULL, (FARPROC *)&pGDIGetClipBox, (FARPROC)extGetClipBox}, + {HOOK_IAT_CANDIDATE, "CreateCompatibleDC", (FARPROC)CreateCompatibleDC, (FARPROC *)&pGDICreateCompatibleDC, (FARPROC)extDDCreateCompatibleDC}, + {HOOK_IAT_CANDIDATE, "DeleteDC", (FARPROC)DeleteDC, (FARPROC *)&pGDIDeleteDC, (FARPROC)extDDDeleteDC}, + {HOOK_IAT_CANDIDATE, "CreateDCA", (FARPROC)CreateDCA, (FARPROC *)&pGDICreateDC, (FARPROC)extDDCreateDC}, + {HOOK_IAT_CANDIDATE, "BitBlt", (FARPROC)BitBlt, (FARPROC *)&pGDIBitBlt, (FARPROC)extDDBitBlt}, + {HOOK_IAT_CANDIDATE, "StretchBlt", (FARPROC)StretchBlt, (FARPROC *)&pGDIStretchBlt, (FARPROC)extDDStretchBlt}, + {HOOK_IAT_CANDIDATE, "GetClipBox", (FARPROC)NULL, (FARPROC *)&pGDIGetClipBox, (FARPROC)extGetClipBox}, - // {"PatBlt", (FARPROC)PatBlt, (FARPROC *)&pGDIPatBlt, (FARPROC)extDDPatBlt}, // missing one ... - // {"MaskBlt", (FARPROC)NULL, (FARPROC *)&pMaskBlt, (FARPROC)extDDMaskBlt}, // missing one ... - {0, NULL, 0, 0} // terminator + // {HOOK_IAT_CANDIDATE, "PatBlt", (FARPROC)PatBlt, (FARPROC *)&pGDIPatBlt, (FARPROC)extDDPatBlt}, // missing one ... + // {HOOK_IAT_CANDIDATE, "MaskBlt", (FARPROC)NULL, (FARPROC *)&pMaskBlt, (FARPROC)extDDMaskBlt}, // missing one ... + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type TextHooks[]={ - {"CreateFontA", (FARPROC)CreateFont, (FARPROC *)&pGDICreateFont, (FARPROC)extCreateFont}, - {"CreateFontIndirectA", (FARPROC)CreateFontIndirectA, (FARPROC *)&pGDICreateFontIndirect, (FARPROC)extCreateFontIndirect}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "CreateFontA", (FARPROC)CreateFont, (FARPROC *)&pGDICreateFont, (FARPROC)extCreateFont}, + {HOOK_IAT_CANDIDATE, "CreateFontIndirectA", (FARPROC)CreateFontIndirectA, (FARPROC *)&pGDICreateFontIndirect, (FARPROC)extCreateFontIndirect}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type GammaHooks[]={ - {"SetDeviceGammaRamp", (FARPROC)SetDeviceGammaRamp, (FARPROC *)&pGDISetDeviceGammaRamp, (FARPROC)extSetDeviceGammaRamp}, - {"GetDeviceGammaRamp", (FARPROC)GetDeviceGammaRamp, (FARPROC *)&pGDIGetDeviceGammaRamp, (FARPROC)extGetDeviceGammaRamp}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "SetDeviceGammaRamp", (FARPROC)SetDeviceGammaRamp, (FARPROC *)&pGDISetDeviceGammaRamp, (FARPROC)extSetDeviceGammaRamp}, + {HOOK_IAT_CANDIDATE, "GetDeviceGammaRamp", (FARPROC)GetDeviceGammaRamp, (FARPROC *)&pGDIGetDeviceGammaRamp, (FARPROC)extGetDeviceGammaRamp}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type FontHooks[]={ - {"CreateScalableFontResourceA", (FARPROC)NULL, (FARPROC *)&pCreateScalableFontResourceA, (FARPROC)extCreateScalableFontResourceA}, - {"CreateScalableFontResourceW", (FARPROC)NULL, (FARPROC *)&pCreateScalableFontResourceW, (FARPROC)extCreateScalableFontResourceW}, - {"AddFontResourceA", (FARPROC)NULL, (FARPROC *)&pAddFontResourceA, (FARPROC)extAddFontResourceA}, - {"AddFontResourceW", (FARPROC)NULL, (FARPROC *)&pAddFontResourceW, (FARPROC)extAddFontResourceW}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "CreateScalableFontResourceA", (FARPROC)NULL, (FARPROC *)&pCreateScalableFontResourceA, (FARPROC)extCreateScalableFontResourceA}, + {HOOK_IAT_CANDIDATE, "CreateScalableFontResourceW", (FARPROC)NULL, (FARPROC *)&pCreateScalableFontResourceW, (FARPROC)extCreateScalableFontResourceW}, + {HOOK_IAT_CANDIDATE, "AddFontResourceA", (FARPROC)NULL, (FARPROC *)&pAddFontResourceA, (FARPROC)extAddFontResourceA}, + {HOOK_IAT_CANDIDATE, "AddFontResourceW", (FARPROC)NULL, (FARPROC *)&pAddFontResourceW, (FARPROC)extAddFontResourceW}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; extern HRESULT WINAPI extDirectDrawCreate(GUID FAR *, LPDIRECTDRAW FAR *, IUnknown FAR *); diff --git a/dll/glide.cpp b/dll/glide.cpp index 91e69b5..8d168d7 100644 --- a/dll/glide.cpp +++ b/dll/glide.cpp @@ -22,12 +22,12 @@ grSstWinOpen_Type pgrSstWinOpen = 0; grQueryResolutions_Type pgrQueryResolutions = 0; static HookEntry_Type Hooks[]={ - {"_grGlideInit@0", (FARPROC)NULL, (FARPROC *)&pgrGlideInit, (FARPROC)extgrGlideInit}, - {"_grGlideShutdown@0", (FARPROC)NULL, (FARPROC *)&pgrGlideShutdown, (FARPROC)extgrGlideShutdown}, - {"_grClipWindow@16", (FARPROC)NULL, (FARPROC *)&pgrClipWindow, (FARPROC)extgrClipWindow}, - {"_grSstWinOpen@28", (FARPROC)NULL, (FARPROC *)&pgrSstWinOpen, (FARPROC)extgrSstWinOpen}, - {"_grQueryResolutions@8", (FARPROC)NULL, (FARPROC *)&pgrQueryResolutions, (FARPROC)extgrQueryResolutions}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "_grGlideInit@0", (FARPROC)NULL, (FARPROC *)&pgrGlideInit, (FARPROC)extgrGlideInit}, + {HOOK_IAT_CANDIDATE, "_grGlideShutdown@0", (FARPROC)NULL, (FARPROC *)&pgrGlideShutdown, (FARPROC)extgrGlideShutdown}, + {HOOK_IAT_CANDIDATE, "_grClipWindow@16", (FARPROC)NULL, (FARPROC *)&pgrClipWindow, (FARPROC)extgrClipWindow}, + {HOOK_IAT_CANDIDATE, "_grSstWinOpen@28", (FARPROC)NULL, (FARPROC *)&pgrSstWinOpen, (FARPROC)extgrSstWinOpen}, + {HOOK_IAT_CANDIDATE, "_grQueryResolutions@8", (FARPROC)NULL, (FARPROC *)&pgrQueryResolutions, (FARPROC)extgrQueryResolutions}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; FARPROC Remap_Glide_ProcAddress(LPCSTR proc, HMODULE hModule) diff --git a/dll/hotpatch.cpp b/dll/hotpatch.cpp new file mode 100644 index 0000000..914c7a7 --- /dev/null +++ b/dll/hotpatch.cpp @@ -0,0 +1,54 @@ +// hotpatch compiled system dlls come with Windows XP SP2 or later + +// return: +// 0 = patch failed +// 1 = already patched +// addr = address of the original function + +#include +#include "dxwnd.h" +#include "dxwcore.hpp" + +void *HotPatch(void *apiproc, const char *apiname, void *hookproc) +{ + DWORD dwPrevProtect; + BYTE* patch_address; + void *orig_address; + + OutTraceH("HotPatch: api=%s addr=%x hook=%x\n", apiname, apiproc, hookproc); + + if(!strcmp(apiname, "GetProcAddress")) return 0; // do not mess with this one! + + patch_address = ((BYTE *)apiproc) - 5; + orig_address = (BYTE *)apiproc + 2; + + // entry point could be at the top of a page? so VirtualProtect first to make sure patch_address is readable + //if(!VirtualProtect(patch_address, 7, PAGE_EXECUTE_READWRITE, &dwPrevProtect)){ + if(!VirtualProtect(patch_address, 7, PAGE_EXECUTE_WRITECOPY, &dwPrevProtect)){ + OutTraceH("HotPatch: access denied. err=%x\n", GetLastError()); + return (void *)0; // access denied + } + + // make sure it is a hotpatchable image... check for 5 nops followed by mov edi,edi + if(memcmp( "\x90\x90\x90\x90\x90\x8B\xFF", patch_address, 7) && memcmp( "\x90\x90\x90\x90\x90\x89\xFF", patch_address, 7)){ + VirtualProtect( patch_address, 7, dwPrevProtect, &dwPrevProtect ); // restore protection + // check it wasn't patched already + if((*patch_address==0xE9) && (*(WORD *)apiproc == 0xF9EB)){ + // should never go through here ... + OutTraceH("HotPatch: patched already\n"); + return (void *)1; + } + else{ + OutTraceH("HotPatch: not patch aware.\n"); + return (void *)0; // not hot patch "aware" + } + } + + *patch_address = 0xE9; // jmp (4-byte relative) + *((DWORD *)(patch_address + 1)) = (DWORD)hookproc - (DWORD)patch_address - 5; // relative address + *((WORD *)apiproc) = 0xF9EB; // should be atomic write (jmp $-5) + + VirtualProtect( patch_address, 7, dwPrevProtect, &dwPrevProtect ); // restore protection + OutTrace("HotPatch: api=%s addr=%x->%x hook=%x\n", apiname, apiproc, orig_address, hookproc); + return orig_address; +} \ No newline at end of file diff --git a/dll/iatpatch.cpp b/dll/iatpatch.cpp new file mode 100644 index 0000000..f922ce7 --- /dev/null +++ b/dll/iatpatch.cpp @@ -0,0 +1,91 @@ +#include +#include "dxwnd.h" +#include "dxwcore.hpp" + +void *IATPatch(HMODULE module, char *dll, void *apiproc, const char *apiname, void *hookproc) +{ + 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; + DWORD oldprotect; + void *org; + + base = (DWORD)module; + org = 0; // by default, ret = 0 => API not found + + __try{ + pnth = PIMAGE_NT_HEADERS(PBYTE(base) + PIMAGE_DOS_HEADER(base)->e_lfanew); + if(!pnth) { + OutTraceH("IATPatch: ERROR no PNTH at %d\n", __LINE__); + return 0; + } + rva = pnth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + if(!rva) { + OutTraceH("IATPatch: ERROR no RVA at %d\n", __LINE__); + return 0; + } + pidesc = (PIMAGE_IMPORT_DESCRIPTOR)(base + rva); + + while(pidesc->FirstThunk){ + impmodule = (PSTR)(base + pidesc->Name); + if(!lstrcmpi(dll, impmodule)) { + //OutTraceH("IATPatch: dll=%s found at %x\n", dll, impmodule); + + ptaddr = (PIMAGE_THUNK_DATA)(base + (DWORD)pidesc->FirstThunk); + ptname = (pidesc->OriginalFirstThunk) ? (PIMAGE_THUNK_DATA)(base + (DWORD)pidesc->OriginalFirstThunk) : NULL; + + while(ptaddr->u1.Function){ + if (ptname){ + // examining by function name + if(!IMAGE_SNAP_BY_ORDINAL(ptname->u1.Ordinal)){ + piname = (PIMAGE_IMPORT_BY_NAME)(base + (DWORD)ptname->u1.AddressOfData); + if(!lstrcmpi(apiname, (char *)piname->Name)) break; + } + } + if (apiproc){ + // examining by function addr + if(ptaddr->u1.Function == (DWORD)apiproc) break; + } + ptaddr ++; + if (ptname) ptname ++; + } + + if(ptaddr->u1.Function) { + org = (void *)ptaddr->u1.Function; + if(org == hookproc) return 0; // already hooked + + if(!VirtualProtect(&ptaddr->u1.Function, 4, PAGE_EXECUTE_READWRITE, &oldprotect)) { + OutTraceDW("IATPatch: VirtualProtect error %d at %d\n", GetLastError(), __LINE__); + return 0; + } + ptaddr->u1.Function = (DWORD)hookproc; + if(!VirtualProtect(&ptaddr->u1.Function, 4, oldprotect, &oldprotect)) { + OutTraceDW("IATPatch: VirtualProtect error %d at %d\n", GetLastError(), __LINE__); + return 0; + } + if (!FlushInstructionCache(GetCurrentProcess(), &ptaddr->u1.Function, 4)) { + OutTraceDW("IATPatch: FlushInstructionCache error %d at %d\n", GetLastError(), __LINE__); + return 0; + } + OutTraceH("IATPatch hook=%s address=%x->%x\n", apiname, org, hookproc); + + return org; + } + } + pidesc ++; + } + if(!pidesc->FirstThunk) { + OutTraceH("IATPatch: PE unreferenced function %s:%s\n", dll, apiname); + return 0; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + OutTraceDW("IATPatch: EXCEPTION hook=%s:%s Hook Failed.\n", dll, apiname); + } + return org; +} diff --git a/dll/imelib.cpp b/dll/imelib.cpp index ee89cdc..9de5931 100644 --- a/dll/imelib.cpp +++ b/dll/imelib.cpp @@ -10,15 +10,15 @@ #include "dxhelper.h" static HookEntry_Type Hooks[]={ - {"ImmNotifyIME", NULL, (FARPROC *)&pImmNotifyIME, (FARPROC)extImmNotifyIME}, - {"ImmCreateContext", NULL, (FARPROC *)&pImmCreateContext, (FARPROC)extImmCreateContext}, - {"ImmDestroyContext", NULL, (FARPROC *)&pImmDestroyContext, (FARPROC)extImmDestroyContext}, - {"ImmSetOpenStatus", NULL, (FARPROC *)&pImmSetOpenStatus, (FARPROC)extImmSetOpenStatus}, - {"ImmAssociateContext", NULL, (FARPROC *)&pImmAssociateContext, (FARPROC)extImmAssociateContext}, - {"ImmSetCompositionWindow", NULL, (FARPROC *)&pImmSetCompositionWindow, (FARPROC)extImmSetCompositionWindow}, - {"ImmSetCompositionStringA", NULL, (FARPROC *)&pImmSetCompositionString, (FARPROC)extImmSetCompositionString}, - {"ImmGetOpenStatus", NULL, (FARPROC *)&pImmGetOpenStatus, (FARPROC)extImmGetOpenStatus}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "ImmNotifyIME", NULL, (FARPROC *)&pImmNotifyIME, (FARPROC)extImmNotifyIME}, + {HOOK_IAT_CANDIDATE, "ImmCreateContext", NULL, (FARPROC *)&pImmCreateContext, (FARPROC)extImmCreateContext}, + {HOOK_IAT_CANDIDATE, "ImmDestroyContext", NULL, (FARPROC *)&pImmDestroyContext, (FARPROC)extImmDestroyContext}, + {HOOK_IAT_CANDIDATE, "ImmSetOpenStatus", NULL, (FARPROC *)&pImmSetOpenStatus, (FARPROC)extImmSetOpenStatus}, + {HOOK_IAT_CANDIDATE, "ImmAssociateContext", NULL, (FARPROC *)&pImmAssociateContext, (FARPROC)extImmAssociateContext}, + {HOOK_IAT_CANDIDATE, "ImmSetCompositionWindow", NULL, (FARPROC *)&pImmSetCompositionWindow, (FARPROC)extImmSetCompositionWindow}, + {HOOK_IAT_CANDIDATE, "ImmSetCompositionStringA", NULL, (FARPROC *)&pImmSetCompositionString, (FARPROC)extImmSetCompositionString}, + {HOOK_IAT_CANDIDATE, "ImmGetOpenStatus", NULL, (FARPROC *)&pImmGetOpenStatus, (FARPROC)extImmGetOpenStatus}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; void HookImeLib(HMODULE module) diff --git a/dll/kernel32.cpp b/dll/kernel32.cpp index 358b53b..9a2c292 100644 --- a/dll/kernel32.cpp +++ b/dll/kernel32.cpp @@ -10,52 +10,52 @@ //#define IsTraceDW TRUE static HookEntry_Type Hooks[]={ - {"IsDebuggerPresent", (FARPROC)NULL, (FARPROC *)NULL, (FARPROC)extIsDebuggerPresent}, - {"GetProcAddress", (FARPROC)GetProcAddress, (FARPROC *)&pGetProcAddress, (FARPROC)extGetProcAddress}, - {"LoadLibraryA", (FARPROC)LoadLibraryA, (FARPROC *)&pLoadLibraryA, (FARPROC)extLoadLibraryA}, - {"LoadLibraryExA", (FARPROC)LoadLibraryExA, (FARPROC *)&pLoadLibraryExA, (FARPROC)extLoadLibraryExA}, - {"LoadLibraryW", (FARPROC)LoadLibraryW, (FARPROC *)&pLoadLibraryW, (FARPROC)extLoadLibraryW}, - {"LoadLibraryExW", (FARPROC)LoadLibraryExW, (FARPROC *)&pLoadLibraryExW, (FARPROC)extLoadLibraryExW}, - {"GetDriveTypeA", (FARPROC)NULL, (FARPROC *)&pGetDriveType, (FARPROC)extGetDriveType}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "IsDebuggerPresent", (FARPROC)NULL, (FARPROC *)NULL, (FARPROC)extIsDebuggerPresent}, + {HOOK_IAT_CANDIDATE, "GetProcAddress", (FARPROC)GetProcAddress, (FARPROC *)&pGetProcAddress, (FARPROC)extGetProcAddress}, + {HOOK_IAT_CANDIDATE, "LoadLibraryA", (FARPROC)LoadLibraryA, (FARPROC *)&pLoadLibraryA, (FARPROC)extLoadLibraryA}, + {HOOK_IAT_CANDIDATE, "LoadLibraryExA", (FARPROC)LoadLibraryExA, (FARPROC *)&pLoadLibraryExA, (FARPROC)extLoadLibraryExA}, + {HOOK_IAT_CANDIDATE, "LoadLibraryW", (FARPROC)LoadLibraryW, (FARPROC *)&pLoadLibraryW, (FARPROC)extLoadLibraryW}, + {HOOK_IAT_CANDIDATE, "LoadLibraryExW", (FARPROC)LoadLibraryExW, (FARPROC *)&pLoadLibraryExW, (FARPROC)extLoadLibraryExW}, + {HOOK_IAT_CANDIDATE, "GetDriveTypeA", (FARPROC)NULL, (FARPROC *)&pGetDriveType, (FARPROC)extGetDriveType}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type FixIOHooks[]={ - {"ReadFile", (FARPROC)NULL, (FARPROC *)&pReadFile, (FARPROC)extReadFile}, - {"CreateFileA", (FARPROC)NULL, (FARPROC *)&pCreateFile, (FARPROC)extCreateFile}, - {"SetFilePointer", (FARPROC)NULL, (FARPROC *)&pSetFilePointer, (FARPROC)extSetFilePointer}, - {"CloseHandle", (FARPROC)NULL, (FARPROC *)&pCloseHandle, (FARPROC)extCloseHandle}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "ReadFile", (FARPROC)NULL, (FARPROC *)&pReadFile, (FARPROC)extReadFile}, + {HOOK_IAT_CANDIDATE, "CreateFileA", (FARPROC)NULL, (FARPROC *)&pCreateFile, (FARPROC)extCreateFile}, + {HOOK_IAT_CANDIDATE, "SetFilePointer", (FARPROC)NULL, (FARPROC *)&pSetFilePointer, (FARPROC)extSetFilePointer}, + {HOOK_IAT_CANDIDATE, "CloseHandle", (FARPROC)NULL, (FARPROC *)&pCloseHandle, (FARPROC)extCloseHandle}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type LimitHooks[]={ - {"GetDiskFreeSpaceA", (FARPROC)GetDiskFreeSpaceA, (FARPROC *)&pGetDiskFreeSpaceA, (FARPROC)extGetDiskFreeSpaceA}, - {"GlobalMemoryStatus", (FARPROC)GlobalMemoryStatus, (FARPROC *)&pGlobalMemoryStatus, (FARPROC)extGlobalMemoryStatus}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "GetDiskFreeSpaceA", (FARPROC)GetDiskFreeSpaceA, (FARPROC *)&pGetDiskFreeSpaceA, (FARPROC)extGetDiskFreeSpaceA}, + {HOOK_IAT_CANDIDATE, "GlobalMemoryStatus", (FARPROC)GlobalMemoryStatus, (FARPROC *)&pGlobalMemoryStatus, (FARPROC)extGlobalMemoryStatus}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type TimeHooks[]={ - {"GetTickCount", (FARPROC)GetTickCount, (FARPROC *)&pGetTickCount, (FARPROC)extGetTickCount}, - {"GetLocalTime", (FARPROC)GetLocalTime, (FARPROC *)&pGetLocalTime, (FARPROC)extGetLocalTime}, - {"GetSystemTime", (FARPROC)GetSystemTime, (FARPROC *)&pGetSystemTime, (FARPROC)extGetSystemTime}, - {"GetSystemTimeAsFileTime", (FARPROC)GetSystemTimeAsFileTime, (FARPROC *)&pGetSystemTimeAsFileTime, (FARPROC)extGetSystemTimeAsFileTime}, - {"Sleep", (FARPROC)Sleep, (FARPROC *)&pSleep, (FARPROC)extSleep}, - {"SleepEx", (FARPROC)SleepEx, (FARPROC *)&pSleepEx, (FARPROC)extSleepEx}, - {"QueryPerformanceCounter", (FARPROC)QueryPerformanceCounter, (FARPROC *)&pQueryPerformanceCounter, (FARPROC)extQueryPerformanceCounter}, - {"QueryPerformanceFrequency", (FARPROC)QueryPerformanceFrequency, (FARPROC *)&pQueryPerformanceFrequency, (FARPROC)extQueryPerformanceFrequency}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "GetTickCount", (FARPROC)GetTickCount, (FARPROC *)&pGetTickCount, (FARPROC)extGetTickCount}, + {HOOK_IAT_CANDIDATE, "GetLocalTime", (FARPROC)GetLocalTime, (FARPROC *)&pGetLocalTime, (FARPROC)extGetLocalTime}, + {HOOK_IAT_CANDIDATE, "GetSystemTime", (FARPROC)GetSystemTime, (FARPROC *)&pGetSystemTime, (FARPROC)extGetSystemTime}, + {HOOK_IAT_CANDIDATE, "GetSystemTimeAsFileTime", (FARPROC)GetSystemTimeAsFileTime, (FARPROC *)&pGetSystemTimeAsFileTime, (FARPROC)extGetSystemTimeAsFileTime}, + {HOOK_IAT_CANDIDATE, "Sleep", (FARPROC)Sleep, (FARPROC *)&pSleep, (FARPROC)extSleep}, + {HOOK_IAT_CANDIDATE, "SleepEx", (FARPROC)SleepEx, (FARPROC *)&pSleepEx, (FARPROC)extSleepEx}, + {HOOK_IAT_CANDIDATE, "QueryPerformanceCounter", (FARPROC)QueryPerformanceCounter, (FARPROC *)&pQueryPerformanceCounter, (FARPROC)extQueryPerformanceCounter}, + {HOOK_IAT_CANDIDATE, "QueryPerformanceFrequency", (FARPROC)QueryPerformanceFrequency, (FARPROC *)&pQueryPerformanceFrequency, (FARPROC)extQueryPerformanceFrequency}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type VersionHooks[]={ - {"GetVersion", (FARPROC)GetVersion, (FARPROC *)&pGetVersion, (FARPROC)extGetVersion}, - {"GetVersionExA", (FARPROC)GetVersionExA, (FARPROC *)&pGetVersionExA, (FARPROC)extGetVersionExA}, - {"GetVersionExW", (FARPROC)GetVersionExW, (FARPROC *)&pGetVersionExW, (FARPROC)extGetVersionExW}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "GetVersion", (FARPROC)GetVersion, (FARPROC *)&pGetVersion, (FARPROC)extGetVersion}, + {HOOK_IAT_CANDIDATE, "GetVersionExA", (FARPROC)GetVersionExA, (FARPROC *)&pGetVersionExA, (FARPROC)extGetVersionExA}, + {HOOK_IAT_CANDIDATE, "GetVersionExW", (FARPROC)GetVersionExW, (FARPROC *)&pGetVersionExW, (FARPROC)extGetVersionExW}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type SuppressChildHooks[]={ - {"CreateProcessA", (FARPROC)NULL, (FARPROC *)NULL, (FARPROC)extCreateProcessA}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "CreateProcessA", (FARPROC)NULL, (FARPROC *)NULL, (FARPROC)extCreateProcessA}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static char *libname = "kernel32.dll"; @@ -574,7 +574,7 @@ FARPROC WINAPI extGetProcAddress(HMODULE hModule, LPCSTR proc) break; case SYSLIBIDX_USER32: if ((DWORD)proc == 0x0020){ // ChangeDisplaySettingsA - pChangeDisplaySettingsA=(ChangeDisplaySettingsA_Type)(*pGetProcAddress)(hModule, proc); + /* if (!pChangeDisplaySettingsA) */ pChangeDisplaySettingsA=(ChangeDisplaySettingsA_Type)(*pGetProcAddress)(hModule, proc); OutTraceDW("GetProcAddress: hooking proc=%s at addr=%x\n", ProcToString(proc), pChangeDisplaySettingsA); return (FARPROC)extChangeDisplaySettingsA; } diff --git a/dll/msvfw.cpp b/dll/msvfw.cpp index bc33d64..b439cc9 100644 --- a/dll/msvfw.cpp +++ b/dll/msvfw.cpp @@ -11,7 +11,7 @@ static HookEntry_Type Hooks[]={ //{"ICSendMessage", (FARPROC)NULL, (FARPROC *)&pICSendMessage, (FARPROC)extICSendMessage}, //{"ICOpen", (FARPROC)NULL, (FARPROC *)&pICOpen, (FARPROC)extICOpen}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; FARPROC Remap_vfw_ProcAddress(LPCSTR proc, HMODULE hModule) diff --git a/dll/ole32.cpp b/dll/ole32.cpp index d37fb1e..624795a 100644 --- a/dll/ole32.cpp +++ b/dll/ole32.cpp @@ -8,10 +8,10 @@ extern void HookModule(HMODULE, int); static HookEntry_Type Hooks[]={ - {"CoCreateInstance", NULL, (FARPROC *)&pCoCreateInstance, (FARPROC)extCoCreateInstance}, - {"CoCreateInstanceEx", NULL, (FARPROC *)&pCoCreateInstanceEx, (FARPROC)extCoCreateInstanceEx}, - {"CoInitialize", NULL, (FARPROC *)&pCoInitialize, (FARPROC)extCoInitialize}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "CoCreateInstance", NULL, (FARPROC *)&pCoCreateInstance, (FARPROC)extCoCreateInstance}, + {HOOK_IAT_CANDIDATE, "CoCreateInstanceEx", NULL, (FARPROC *)&pCoCreateInstanceEx, (FARPROC)extCoCreateInstanceEx}, + {HOOK_IAT_CANDIDATE, "CoInitialize", NULL, (FARPROC *)&pCoInitialize, (FARPROC)extCoInitialize}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; extern HRESULT WINAPI extDirectDrawCreate(GUID FAR *, LPDIRECTDRAW FAR *, IUnknown FAR *); diff --git a/dll/opengl.cpp b/dll/opengl.cpp index 9688381..31898a3 100644 --- a/dll/opengl.cpp +++ b/dll/opengl.cpp @@ -8,28 +8,28 @@ #undef DXWDECLARATIONS static HookEntry_Type Hooks[]={ - {"glViewport", NULL, (FARPROC *)&pglViewport, (FARPROC)extglViewport}, - {"glScissor", NULL, (FARPROC *)&pglScissor, (FARPROC)extglScissor}, - {"glGetIntegerv", NULL, (FARPROC *)&pglGetIntegerv, (FARPROC)&extglGetIntegerv}, - {"glDrawBuffer", NULL, (FARPROC *)&pglDrawBuffer, (FARPROC)extglDrawBuffer}, - {"glPolygonMode", NULL, (FARPROC *)&pglPolygonMode, (FARPROC)extglPolygonMode}, - {"glGetFloatv", NULL, (FARPROC *)&pglGetFloatv, (FARPROC)extglGetFloatv}, - {"glClear", NULL, (FARPROC *)&pglClear, (FARPROC)extglClear}, - {"wglCreateContext", NULL, (FARPROC *)&pwglCreateContext, (FARPROC)extwglCreateContext}, - {"wglMakeCurrent", NULL, (FARPROC *)&pwglMakeCurrent, (FARPROC)extwglMakeCurrent}, - {"wglGetProcAddress", NULL, (FARPROC *)&pwglGetProcAddress, (FARPROC)extwglGetProcAddress}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "glViewport", NULL, (FARPROC *)&pglViewport, (FARPROC)extglViewport}, + {HOOK_IAT_CANDIDATE, "glScissor", NULL, (FARPROC *)&pglScissor, (FARPROC)extglScissor}, + {HOOK_IAT_CANDIDATE, "glGetIntegerv", NULL, (FARPROC *)&pglGetIntegerv, (FARPROC)&extglGetIntegerv}, + {HOOK_IAT_CANDIDATE, "glDrawBuffer", NULL, (FARPROC *)&pglDrawBuffer, (FARPROC)extglDrawBuffer}, + {HOOK_IAT_CANDIDATE, "glPolygonMode", NULL, (FARPROC *)&pglPolygonMode, (FARPROC)extglPolygonMode}, + {HOOK_IAT_CANDIDATE, "glGetFloatv", NULL, (FARPROC *)&pglGetFloatv, (FARPROC)extglGetFloatv}, + {HOOK_IAT_CANDIDATE, "glClear", NULL, (FARPROC *)&pglClear, (FARPROC)extglClear}, + {HOOK_IAT_CANDIDATE, "wglCreateContext", NULL, (FARPROC *)&pwglCreateContext, (FARPROC)extwglCreateContext}, + {HOOK_IAT_CANDIDATE, "wglMakeCurrent", NULL, (FARPROC *)&pwglMakeCurrent, (FARPROC)extwglMakeCurrent}, + {HOOK_IAT_CANDIDATE, "wglGetProcAddress", NULL, (FARPROC *)&pwglGetProcAddress, (FARPROC)extwglGetProcAddress}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type wglHooks[]={ - {"glViewport", NULL, (FARPROC *)&pglViewport, (FARPROC)extglViewport}, - {"glScissor", NULL, (FARPROC *)&pglScissor, (FARPROC)extglScissor}, - {"glGetIntegerv", NULL, (FARPROC *)&pglGetIntegerv, (FARPROC)&extglGetIntegerv}, - {"glDrawBuffer", NULL, (FARPROC *)&pglDrawBuffer, (FARPROC)extglDrawBuffer}, - {"glPolygonMode", NULL, (FARPROC *)&pglPolygonMode, (FARPROC)extglPolygonMode}, - {"glGetFloatv", NULL, (FARPROC *)&pglGetFloatv, (FARPROC)extglGetFloatv}, - {"glClear", NULL, (FARPROC *)&pglClear, (FARPROC)extglClear}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "glViewport", NULL, (FARPROC *)&pglViewport, (FARPROC)extglViewport}, + {HOOK_IAT_CANDIDATE, "glScissor", NULL, (FARPROC *)&pglScissor, (FARPROC)extglScissor}, + {HOOK_IAT_CANDIDATE, "glGetIntegerv", NULL, (FARPROC *)&pglGetIntegerv, (FARPROC)&extglGetIntegerv}, + {HOOK_IAT_CANDIDATE, "glDrawBuffer", NULL, (FARPROC *)&pglDrawBuffer, (FARPROC)extglDrawBuffer}, + {HOOK_IAT_CANDIDATE, "glPolygonMode", NULL, (FARPROC *)&pglPolygonMode, (FARPROC)extglPolygonMode}, + {HOOK_IAT_CANDIDATE, "glGetFloatv", NULL, (FARPROC *)&pglGetFloatv, (FARPROC)extglGetFloatv}, + {HOOK_IAT_CANDIDATE, "glClear", NULL, (FARPROC *)&pglClear, (FARPROC)extglClear}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; FARPROC Remap_gl_ProcAddress(LPCSTR proc, HMODULE hModule) diff --git a/dll/syslibs.h b/dll/syslibs.h index a524f5d..6850b30 100644 --- a/dll/syslibs.h +++ b/dll/syslibs.h @@ -194,6 +194,10 @@ typedef BOOL (WINAPI *IsWindowVisible_Type)(HWND); typedef BOOL (WINAPI *SystemParametersInfo_Type)(UINT, UINT, PVOID, UINT); typedef BOOL (WINAPI *KillTimer_Type)(HWND, UINT_PTR); typedef BOOL (WINAPI *GetUpdateRect_Type)(HWND, LPRECT, BOOL); +typedef BOOL (WINAPI *GetCursorInfo_Type)(PCURSORINFO); +typedef HWND (WINAPI *WindowFromPoint_Type)(POINT); +typedef HWND (WINAPI *ChildWindowFromPoint_Type)(HWND, POINT); +typedef HWND (WINAPI *ChildWindowFromPointEx_Type)(HWND, POINT, UINT); // Winmm.dll: typedef MCIERROR(WINAPI *mciSendCommand_Type)(MCIDEVICEID, UINT, DWORD_PTR, DWORD_PTR); @@ -398,6 +402,10 @@ DXWEXTERN SystemParametersInfo_Type pSystemParametersInfoA DXWINITIALIZED; DXWEXTERN SystemParametersInfo_Type pSystemParametersInfoW DXWINITIALIZED; DXWEXTERN KillTimer_Type pKillTimer DXWINITIALIZED; DXWEXTERN GetUpdateRect_Type pGetUpdateRect DXWINITIALIZED; +DXWEXTERN GetCursorInfo_Type pGetCursorInfo DXWINITIALIZED; +DXWEXTERN WindowFromPoint_Type pWindowFromPoint DXWINITIALIZED; +DXWEXTERN ChildWindowFromPoint_Type pChildWindowFromPoint DXWINITIALIZED; +DXWEXTERN ChildWindowFromPointEx_Type pChildWindowFromPointEx DXWINITIALIZED; // Winmm.dll: DXWEXTERN mciSendCommand_Type pmciSendCommand DXWINITIALIZED; @@ -609,6 +617,11 @@ extern BOOL WINAPI extSystemParametersInfoA(UINT, UINT, PVOID, UINT); extern BOOL WINAPI extSystemParametersInfoW(UINT, UINT, PVOID, UINT); extern BOOL WINAPI extKillTimer(HWND, UINT_PTR); extern BOOL WINAPI extGetUpdateRect(HWND, LPRECT, BOOL); +extern BOOL WINAPI extGetCursorInfo(PCURSORINFO); +extern HWND WINAPI extWindowFromPoint(POINT); +extern HWND WINAPI extChildWindowFromPoint(HWND, POINT); +extern HWND WINAPI extChildWindowFromPointEx(HWND, POINT, UINT); + // Winmm.dll: extern MCIERROR WINAPI extmciSendCommand(MCIDEVICEID, UINT, DWORD_PTR, DWORD_PTR); diff --git a/dll/user32.cpp b/dll/user32.cpp index 848dc56..7f984fc 100644 --- a/dll/user32.cpp +++ b/dll/user32.cpp @@ -1,6 +1,7 @@ #define _WIN32_WINNT 0x0600 #define WIN32_LEAN_AND_MEAN #define _CRT_SECURE_NO_WARNINGS +#define _CRT_NON_CONFORMING_SWPRINTFS #include #include @@ -11,132 +12,135 @@ #include "hddraw.h" #include "dxhelper.h" -static HookEntry_Type Hooks[]={ - {"UpdateWindow", (FARPROC)NULL, (FARPROC *)&pUpdateWindow, (FARPROC)extUpdateWindow}, - //{"GetWindowPlacement", (FARPROC)NULL, (FARPROC *)&pGetWindowPlacement, (FARPROC)extGetWindowPlacement}, - //{"SetWindowPlacement", (FARPROC)NULL, (FARPROC *)&pSetWindowPlacement, (FARPROC)extSetWindowPlacement}, - {"ChangeDisplaySettingsA", (FARPROC)ChangeDisplaySettingsA, (FARPROC *)&pChangeDisplaySettingsA, (FARPROC)extChangeDisplaySettingsA}, - {"ChangeDisplaySettingsExA", (FARPROC)ChangeDisplaySettingsExA, (FARPROC *)&pChangeDisplaySettingsExA, (FARPROC)extChangeDisplaySettingsExA}, - {"ChangeDisplaySettingsW", (FARPROC)NULL, (FARPROC *)&pChangeDisplaySettingsW, (FARPROC)extChangeDisplaySettingsW}, // ref. by Knights of Honor - {"ChangeDisplaySettingsExW", (FARPROC)NULL, (FARPROC *)&pChangeDisplaySettingsExW, (FARPROC)extChangeDisplaySettingsExW}, - {"ShowCursor", (FARPROC)ShowCursor, (FARPROC *)&pShowCursor, (FARPROC)extShowCursor}, - {"CreateDialogIndirectParamA", (FARPROC)CreateDialogIndirectParamA, (FARPROC *)&pCreateDialogIndirectParam, (FARPROC)extCreateDialogIndirectParam}, - {"CreateDialogParamA", (FARPROC)CreateDialogParamA, (FARPROC *)&pCreateDialogParam, (FARPROC)extCreateDialogParam}, - {"MoveWindow", (FARPROC)MoveWindow, (FARPROC *)&pMoveWindow, (FARPROC)extMoveWindow}, - {"EnumDisplaySettingsA", (FARPROC)EnumDisplaySettingsA, (FARPROC *)&pEnumDisplaySettings, (FARPROC)extEnumDisplaySettings}, - {"GetClipCursor", (FARPROC)GetClipCursor, (FARPROC*)&pGetClipCursor, (FARPROC)extGetClipCursor}, - {"ClipCursor", (FARPROC)ClipCursor, (FARPROC *)&pClipCursor, (FARPROC)extClipCursor}, - {"DefWindowProcA", (FARPROC)DefWindowProcA, (FARPROC *)&pDefWindowProcA, (FARPROC)extDefWindowProcA}, - {"DefWindowProcW", (FARPROC)DefWindowProcW, (FARPROC *)&pDefWindowProcW, (FARPROC)extDefWindowProcW}, - {"CreateWindowExA", (FARPROC)CreateWindowExA, (FARPROC *)&pCreateWindowExA, (FARPROC)extCreateWindowExA}, - {"CreateWindowExW", (FARPROC)CreateWindowExW, (FARPROC *)&pCreateWindowExW, (FARPROC)extCreateWindowExW}, - {"RegisterClassExA", (FARPROC)RegisterClassExA, (FARPROC *)&pRegisterClassExA, (FARPROC)extRegisterClassExA}, - {"RegisterClassA", (FARPROC)RegisterClassA, (FARPROC *)&pRegisterClassA, (FARPROC)extRegisterClassA}, - {"GetSystemMetrics", (FARPROC)GetSystemMetrics, (FARPROC *)&pGetSystemMetrics, (FARPROC)extGetSystemMetrics}, - {"GetDesktopWindow", (FARPROC)GetDesktopWindow, (FARPROC *)&pGetDesktopWindow, (FARPROC)extGetDesktopWindow}, - {"CloseWindow", (FARPROC)NULL, (FARPROC *)&pCloseWindow, (FARPROC)extCloseWindow}, - {"DestroyWindow", (FARPROC)NULL, (FARPROC *)&pDestroyWindow, (FARPROC)extDestroyWindow}, - {"SetSysColors", (FARPROC)NULL, (FARPROC *)&pSetSysColors, (FARPROC)extSetSysColors}, - {"SetCapture", (FARPROC)NULL, (FARPROC *)&pSetCapture, (FARPROC)extSetCapture}, - {"SetWindowLongA", (FARPROC)SetWindowLongA, (FARPROC *)&pSetWindowLongA, (FARPROC)extSetWindowLongA}, - {"GetWindowLongA", (FARPROC)GetWindowLongA, (FARPROC *)&pGetWindowLongA, (FARPROC)extGetWindowLongA}, - {"SetWindowLongW", (FARPROC)SetWindowLongW, (FARPROC *)&pSetWindowLongW, (FARPROC)extSetWindowLongW}, - {"GetWindowLongW", (FARPROC)GetWindowLongW, (FARPROC *)&pGetWindowLongW, (FARPROC)extGetWindowLongW}, +BOOL IsChangeDisplaySettingsHotPatched = FALSE; - //{"GetActiveWindow", (FARPROC)NULL, (FARPROC *)&pGetActiveWindow, (FARPROC)extGetActiveWindow}, - //{"GetForegroundWindow", (FARPROC)NULL, (FARPROC *)&pGetForegroundWindow, (FARPROC)extGetForegroundWindow}, - {"IsWindowVisible", (FARPROC)NULL, (FARPROC *)&pIsWindowVisible, (FARPROC)extIsWindowVisible}, - {"SystemParametersInfoA", (FARPROC)SystemParametersInfoA, (FARPROC *)&pSystemParametersInfoA, (FARPROC)extSystemParametersInfoA}, - {"SystemParametersInfoW", (FARPROC)SystemParametersInfoW, (FARPROC *)&pSystemParametersInfoW, (FARPROC)extSystemParametersInfoW}, - {0, NULL, 0, 0} // terminator +static HookEntry_Type Hooks[]={ + {HOOK_IAT_CANDIDATE, "UpdateWindow", (FARPROC)NULL, (FARPROC *)&pUpdateWindow, (FARPROC)extUpdateWindow}, + //{HOOK_IAT_CANDIDATE, "GetWindowPlacement", (FARPROC)NULL, (FARPROC *)&pGetWindowPlacement, (FARPROC)extGetWindowPlacement}, + //{HOOK_IAT_CANDIDATE, "SetWindowPlacement", (FARPROC)NULL, (FARPROC *)&pSetWindowPlacement, (FARPROC)extSetWindowPlacement}, + {HOOK_HOT_CANDIDATE, "ChangeDisplaySettingsA", (FARPROC)ChangeDisplaySettingsA, (FARPROC *)&pChangeDisplaySettingsA, (FARPROC)extChangeDisplaySettingsA}, + {HOOK_HOT_CANDIDATE, "ChangeDisplaySettingsExA", (FARPROC)ChangeDisplaySettingsExA, (FARPROC *)&pChangeDisplaySettingsExA, (FARPROC)extChangeDisplaySettingsExA}, + {HOOK_HOT_CANDIDATE, "ChangeDisplaySettingsW", (FARPROC)NULL, (FARPROC *)&pChangeDisplaySettingsW, (FARPROC)extChangeDisplaySettingsW}, // ref. by Knights of Honor + {HOOK_HOT_CANDIDATE, "ChangeDisplaySettingsExW", (FARPROC)NULL, (FARPROC *)&pChangeDisplaySettingsExW, (FARPROC)extChangeDisplaySettingsExW}, + {HOOK_IAT_CANDIDATE, "ShowCursor", (FARPROC)ShowCursor, (FARPROC *)&pShowCursor, (FARPROC)extShowCursor}, + {HOOK_IAT_CANDIDATE, "CreateDialogIndirectParamA", (FARPROC)CreateDialogIndirectParamA, (FARPROC *)&pCreateDialogIndirectParam, (FARPROC)extCreateDialogIndirectParam}, + {HOOK_IAT_CANDIDATE, "CreateDialogParamA", (FARPROC)CreateDialogParamA, (FARPROC *)&pCreateDialogParam, (FARPROC)extCreateDialogParam}, + {HOOK_IAT_CANDIDATE, "MoveWindow", (FARPROC)MoveWindow, (FARPROC *)&pMoveWindow, (FARPROC)extMoveWindow}, + {HOOK_IAT_CANDIDATE, "EnumDisplaySettingsA", (FARPROC)EnumDisplaySettingsA, (FARPROC *)&pEnumDisplaySettings, (FARPROC)extEnumDisplaySettings}, + {HOOK_IAT_CANDIDATE, "GetClipCursor", (FARPROC)GetClipCursor, (FARPROC*)&pGetClipCursor, (FARPROC)extGetClipCursor}, + {HOOK_IAT_CANDIDATE, "ClipCursor", (FARPROC)ClipCursor, (FARPROC *)&pClipCursor, (FARPROC)extClipCursor}, + {HOOK_IAT_CANDIDATE, "DefWindowProcA", (FARPROC)DefWindowProcA, (FARPROC *)&pDefWindowProcA, (FARPROC)extDefWindowProcA}, + {HOOK_IAT_CANDIDATE, "DefWindowProcW", (FARPROC)DefWindowProcW, (FARPROC *)&pDefWindowProcW, (FARPROC)extDefWindowProcW}, + {HOOK_HOT_CANDIDATE, "CreateWindowExA", (FARPROC)CreateWindowExA, (FARPROC *)&pCreateWindowExA, (FARPROC)extCreateWindowExA}, + {HOOK_HOT_CANDIDATE, "CreateWindowExW", (FARPROC)CreateWindowExW, (FARPROC *)&pCreateWindowExW, (FARPROC)extCreateWindowExW}, + {HOOK_IAT_CANDIDATE, "RegisterClassExA", (FARPROC)RegisterClassExA, (FARPROC *)&pRegisterClassExA, (FARPROC)extRegisterClassExA}, + {HOOK_IAT_CANDIDATE, "RegisterClassA", (FARPROC)RegisterClassA, (FARPROC *)&pRegisterClassA, (FARPROC)extRegisterClassA}, + {HOOK_IAT_CANDIDATE, "GetSystemMetrics", (FARPROC)GetSystemMetrics, (FARPROC *)&pGetSystemMetrics, (FARPROC)extGetSystemMetrics}, + {HOOK_IAT_CANDIDATE, "GetDesktopWindow", (FARPROC)GetDesktopWindow, (FARPROC *)&pGetDesktopWindow, (FARPROC)extGetDesktopWindow}, + {HOOK_IAT_CANDIDATE, "CloseWindow", (FARPROC)NULL, (FARPROC *)&pCloseWindow, (FARPROC)extCloseWindow}, + {HOOK_IAT_CANDIDATE, "DestroyWindow", (FARPROC)NULL, (FARPROC *)&pDestroyWindow, (FARPROC)extDestroyWindow}, + {HOOK_IAT_CANDIDATE, "SetSysColors", (FARPROC)NULL, (FARPROC *)&pSetSysColors, (FARPROC)extSetSysColors}, + {HOOK_IAT_CANDIDATE, "SetCapture", (FARPROC)NULL, (FARPROC *)&pSetCapture, (FARPROC)extSetCapture}, + {HOOK_IAT_CANDIDATE, "SetWindowLongA", (FARPROC)SetWindowLongA, (FARPROC *)&pSetWindowLongA, (FARPROC)extSetWindowLongA}, + {HOOK_IAT_CANDIDATE, "GetWindowLongA", (FARPROC)GetWindowLongA, (FARPROC *)&pGetWindowLongA, (FARPROC)extGetWindowLongA}, + {HOOK_IAT_CANDIDATE, "SetWindowLongW", (FARPROC)SetWindowLongW, (FARPROC *)&pSetWindowLongW, (FARPROC)extSetWindowLongW}, + {HOOK_IAT_CANDIDATE, "GetWindowLongW", (FARPROC)GetWindowLongW, (FARPROC *)&pGetWindowLongW, (FARPROC)extGetWindowLongW}, + + //{HOOK_IAT_CANDIDATE, "GetActiveWindow", (FARPROC)NULL, (FARPROC *)&pGetActiveWindow, (FARPROC)extGetActiveWindow}, + //{HOOK_IAT_CANDIDATE, "GetForegroundWindow", (FARPROC)NULL, (FARPROC *)&pGetForegroundWindow, (FARPROC)extGetForegroundWindow}, + {HOOK_IAT_CANDIDATE, "IsWindowVisible", (FARPROC)NULL, (FARPROC *)&pIsWindowVisible, (FARPROC)extIsWindowVisible}, + {HOOK_IAT_CANDIDATE, "SystemParametersInfoA", (FARPROC)SystemParametersInfoA, (FARPROC *)&pSystemParametersInfoA, (FARPROC)extSystemParametersInfoA}, + {HOOK_IAT_CANDIDATE, "SystemParametersInfoW", (FARPROC)SystemParametersInfoW, (FARPROC *)&pSystemParametersInfoW, (FARPROC)extSystemParametersInfoW}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type NoGDIHooks[]={ - {"BeginPaint", (FARPROC)BeginPaint, (FARPROC *)&pBeginPaint, (FARPROC)extBeginPaint}, - {"EndPaint", (FARPROC)EndPaint, (FARPROC *)&pEndPaint, (FARPROC)extEndPaint}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "BeginPaint", (FARPROC)BeginPaint, (FARPROC *)&pBeginPaint, (FARPROC)extBeginPaint}, + {HOOK_IAT_CANDIDATE, "EndPaint", (FARPROC)EndPaint, (FARPROC *)&pEndPaint, (FARPROC)extEndPaint}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type EmulateHooks[]={ - {"BeginPaint", (FARPROC)BeginPaint, (FARPROC *)&pBeginPaint, (FARPROC)extEMUBeginPaint}, - {"EndPaint", (FARPROC)EndPaint, (FARPROC *)&pEndPaint, (FARPROC)extEMUEndPaint}, - {"GetDC", (FARPROC)GetDC, (FARPROC *)&pGDIGetDC, (FARPROC)extEMUGetDC}, - {"GetDCEx", (FARPROC)GetDCEx, (FARPROC *)&pGDIGetDCEx, (FARPROC)extEMUGetDCEx}, - {"GetWindowDC", (FARPROC)GetWindowDC, (FARPROC *)&pGDIGetWindowDC, (FARPROC)extEMUGetWindowDC}, - {"ReleaseDC", (FARPROC)ReleaseDC, (FARPROC *)&pGDIReleaseDC, (FARPROC)extEMUReleaseDC}, - //{"InvalidateRect", (FARPROC)InvalidateRect, (FARPROC *)&pInvalidateRect, (FARPROC)extInvalidateRect}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "BeginPaint", (FARPROC)BeginPaint, (FARPROC *)&pBeginPaint, (FARPROC)extEMUBeginPaint}, + {HOOK_IAT_CANDIDATE, "EndPaint", (FARPROC)EndPaint, (FARPROC *)&pEndPaint, (FARPROC)extEMUEndPaint}, + {HOOK_IAT_CANDIDATE, "GetDC", (FARPROC)GetDC, (FARPROC *)&pGDIGetDC, (FARPROC)extEMUGetDC}, + {HOOK_IAT_CANDIDATE, "GetDCEx", (FARPROC)GetDCEx, (FARPROC *)&pGDIGetDCEx, (FARPROC)extEMUGetDCEx}, + {HOOK_IAT_CANDIDATE, "GetWindowDC", (FARPROC)GetWindowDC, (FARPROC *)&pGDIGetWindowDC, (FARPROC)extEMUGetWindowDC}, + {HOOK_IAT_CANDIDATE, "ReleaseDC", (FARPROC)ReleaseDC, (FARPROC *)&pGDIReleaseDC, (FARPROC)extEMUReleaseDC}, + //{HOOK_IAT_CANDIDATE, "InvalidateRect", (FARPROC)InvalidateRect, (FARPROC *)&pInvalidateRect, (FARPROC)extInvalidateRect}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type DDHooks[]={ - {"BeginPaint", (FARPROC)BeginPaint, (FARPROC *)&pBeginPaint, (FARPROC)extDDBeginPaint}, - {"EndPaint", (FARPROC)EndPaint, (FARPROC *)&pEndPaint, (FARPROC)extDDEndPaint}, - {"GetDC", (FARPROC)GetDC, (FARPROC *)&pGDIGetDC, (FARPROC)extDDGetDC}, - {"GetDCEx", (FARPROC)GetDCEx, (FARPROC *)&pGDIGetDCEx, (FARPROC)extDDGetDCEx}, - {"GetWindowDC", (FARPROC)GetWindowDC, (FARPROC *)&pGDIGetWindowDC, (FARPROC)extDDGetDC}, - {"ReleaseDC", (FARPROC)ReleaseDC, (FARPROC *)&pGDIReleaseDC, (FARPROC)extDDReleaseDC}, - {"InvalidateRect", (FARPROC)InvalidateRect, (FARPROC *)&pInvalidateRect, (FARPROC)extInvalidateRect}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "BeginPaint", (FARPROC)BeginPaint, (FARPROC *)&pBeginPaint, (FARPROC)extDDBeginPaint}, + {HOOK_IAT_CANDIDATE, "EndPaint", (FARPROC)EndPaint, (FARPROC *)&pEndPaint, (FARPROC)extDDEndPaint}, + {HOOK_IAT_CANDIDATE, "GetDC", (FARPROC)GetDC, (FARPROC *)&pGDIGetDC, (FARPROC)extDDGetDC}, + {HOOK_IAT_CANDIDATE, "GetDCEx", (FARPROC)GetDCEx, (FARPROC *)&pGDIGetDCEx, (FARPROC)extDDGetDCEx}, + {HOOK_IAT_CANDIDATE, "GetWindowDC", (FARPROC)GetWindowDC, (FARPROC *)&pGDIGetWindowDC, (FARPROC)extDDGetDC}, + {HOOK_IAT_CANDIDATE, "ReleaseDC", (FARPROC)ReleaseDC, (FARPROC *)&pGDIReleaseDC, (FARPROC)extDDReleaseDC}, + {HOOK_IAT_CANDIDATE, "InvalidateRect", (FARPROC)InvalidateRect, (FARPROC *)&pInvalidateRect, (FARPROC)extInvalidateRect}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type ScaledHooks[]={ - {"FrameRect", (FARPROC)NULL, (FARPROC *)&pFrameRect, (FARPROC)extFrameRect}, - {"TabbedTextOutA", (FARPROC)TabbedTextOutA, (FARPROC *)&pTabbedTextOutA, (FARPROC)extTabbedTextOutA}, - {"DrawTextA", (FARPROC)DrawTextA, (FARPROC *)&pDrawText, (FARPROC)extDrawTextA}, - {"DrawTextExA", (FARPROC)DrawTextExA, (FARPROC *)&pDrawTextEx, (FARPROC)extDrawTextExA}, - {"FillRect", (FARPROC)NULL, (FARPROC *)&pFillRect, (FARPROC)extFillRect}, - {"BeginPaint", (FARPROC)BeginPaint, (FARPROC *)&pBeginPaint, (FARPROC)extBeginPaint}, - {"EndPaint", (FARPROC)EndPaint, (FARPROC *)&pEndPaint, (FARPROC)extEndPaint}, - {"GetDC", (FARPROC)GetDC, (FARPROC *)&pGDIGetDC, (FARPROC)extGDIGetDC}, - {"GetDCEx", (FARPROC)NULL, (FARPROC *)&pGDIGetDCEx, (FARPROC)extGDIGetDCEx}, - {"GetWindowDC", (FARPROC)GetWindowDC, (FARPROC *)&pGDIGetWindowDC, (FARPROC)extGDIGetWindowDC}, - {"ReleaseDC", (FARPROC)ReleaseDC, (FARPROC *)&pGDIReleaseDC, (FARPROC)extGDIReleaseDC}, - {"InvalidateRect", (FARPROC)InvalidateRect, (FARPROC *)&pInvalidateRect, (FARPROC)extInvalidateRect}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "FrameRect", (FARPROC)NULL, (FARPROC *)&pFrameRect, (FARPROC)extFrameRect}, + {HOOK_IAT_CANDIDATE, "TabbedTextOutA", (FARPROC)TabbedTextOutA, (FARPROC *)&pTabbedTextOutA, (FARPROC)extTabbedTextOutA}, + {HOOK_IAT_CANDIDATE, "DrawTextA", (FARPROC)DrawTextA, (FARPROC *)&pDrawText, (FARPROC)extDrawTextA}, + {HOOK_IAT_CANDIDATE, "DrawTextExA", (FARPROC)DrawTextExA, (FARPROC *)&pDrawTextEx, (FARPROC)extDrawTextExA}, + {HOOK_IAT_CANDIDATE, "FillRect", (FARPROC)NULL, (FARPROC *)&pFillRect, (FARPROC)extFillRect}, + {HOOK_IAT_CANDIDATE, "BeginPaint", (FARPROC)BeginPaint, (FARPROC *)&pBeginPaint, (FARPROC)extBeginPaint}, + {HOOK_IAT_CANDIDATE, "EndPaint", (FARPROC)EndPaint, (FARPROC *)&pEndPaint, (FARPROC)extEndPaint}, + {HOOK_IAT_CANDIDATE, "GetDC", (FARPROC)GetDC, (FARPROC *)&pGDIGetDC, (FARPROC)extGDIGetDC}, + {HOOK_IAT_CANDIDATE, "GetDCEx", (FARPROC)NULL, (FARPROC *)&pGDIGetDCEx, (FARPROC)extGDIGetDCEx}, + {HOOK_IAT_CANDIDATE, "GetWindowDC", (FARPROC)GetWindowDC, (FARPROC *)&pGDIGetWindowDC, (FARPROC)extGDIGetWindowDC}, + {HOOK_IAT_CANDIDATE, "ReleaseDC", (FARPROC)ReleaseDC, (FARPROC *)&pGDIReleaseDC, (FARPROC)extGDIReleaseDC}, + {HOOK_IAT_CANDIDATE, "InvalidateRect", (FARPROC)InvalidateRect, (FARPROC *)&pInvalidateRect, (FARPROC)extInvalidateRect}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type RemapHooks[]={ - {"ScreenToClient", (FARPROC)ScreenToClient, (FARPROC *)&pScreenToClient, (FARPROC)extScreenToClient}, - {"ClientToScreen", (FARPROC)ClientToScreen, (FARPROC *)&pClientToScreen, (FARPROC)extClientToScreen}, - {"GetClientRect", (FARPROC)GetClientRect, (FARPROC *)&pGetClientRect, (FARPROC)extGetClientRect}, - {"GetWindowRect", (FARPROC)GetWindowRect, (FARPROC *)&pGetWindowRect, (FARPROC)extGetWindowRect}, - {"MapWindowPoints", (FARPROC)MapWindowPoints, (FARPROC *)&pMapWindowPoints, (FARPROC)extMapWindowPoints}, - //{"GetUpdateRect", (FARPROC)GetUpdateRect, (FARPROC *)&pGetUpdateRect, (FARPROC)extGetUpdateRect}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "ScreenToClient", (FARPROC)ScreenToClient, (FARPROC *)&pScreenToClient, (FARPROC)extScreenToClient}, + {HOOK_IAT_CANDIDATE, "ClientToScreen", (FARPROC)ClientToScreen, (FARPROC *)&pClientToScreen, (FARPROC)extClientToScreen}, + {HOOK_IAT_CANDIDATE, "GetClientRect", (FARPROC)GetClientRect, (FARPROC *)&pGetClientRect, (FARPROC)extGetClientRect}, + {HOOK_IAT_CANDIDATE, "GetWindowRect", (FARPROC)GetWindowRect, (FARPROC *)&pGetWindowRect, (FARPROC)extGetWindowRect}, + {HOOK_IAT_CANDIDATE, "MapWindowPoints", (FARPROC)MapWindowPoints, (FARPROC *)&pMapWindowPoints, (FARPROC)extMapWindowPoints}, + //{HOOK_IAT_CANDIDATE, "GetUpdateRect", (FARPROC)GetUpdateRect, (FARPROC *)&pGetUpdateRect, (FARPROC)extGetUpdateRect}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type PeekAllHooks[]={ - {"PeekMessageA", (FARPROC)NULL, (FARPROC *)&pPeekMessage, (FARPROC)extPeekMessage}, - {"PeekMessageW", (FARPROC)NULL, (FARPROC *)&pPeekMessage, (FARPROC)extPeekMessage}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "PeekMessageA", (FARPROC)NULL, (FARPROC *)&pPeekMessage, (FARPROC)extPeekMessage}, + {HOOK_IAT_CANDIDATE, "PeekMessageW", (FARPROC)NULL, (FARPROC *)&pPeekMessage, (FARPROC)extPeekMessage}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type MouseHooks[]={ - {"GetCursorPos", (FARPROC)GetCursorPos, (FARPROC *)&pGetCursorPos, (FARPROC)extGetCursorPos}, - {"SetCursor", (FARPROC)SetCursor, (FARPROC *)&pSetCursor, (FARPROC)extSetCursor}, - {"SendMessageA", (FARPROC)SendMessageA, (FARPROC *)&pSendMessageA, (FARPROC)extSendMessageA}, - {"SendMessageW", (FARPROC)SendMessageW, (FARPROC *)&pSendMessageW, (FARPROC)extSendMessageW}, - //{"SetPhysicalCursorPos", NULL, (FARPROC *)&pSetCursor, (FARPROC)extSetCursor}, // ??? - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "GetCursorPos", (FARPROC)GetCursorPos, (FARPROC *)&pGetCursorPos, (FARPROC)extGetCursorPos}, + {HOOK_IAT_CANDIDATE, "GetCursorInfo", (FARPROC)GetCursorInfo, (FARPROC *)&pGetCursorInfo, (FARPROC)extGetCursorInfo}, + {HOOK_IAT_CANDIDATE, "SetCursor", (FARPROC)SetCursor, (FARPROC *)&pSetCursor, (FARPROC)extSetCursor}, + {HOOK_IAT_CANDIDATE, "SendMessageA", (FARPROC)SendMessageA, (FARPROC *)&pSendMessageA, (FARPROC)extSendMessageA}, + {HOOK_IAT_CANDIDATE, "SendMessageW", (FARPROC)SendMessageW, (FARPROC *)&pSendMessageW, (FARPROC)extSendMessageW}, + //{HOOK_IAT_CANDIDATE, "SetPhysicalCursorPos", NULL, (FARPROC *)&pSetCursor, (FARPROC)extSetCursor}, // ??? + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type WinHooks[]={ - {"ShowWindow", (FARPROC)ShowWindow, (FARPROC *)&pShowWindow, (FARPROC)extShowWindow}, - {"SetWindowPos", (FARPROC)SetWindowPos, (FARPROC *)&pSetWindowPos, (FARPROC)extSetWindowPos}, - {"DeferWindowPos", (FARPROC)DeferWindowPos, (FARPROC *)&pGDIDeferWindowPos, (FARPROC)extDeferWindowPos}, - {"CallWindowProcA", (FARPROC)CallWindowProcA, (FARPROC *)&pCallWindowProc, (FARPROC)extCallWindowProc}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "ShowWindow", (FARPROC)ShowWindow, (FARPROC *)&pShowWindow, (FARPROC)extShowWindow}, + {HOOK_IAT_CANDIDATE, "SetWindowPos", (FARPROC)SetWindowPos, (FARPROC *)&pSetWindowPos, (FARPROC)extSetWindowPos}, + {HOOK_IAT_CANDIDATE, "DeferWindowPos", (FARPROC)DeferWindowPos, (FARPROC *)&pGDIDeferWindowPos, (FARPROC)extDeferWindowPos}, + {HOOK_IAT_CANDIDATE, "CallWindowProcA", (FARPROC)CallWindowProcA, (FARPROC *)&pCallWindowProc, (FARPROC)extCallWindowProc}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type MouseHooks2[]={ - {"SetCursorPos", (FARPROC)SetCursorPos, (FARPROC *)&pSetCursorPos, (FARPROC)extSetCursorPos}, - {0, NULL, 0, 0} // terminator + {HOOK_HOT_CANDIDATE, "SetCursorPos", (FARPROC)SetCursorPos, (FARPROC *)&pSetCursorPos, (FARPROC)extSetCursorPos}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type TimeHooks[]={ - {"SetTimer", (FARPROC)SetTimer, (FARPROC *)&pSetTimer, (FARPROC)extSetTimer}, - {"KillTimer", (FARPROC)KillTimer, (FARPROC *)&pKillTimer, (FARPROC)extKillTimer}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "SetTimer", (FARPROC)SetTimer, (FARPROC *)&pSetTimer, (FARPROC)extSetTimer}, + {HOOK_IAT_CANDIDATE, "KillTimer", (FARPROC)KillTimer, (FARPROC *)&pKillTimer, (FARPROC)extKillTimer}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; FARPROC Remap_user32_ProcAddress(LPCSTR proc, HMODULE hModule) @@ -182,6 +186,7 @@ void HookUser32(HMODULE hModule) if (dxw.dwFlags3 & PEEKALLMESSAGES) HookLibrary(hModule, PeekAllHooks, libname); if (dxw.dwFlags2 & TIMESTRETCH) HookLibrary(hModule, TimeHooks, libname); + IsChangeDisplaySettingsHotPatched = IsHotPatched(Hooks, "ChangeDisplaySettingsExA") || IsHotPatched(Hooks, "ChangeDisplaySettingsExW"); return; } @@ -286,7 +291,7 @@ LONG WINAPI MyChangeDisplaySettings(char *fname, BOOL WideChar, void *lpDevMode, NewMode.dmPelsWidth, NewMode.dmPelsHeight, NewMode.dmBitsPerPel, dmBitsPerPel); NewMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; NewMode.dmBitsPerPel = dmBitsPerPel; - res=(*pChangeDisplaySettingsA)(&NewMode, 0); + res=(*pChangeDisplaySettingsExA)(NULL, &NewMode, NULL, 0, NULL); if(res) OutTraceE("ChangeDisplaySettings: ERROR err=%d at %d\n", GetLastError(), __LINE__); return res; } @@ -295,7 +300,7 @@ LONG WINAPI MyChangeDisplaySettings(char *fname, BOOL WideChar, void *lpDevMode, if(WideChar) return (*pChangeDisplaySettingsW)((LPDEVMODEW)lpDevMode, dwflags); else - return (*pChangeDisplaySettingsA)((LPDEVMODEA)lpDevMode, dwflags); + return (*pChangeDisplaySettingsExA)(NULL, (LPDEVMODEA)lpDevMode, NULL, dwflags, NULL); } } @@ -1337,12 +1342,21 @@ static LPCSTR ClassToStr(LPCSTR Class) { static char AtomBuf[20+1]; if(((DWORD)Class & 0xFFFF0000) == 0){ - sprintf(AtomBuf, "ATOM(%X)", Class); + sprintf(AtomBuf, "ATOM(%X)", (DWORD)Class); return AtomBuf; } return Class; } +static LPCWSTR ClassToWStr(LPCWSTR Class) +{ + static WCHAR AtomBuf[20+1]; + if(((DWORD)Class & 0xFFFF0000) == 0){ + swprintf(AtomBuf, L"ATOM(%X)", (DWORD)Class); + return AtomBuf; + } + return Class; +} // to do: implement and use ClassToWStr() for widechar call HWND WINAPI extCreateWindowExW( @@ -1370,7 +1384,7 @@ HWND WINAPI extCreateWindowExW( if (nHeight==CW_USEDEFAULT) strcpy(hString,"CW_USEDEFAULT"); else sprintf(hString,"%d", nHeight); OutTrace("CreateWindowExW: class=\"%ls\" wname=\"%ls\" pos=(%s,%s) size=(%s,%s) Style=%x(%s) ExStyle=%x(%s)\n", - lpClassName, lpWindowName, xString, yString, wString, hString, + ClassToWStr(lpClassName), lpWindowName, xString, yString, wString, hString, dwStyle, ExplainStyle(dwStyle), dwExStyle, ExplainExStyle(dwExStyle)); } if(IsDebug) OutTrace("CreateWindowExW: DEBUG screen=(%d,%d)\n", dxw.GetScreenWidth(), dxw.GetScreenHeight()); @@ -1645,7 +1659,7 @@ LONG WINAPI extChangeDisplaySettingsA(DEVMODEA *lpDevMode, DWORD dwflags) if(dxw.Windowize) return MyChangeDisplaySettings("ChangeDisplaySettingsA", FALSE, lpDevMode, dwflags); else - return (*pChangeDisplaySettingsA)(lpDevMode, dwflags); + return (*pChangeDisplaySettingsExA)(NULL, lpDevMode, NULL, dwflags, NULL); } LONG WINAPI extChangeDisplaySettingsW(DEVMODEW *lpDevMode, DWORD dwflags) @@ -2532,4 +2546,63 @@ BOOL WINAPI extGetUpdateRect(HWND hWnd, LPRECT lpRect, BOOL bErase) else OutTraceE("GetUpdateRect ERROR: err=%d\n", GetLastError()); return ret; -} \ No newline at end of file +} + +BOOL WINAPI extGetCursorInfo(PCURSORINFO pci) +{ + BOOL ret; + OutTraceDW("GetCursorInfo\n"); + ret = (*pGetCursorInfo)(pci); + if(ret){ + OutTraceDW("GetCursorInfo: flags=%x hcursor=%x pos=(%d,%d)\n", pci->flags, pci->hCursor, pci->ptScreenPos.x, pci->ptScreenPos.y); + if(dxw.IsFullScreen()){ + dxw.UnmapClient(&(pci->ptScreenPos)); + OutTraceDW("GetCursorInfo: FIXED pos=(%d,%d)\n", pci->ptScreenPos.x, pci->ptScreenPos.y); + } + } + else + OutTraceE("GetCursorInfo ERROR: err=%d\n", GetLastError()); + return ret; +} + +// --- to be hooked .... + +HWND WINAPI extWindowFromPoint(POINT Point) +{ + HWND ret; + OutTraceDW("WindowFromPoint: point=(%d,%d)\n", Point.x, Point.y); + if(dxw.IsFullScreen()){ + dxw.UnmapClient(&Point); + OutTraceDW("WindowFromPoint: FIXED point=(%d,%d)\n", Point.x, Point.y); + } + ret = (*pWindowFromPoint)(Point); + OutTraceDW("WindowFromPoint: hwnd=%x\n", ret); + return ret; +} + +HWND WINAPI extChildWindowFromPoint(HWND hWndParent, POINT Point) +{ + HWND ret; + OutTraceDW("ChildWindowFromPoint: hWndParent=%x point=(%d,%d)\n", hWndParent, Point.x, Point.y); + if(dxw.IsDesktop(hWndParent) && dxw.IsFullScreen()){ + dxw.UnmapClient(&Point); + OutTraceDW("ChildWindowFromPoint: FIXED point=(%d,%d)\n", Point.x, Point.y); + } + ret = (*pChildWindowFromPoint)(hWndParent, Point); + OutTraceDW("ChildWindowFromPoint: hwnd=%x\n", ret); + return ret; +} + +HWND WINAPI extChildWindowFromPointEx(HWND hWndParent, POINT Point, UINT uFlags) +{ + HWND ret; + OutTraceDW("ChildWindowFromPoint: hWndParent=%x point=(%d,%d) flags=%x\n", hWndParent, Point.x, Point.y, uFlags); + if(dxw.IsDesktop(hWndParent) && dxw.IsFullScreen()){ + dxw.UnmapClient(&Point); + OutTraceDW("ChildWindowFromPointEx: FIXED point=(%d,%d)\n", Point.x, Point.y); + } + ret = (*pChildWindowFromPointEx)(hWndParent, Point, uFlags); + OutTraceDW("ChildWindowFromPointEx: hwnd=%x\n", ret); + return ret; +} + diff --git a/dll/winmm.cpp b/dll/winmm.cpp index c573f76..e594511 100644 --- a/dll/winmm.cpp +++ b/dll/winmm.cpp @@ -10,15 +10,15 @@ //#define OutTraceDW OutTrace static HookEntry_Type Hooks[]={ - {"mciSendCommandA", NULL, (FARPROC *)&pmciSendCommand, (FARPROC)extmciSendCommand}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "mciSendCommandA", NULL, (FARPROC *)&pmciSendCommand, (FARPROC)extmciSendCommand}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; static HookEntry_Type TimeHooks[]={ - {"timeGetTime", NULL, (FARPROC *)&ptimeGetTime, (FARPROC)exttimeGetTime}, - {"timeKillEvent", NULL, (FARPROC *)&ptimeKillEvent, (FARPROC)exttimeKillEvent}, - {"timeSetEvent", NULL, (FARPROC *)&ptimeSetEvent, (FARPROC)exttimeSetEvent}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "timeGetTime", NULL, (FARPROC *)&ptimeGetTime, (FARPROC)exttimeGetTime}, + {HOOK_IAT_CANDIDATE, "timeKillEvent", NULL, (FARPROC *)&ptimeKillEvent, (FARPROC)exttimeKillEvent}, + {HOOK_IAT_CANDIDATE, "timeSetEvent", NULL, (FARPROC *)&ptimeSetEvent, (FARPROC)exttimeSetEvent}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; void HookWinMM(HMODULE module) diff --git a/dll/wintrust.cpp b/dll/wintrust.cpp index d2c1406..5ac19e9 100644 --- a/dll/wintrust.cpp +++ b/dll/wintrust.cpp @@ -10,8 +10,8 @@ WinVerifyTrust_Type pWinVerifyTrust; extern LONG WINAPI extWinVerifyTrust(HWND, GUID *, LPVOID); static HookEntry_Type Hooks[]={ - {"WinVerifyTrust", NULL, (FARPROC *)&pWinVerifyTrust, (FARPROC)extWinVerifyTrust}, - {0, NULL, 0, 0} // terminator + {HOOK_IAT_CANDIDATE, "WinVerifyTrust", NULL, (FARPROC *)&pWinVerifyTrust, (FARPROC)extWinVerifyTrust}, + {HOOK_IAT_CANDIDATE, 0, NULL, 0, 0} // terminator }; void HookTrust(HMODULE module) diff --git a/host/Resource.h b/host/Resource.h index ae52826..4333196 100644 --- a/host/Resource.h +++ b/host/Resource.h @@ -213,8 +213,10 @@ #define IDC_DISABLEFOGGING 1175 #define IDC_RELEASEMOUSE 1176 #define IDC_HOOKGLIDE 1177 -#define IDC_RELEASEMOUSE2 1177 -#define IDC_FRAMECOMPENSATION 1177 +#define IDC_FRAMECOMPENSATION 1178 +#define IDC_HOTPATCH 1179 +#define IDC_ENABLEHOTKEYS 1180 +#define IDC_HOTPATCHALWAYS 1181 #define ID_MODIFY 32771 #define ID_DELETE 32772 #define ID_ADD 32773 diff --git a/host/TabDebug.cpp b/host/TabDebug.cpp index ea57c1d..4c6ece2 100644 --- a/host/TabDebug.cpp +++ b/host/TabDebug.cpp @@ -42,6 +42,7 @@ void CTabDebug::DoDataExchange(CDataExchange* pDX) DDX_Check(pDX, IDC_NOFILLRECT, cTarget->m_NoFillRect); DDX_Check(pDX, IDC_EMULATEREGISTRY, cTarget->m_EmulateRegistry); DDX_Check(pDX, IDC_ZBUFFERALWAYS, cTarget->m_ZBufferAlways); + DDX_Check(pDX, IDC_HOTPATCHALWAYS, cTarget->m_HotPatchAlways); } BEGIN_MESSAGE_MAP(CTabDebug, CDialog) diff --git a/host/TabInput.cpp b/host/TabInput.cpp index 473d65c..d455299 100644 --- a/host/TabInput.cpp +++ b/host/TabInput.cpp @@ -50,6 +50,9 @@ void CTabInput::DoDataExchange(CDataExchange* pDX) // Message processing DDX_Check(pDX, IDC_FILTERMESSAGES, cTarget->m_FilterMessages); DDX_Check(pDX, IDC_PEEKALLMESSAGES, cTarget->m_PeekAllMessages); + // Keyboard handling + DDX_Check(pDX, IDC_ENABLEHOTKEYS, cTarget->m_EnableHotKeys); + DDX_Check(pDX, IDC_HANDLEALTF4, cTarget->m_HandleAltF4); } BEGIN_MESSAGE_MAP(CTabInput, CDialog) diff --git a/host/TabProgram.cpp b/host/TabProgram.cpp index 31cffe5..4de2383 100644 --- a/host/TabProgram.cpp +++ b/host/TabProgram.cpp @@ -35,6 +35,7 @@ void CTabProgram::DoDataExchange(CDataExchange* pDX) DDX_Text(pDX, IDC_MODULE, cTarget->m_Module); DDX_Check(pDX, IDC_UNNOTIFY, cTarget->m_UnNotify); DDX_Check(pDX, IDC_WINDOWIZE, cTarget->m_Windowize); + DDX_Check(pDX, IDC_HOTPATCH, cTarget->m_HotPatch); DDX_Check(pDX, IDC_HOOKDLLS, cTarget->m_HookDLLs); DDX_Check(pDX, IDC_FULLSCREENONLY, cTarget->m_FullScreenOnly); DDX_Check(pDX, IDC_HOOKCHILDWIN, cTarget->m_HookChildWin); @@ -42,9 +43,9 @@ void CTabProgram::DoDataExchange(CDataExchange* pDX) DDX_Check(pDX, IDC_STARTDEBUG, cTarget->m_StartDebug); DDX_Check(pDX, IDC_CLIENTREMAPPING, cTarget->m_ClientRemapping); DDX_Check(pDX, IDC_SAVELOAD, cTarget->m_SaveLoad); - DDX_Check(pDX, IDC_HANDLEALTF4, cTarget->m_HandleAltF4); DDX_Check(pDX, IDC_KEEPASPECTRATIO, cTarget->m_KeepAspectRatio); DDX_Check(pDX, IDC_HOOKOPENGL, cTarget->m_HookOpenGL); // duplicated + DDX_Check(pDX, IDC_NOBANNER, cTarget->m_NoBanner); DDX_Text(pDX, IDC_POSX, cTarget->m_PosX); DDX_Text(pDX, IDC_POSY, cTarget->m_PosY); DDX_Text(pDX, IDC_SIZX, cTarget->m_SizX); diff --git a/host/TabWindow.cpp b/host/TabWindow.cpp index 7c95213..813f0ae 100644 --- a/host/TabWindow.cpp +++ b/host/TabWindow.cpp @@ -45,7 +45,6 @@ void CTabWindow::DoDataExchange(CDataExchange* pDX) DDX_Check(pDX, IDC_NOWINDOWMOVE, cTarget->m_NoWindowMove); DDX_Check(pDX, IDC_SUPPRESSCHILD, cTarget->m_SuppressChild); DDX_Check(pDX, IDC_HIDEDESKTOP, cTarget->m_HideDesktop); - DDX_Check(pDX, IDC_NOBANNER, cTarget->m_NoBanner); // color management DDX_Check(pDX, IDC_INIT8BPP, cTarget->m_Init8BPP); diff --git a/host/TargetDlg.cpp b/host/TargetDlg.cpp index 63b4ca7..6288195 100644 --- a/host/TargetDlg.cpp +++ b/host/TargetDlg.cpp @@ -61,11 +61,13 @@ CTargetDlg::CTargetDlg(CWnd* pParent /*=NULL*/) m_ZBufferClean = FALSE; m_ZBuffer0Clean = FALSE; m_ZBufferAlways = FALSE; + m_HotPatchAlways = FALSE; m_DisableFogging = FALSE; m_NoPower2Fix = FALSE; m_NoPerfCounter = FALSE; m_UnNotify = FALSE; m_Windowize = TRUE; // default true !! + m_HotPatch = FALSE; m_HookDLLs = TRUE; // default true !! m_HookEnabled = TRUE; // default true !! m_EmulateRegistry = FALSE; @@ -128,6 +130,7 @@ CTargetDlg::CTargetDlg(CWnd* pParent /*=NULL*/) m_FineTiming = FALSE; m_ReleaseMouse = FALSE; m_FrameCompensation = FALSE; + m_EnableHotKeys = TRUE; // default true !! m_InterceptRDTSC = FALSE; m_HookOpenGL = FALSE; m_ForceHookOpenGL = FALSE; @@ -144,6 +147,7 @@ CTargetDlg::CTargetDlg(CWnd* pParent /*=NULL*/) m_WireFrame = FALSE; m_NoTextures = FALSE; m_BlackWhite = FALSE; + m_AssertDialog = FALSE; m_InitX = 0; m_InitY = 0; m_MaxX = 0; diff --git a/host/TargetDlg.h b/host/TargetDlg.h index 23a6cae..cf3b960 100644 --- a/host/TargetDlg.h +++ b/host/TargetDlg.h @@ -46,6 +46,7 @@ public: //BOOL m_HandleDC; BOOL m_UnNotify; BOOL m_Windowize; + BOOL m_HotPatch; BOOL m_HookDLLs; BOOL m_EmulateRegistry; BOOL m_FullScreenOnly; @@ -111,6 +112,7 @@ public: BOOL m_FineTiming; BOOL m_ReleaseMouse; BOOL m_FrameCompensation; + BOOL m_EnableHotKeys; BOOL m_InterceptRDTSC; BOOL m_HookOpenGL; BOOL m_ForceHookOpenGL; @@ -131,6 +133,7 @@ public: BOOL m_ZBufferClean; BOOL m_ZBuffer0Clean; BOOL m_ZBufferAlways; + BOOL m_HotPatchAlways; BOOL m_NoPower2Fix; BOOL m_NoPerfCounter; BOOL m_DisableFogging; diff --git a/host/dxwndhost.aps b/host/dxwndhost.aps index 10302559cc0e7f0fd0109b9e05b083f8de0bbcf4..0cb5c3dd5f7968d0d52a335b123e0afb7dfc7022 100644 GIT binary patch delta 3775 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 delta 3215 zcmZu!3s6+o8UFvX4`7$@5SE37SwsbmHnO0BRU2iKU6DXx|ra_NNjOyo1hc6YfG{;kxSCB3DbY{X;g)4Ry z<@$1S^70gR2J`}O^TIg(O|+5q0Y7&l!P>7F=jP;jeDewla%JO;>2Qq?oQT2eJj-I4 zdP8wgX`kFeG0cFQBjm`yPKN{j4ZQMXblfdPr9H`hw0llRI()^?S}eA&6;+wd>nbUd zx6SEz7QUh3A{JSA_etZD0o79ITHq`66cwv`>vTBbwj#)b_=<{M#U({b@(j46@a}S# z*X#3?=M>9yngi}CbdjsnBd<^+|HIFoissd)f?@`>tg?JJEl!cs;ak2lCIRn}X)J!n z|8go8@AIA*3x2=W97FjL!==-qj3JLaGmhz0rr?Yj^*k-s!c52yy=7-+;0#f%|tFOPf;B>iO;T*w0Ne! z^rh|E0Jz-$?JL(&KLI3(AKPN#St}zn+b;jBE1yCQH2dpUOwg2y1x0*22U$*l5bjSrlJ*=L`24P@xKIcO z9YoL_2@Ik@5787`0Q_%s^#@@Fue_QX5(M&f3-bA)tCe9AUEAn5>lBs;_hv9U+pOMg z@}s@?dHE6C_p6_~x(|=coA9BOZR7~az1~dJuZ)WI%*28OHf@mH&953n&7%MI% z(zGpKJ=FOp+!-{p{XyI|G?kYRI(gktp&=Zqz{LMKG~R+rSNV?)&>I1jav>f*oNaTz z5RhnIZxHEQ;W*7nua}zP6ziIG0a;DVfz>oYFyAL7@{VD8?|j?}&J`3!hLJafOHV*i!yT;eGmfpjzL0!n%T(o*v%)r7b0cs;{8m2$E!= zyXpyR2Qh8E+}3YP*+JoN`vTSjPgrMCsp5YA&NZ9yLprvz*SYDY&Dck5!vLRr$7bvd z257v?rw-bT`^oU;18yI(1+CTrICUtF8Za*CeX_J0QA`OTkVP$;!ckm|Bv56bH4&IH zhJcN7Hd#=-Yb8)k%DGq+w`>G9QO-O&ir~k%S^~0zM}?k1)ON zelm(Rlh9_MrdcnFdqa#xV*CDq)s z0cp>4@-~Y(M_CN}8CBm-Y7MEn-!;;(LPO?FCj;If;cI3V-Hs2a(Yb)^{Wu_p2&tJ$ zT|!acC96lNk>h4SHLJ-h22fnNL-JXYb+y36Eym(RXA3i!3``p;O!Wp^nb;CXDtQfT z5)*GsmaPO#&jhTI!grKrYMRYNxQkSI0p?D3K#o!>nZGCr5YcMs6E8Cn|0?+#s4;F7 z|7f8-x>Sy$U?I&Gag2)mO4*|@8fmAHTA0OL(jOldIJ*! w`{jynQ^i3-tGHZIL7dP*LJcx%YNSa7p|gY%vgkhzNix!XmtKGXqrb=h1;bykYXATM diff --git a/host/dxwndhost.rc b/host/dxwndhost.rc index a22a4ad..ce02451 100644 --- a/host/dxwndhost.rc +++ b/host/dxwndhost.rc @@ -199,7 +199,7 @@ END // Dialog // -IDD_ABOUTBOX DIALOGEX 0, 0, 235, 78 +IDD_ABOUTBOX DIALOGEX 0, 0, 235, 88 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "DXWnd Version Information" FONT 9, "Arial", 400, 0, 0x0 @@ -211,6 +211,7 @@ BEGIN LTEXT "thank you also to:",IDC_STATIC,40,40,119,8 LTEXT "Olly (www.ollydbg.de) for disasm lib",IDC_STATIC,40,50,119,8 LTEXT "Michael Koch for d3d9 proxy dll",IDC_STATIC,40,60,181,8 + LTEXT "Aqrit for hot patching schema",IDC_STATIC,40,70,181,8 END IDD_TARGET DIALOGEX 0, 0, 273, 248 @@ -272,7 +273,6 @@ BEGIN EDITTEXT IDC_SIZY,113,210,19,14,ES_AUTOHSCROLL,WS_EX_RIGHT CONTROL "Do not notify on task switch",IDC_UNNOTIFY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,105,124,10 CONTROL "Optimize CPU (DirectX1 - 7)",IDC_SAVELOAD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,170,141,109,10 - CONTROL "Intercept Alt-F4 key",IDC_HANDLEALTF4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,117,109,10 CONTROL "Run in Window",IDC_WINDOWIZE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,129,124,10 GROUPBOX "Generic",IDC_STATIC,7,82,286,89 CONTROL "use DLL Injection",IDC_STARTDEBUG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,170,117,100,10 @@ -291,6 +291,8 @@ BEGIN EDITTEXT IDC_LAUNCH,39,48,162,14,ES_AUTOHSCROLL LTEXT "Launch:",IDC_STATIC,11,51,28,9 PUSHBUTTON "...",IDC_OPENLAUNCH,207,48,12,14 + CONTROL "Hot patch (obfuscated IAT)",IDC_HOTPATCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,170,105,116,10 + CONTROL "No banner",IDC_NOBANNER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,117,108,10 END IDD_TAB_LOG DIALOGEX 0, 0, 300, 240 @@ -357,17 +359,17 @@ STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Correct mouse position",IDC_MODIFYMOUSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,38,107,12 - CONTROL "Hide Cursor",IDC_HIDEHWCURSOR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,15,100,10 + CONTROL "Hide Cursor",IDC_HIDEHWCURSOR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,14,100,12 CONTROL "Intercept GDI Cursor Clipping",IDC_ENABLECLIPPING, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,97,109,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,98,109,12 GROUPBOX "Cursor handling",IDC_STATIC,7,3,129,162 - CONTROL "Keep cursor within window",IDC_KEEPCURSORWITHIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,73,108,10 - CONTROL "Force cursor clipping",IDC_CLIPCURSOR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,62,108,10 - CONTROL "Message processing",IDC_MESSAGEPROC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,108,103,10 - CONTROL "Keep cursor fixed",IDC_KEEPCURSORFIXED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,85,107,10 - CONTROL "fix WM_NCHITTEST",IDC_FIXNCHITTEST,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,119,103,10 - CONTROL "Show Cursor",IDC_SHOWHWCURSOR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,27,104,10 - CONTROL "Slow down mouse polling",IDC_SLOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,130,103,12 + CONTROL "Keep cursor within window",IDC_KEEPCURSORWITHIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,74,108,12 + CONTROL "Force cursor clipping",IDC_CLIPCURSOR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,62,108,12 + CONTROL "Message processing",IDC_MESSAGEPROC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,110,103,12 + CONTROL "Keep cursor fixed",IDC_KEEPCURSORFIXED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,86,107,12 + CONTROL "fix WM_NCHITTEST",IDC_FIXNCHITTEST,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,122,103,12 + CONTROL "Show Cursor",IDC_SHOWHWCURSOR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,26,104,12 + CONTROL "Slow down mouse polling",IDC_SLOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,134,103,12 LTEXT "Initial coord.",IDC_STATIC,148,33,46,9 LTEXT "X",IDC_STATIC,234,34,9,9 LTEXT "Y",IDC_STATIC,261,34,9,9 @@ -388,8 +390,11 @@ BEGIN CONTROL "Filter offending messages",IDC_FILTERMESSAGES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,96,114,10 CONTROL "Peek all messages in queue",IDC_PEEKALLMESSAGES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,108,114,10 GROUPBOX "Message handling",IDC_STATIC,142,79,151,72 - CONTROL "Release mouse outside window",IDC_RELEASEMOUSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,142,114,12 + CONTROL "Release mouse outside window",IDC_RELEASEMOUSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,146,114,12 CONTROL "Window frame compensation",IDC_FRAMECOMPENSATION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,50,114,12 + GROUPBOX "Keyboard handling",IDC_STATIC,7,167,130,66 + CONTROL "Enable hot keys",IDC_ENABLEHOTKEYS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,180,114,12 + CONTROL "Intercept Alt-F4 key",IDC_HANDLEALTF4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,192,109,12 END IDD_TAB_TIMING DIALOGEX 0, 0, 300, 240 @@ -448,7 +453,6 @@ BEGIN CONTROL "SVGA modes",IDC_SUPPORTSVGA,"Button",BS_AUTORADIOBUTTON | WS_GROUP,160,136,124,10 CONTROL "HDMI modes",IDC_SUPPORTHDMI,"Button",BS_AUTORADIOBUTTON,160,148,124,10 CONTROL "Monitor native modes",IDC_NATIVERES,"Button",BS_AUTORADIOBUTTON,160,160,124,10 - CONTROL "No banner",IDC_NOBANNER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,196,108,10 END IDD_TAB_EMPTY DIALOGEX 0, 0, 300, 240 @@ -524,6 +528,7 @@ BEGIN CONTROL "Emulate Registry",IDC_EMULATEREGISTRY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,84,124,10 CONTROL "Force D3DCMP_ALWAYS setting ",IDC_ZBUFFERALWAYS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,159,164,127,10 CONTROL "Suppress FillRect",IDC_NOFILLRECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,160,60,127,10 + CONTROL "Force hot patching",IDC_HOTPATCHALWAYS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,159,176,127,10 END IDD_PALETTE DIALOGEX 0, 0, 169, 167 @@ -586,7 +591,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 228 TOPMARGIN, 7 - BOTTOMMARGIN, 71 + BOTTOMMARGIN, 81 END IDD_TARGET, DIALOG diff --git a/host/dxwndhost.vs2008.suo b/host/dxwndhost.vs2008.suo index f8013afb9fca6e022ff8608c26e39371cecde512..bfbcff90484ad59ab899197f781769afcba15a14 100644 GIT binary patch delta 14743 zcmcJ04O~>!*7xk=KqHRRI3i9&bcDf(5JyBbZmWj1=fmEAJzTDMH)`LBJ>VBO#Qyw7vr_j!)L!%$M ztDJhU+W5Uy}j`BVp0UG22C{8G2=>Vh^`3d{GG&NO>) zc^c#fYIj}xs5UsV;HYDr@474z{Wr~Gxm!?G>O9S6sXL#VZk6j-iZm+dqPM!sl27+_ zc~$k{1G5jj?|>(?yh$m7He{@#;~kSo&v3TQvfOF8&vLJ2yrsx8k%kWLM8U3Ms>w@l zcMt0k8}F~czAwEoEx9i2eM(r@;j}K0nQx2QiKCbntwc}3X}+jC-}@bnzu8xln;)|W zZBRJ2+9yQk=uSA@ennkX?n8opuqTnvXO++TwIn*T!A|96y10!F=HE^aO^T&+n|5AuI#uTDGI*=V5oIJoDdZa#M<0%l7q`>8aVeBr8AqXE(Q=+k#EB?sSCmYFepxcM zgBVErmpLgo$VNr|Jk$W~`dFp>b`MwYOhXBAEdRq&GH3xDoGYS zspQB_7+4DJsE}upwOH{g+Eb*{CgLbqluEHh!>Md?5{1s#WjG`f$vMMD&LS(tð$ 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 PSIRQfrN8^PXvhBnK)Rad diff --git a/host/dxwndhostView.cpp b/host/dxwndhostView.cpp index b7a58c6..bd2ef69 100644 --- a/host/dxwndhostView.cpp +++ b/host/dxwndhostView.cpp @@ -119,6 +119,7 @@ static void SetTargetFromDlg(TARGETMAP *t, CTargetDlg *dlg) if(dlg->m_HookEnabled) t->flags3 |= HOOKENABLED; if(dlg->m_NoBanner) t->flags2 |= NOBANNER; if(dlg->m_StartDebug) t->flags2 |= STARTDEBUG; + if(dlg->m_HotPatch) t->flags4 |= HOTPATCH; if(dlg->m_FullScreenOnly) t->flags3 |= FULLSCREENONLY; if(dlg->m_FilterMessages) t->flags3 |= FILTERMESSAGES; if(dlg->m_PeekAllMessages) t->flags3 |= PEEKALLMESSAGES; @@ -169,6 +170,7 @@ static void SetTargetFromDlg(TARGETMAP *t, CTargetDlg *dlg) if(dlg->m_ZBufferClean) t->flags4 |= ZBUFFERCLEAN; if(dlg->m_ZBuffer0Clean) t->flags4 |= ZBUFFER0CLEAN; if(dlg->m_ZBufferAlways) t->flags4 |= ZBUFFERALWAYS; + if(dlg->m_HotPatchAlways) t->flags4 |= HOTPATCHALWAYS; if(dlg->m_NoPower2Fix) t->flags4 |= NOPOWER2FIX; if(dlg->m_NoPerfCounter) t->flags4 |= NOPERFCOUNTER; if(dlg->m_DisableFogging) t->flags4 |= DISABLEFOGGING; @@ -242,6 +244,7 @@ static void SetTargetFromDlg(TARGETMAP *t, CTargetDlg *dlg) if(dlg->m_FineTiming) t->flags4 |= FINETIMING; if(dlg->m_ReleaseMouse) t->flags4 |= RELEASEMOUSE; if(dlg->m_FrameCompensation) t->flags4 |= FRAMECOMPENSATION; + if(dlg->m_EnableHotKeys) t->flags4 |= ENABLEHOTKEYS; if(dlg->m_InterceptRDTSC) t->flags4 |= INTERCEPTRDTSC; if(dlg->m_HookOpenGL) t->flags2 |= HOOKOPENGL; if(dlg->m_ForceHookOpenGL) t->flags3 |= FORCEHOOKOPENGL; @@ -286,6 +289,7 @@ static void SetDlgFromTarget(TARGETMAP *t, CTargetDlg *dlg) dlg->m_OpenGLLib = t->OpenGLLib; dlg->m_UnNotify = t->flags & UNNOTIFY ? 1 : 0; dlg->m_Windowize = t->flags2 & WINDOWIZE ? 1 : 0; + dlg->m_HotPatch = t->flags4 & HOTPATCH ? 1 : 0; dlg->m_HookDLLs = t->flags3 & HOOKDLLS ? 1 : 0; dlg->m_EmulateRegistry = t->flags3 & EMULATEREGISTRY ? 1 : 0; dlg->m_HookEnabled = t->flags3 & HOOKENABLED ? 1 : 0; @@ -353,6 +357,7 @@ static void SetDlgFromTarget(TARGETMAP *t, CTargetDlg *dlg) dlg->m_ZBufferClean = t->flags4 & ZBUFFERCLEAN ? 1 : 0; dlg->m_ZBuffer0Clean = t->flags4 & ZBUFFER0CLEAN ? 1 : 0; dlg->m_ZBufferAlways = t->flags4 & ZBUFFERALWAYS ? 1 : 0; + dlg->m_HotPatchAlways = t->flags4 & HOTPATCHALWAYS ? 1 : 0; dlg->m_NoPower2Fix = t->flags4 & NOPOWER2FIX ? 1 : 0; dlg->m_NoPerfCounter = t->flags4 & NOPERFCOUNTER ? 1 : 0; dlg->m_DisableFogging = t->flags4 & DISABLEFOGGING ? 1 : 0; @@ -408,6 +413,7 @@ static void SetDlgFromTarget(TARGETMAP *t, CTargetDlg *dlg) dlg->m_FineTiming = t->flags4 & FINETIMING ? 1 : 0; dlg->m_ReleaseMouse = t->flags4 & RELEASEMOUSE ? 1 : 0; dlg->m_FrameCompensation = t->flags4 & FRAMECOMPENSATION ? 1 : 0; + dlg->m_EnableHotKeys = t->flags4 & ENABLEHOTKEYS ? 1 : 0; dlg->m_InterceptRDTSC = t->flags4 & INTERCEPTRDTSC ? 1 : 0; dlg->m_HookOpenGL = t->flags2 & HOOKOPENGL ? 1 : 0; dlg->m_ForceHookOpenGL = t->flags3 & FORCEHOOKOPENGL ? 1 : 0;