From e8bfc5841747c730dba1d034bcf1a809d342ac84 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 5 Oct 2015 13:11:26 -0400 Subject: [PATCH 01/57] initial changes to master repo --- README | 38 ++++++++++++++++++++++---------------- README.mdown | 35 +++++++++++++++++------------------ main.cpp | 12 +++++++++++- rawinput.cpp | 27 +++++++++++++++++++++++---- versioninfo.h | 4 ++-- 5 files changed, 75 insertions(+), 41 deletions(-) diff --git a/README b/README index 0a6d10e..e9cab48 100644 --- a/README +++ b/README @@ -1,23 +1,27 @@ -RInput Library v1.31 ----------------------------------- +RInput Library v1.32 by Vols and Jezuz +-------------------------------------- RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. Note: ----------------------------------- -Sorry for posting the source this late, I never found the time to find the correct backup, write some comments and add a licensing. The injector still might become open source if I find the time some day. -You can use my previous injector in case you just want to edit the source of the library. Otherwise you can use the instructions below with your own injection stub. - -The code as presented (the last version I released in 2009) supports X86 architecture only. After Valve embedded Raw Input into their games, I did not see the urge of rewriting the projects for the X64 architecture. +-------------------------------------- +This is a fork of RInput Library v1.31 as originally authored by abort. x64 architecture support was never added by him, but here are some notes on what would need to be changed to accomplish this, in case someone is interested: +- The detouring/hooking mechanism needs to support the x64 architectures. This is mainly due to the jumping mechanism for larger pointers. The jumps need to support the whole memory address span over 32-bit +- The injector for RInput needs to support 64-bit injection and support for finding 64-bit processes +- This library obviously has to get recompiled to 64-bit (might require some changes to Win32 API calls for the RInput HWND) Building: ----------------------------------- -Compile with Visual Studio (or Visual C++ Express); -- Link the library to MS Detours (MS Detours 1.5 was used back then; http://home.comcast.net/~wiccaan/downloads/Detours.rar) -- Make sure you compile the library with a Multi-Byte Character Set +-------------------------------------- +Compiled with Visual C++ 2008 Express Edition, though you may be able to get it to work with other versions or compilers (available for free @ https://go.microsoft.com/?linkid=7729279); +- Pick Dynamic Library (.dll) as the configuration type +- Compile using Multi-Byte Character Set +- Add the Microsoft SDK (I used v6.0A that came with VC++2008) folders as additional Include directory and additional linker library directory +- Compiled with Multi-threaded DLL runtime library for linking +- Use x86 architecture for compiling (i.e., RInput does not support for x64 applications) +- MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar How it works: ----------------------------------- -As most of the developers are not into Win32 API these days, I'll just sum up how the process works: +-------------------------------------- +Summary of the process by the original author: 1. The library gets injected into a process by an injector 2. The injector creates a windows event to communicate with the injected library @@ -27,6 +31,8 @@ As most of the developers are not into Win32 API these days, I'll just sum up ho 6. The injector gives the user feedback, based on whether the event was raised or not Credits: ----------------------------------- -Dr3am - for the icons -Microsoft - for preventing me from writing my own detour/hooking stub \ No newline at end of file +-------------------------------------- +BuSheeZy - helping me understand Visual C++ IDE and Git +qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works +about - original author +Dr3am - for the icons \ No newline at end of file diff --git a/README.mdown b/README.mdown index 3d52141..43f5b0a 100644 --- a/README.mdown +++ b/README.mdown @@ -1,19 +1,23 @@ -# RInput Library v1.31 +# RInput Library v1.32 by Vols and Jezuz RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. ## Note -Sorry for posting the source this late, I never found the time to find the correct backup, write some comments and add a licensing. The injector still might become open source if I find the time some day. -You can use my previous injector in case you just want to edit the source of the library. Otherwise you can use the instructions below with your own injection stub. - -The code as presented (the last version I released in 2009) supports X86 architecture only. After Valve embedded Raw Input into their games, I did not see the urge of rewriting the projects for the X64 architecture. +This is a fork of RInput Library v1.31 as originally authored by abort. x64 architecture support was never added by him, but here are some notes on what would need to be changed to accomplish this, in case someone is interested: +- The detouring/hooking mechanism needs to support the x64 architectures. This is mainly due to the jumping mechanism for larger pointers. The jumps need to support the whole memory address span over 32-bit +- The injector for RInput needs to support 64-bit injection and support for finding 64-bit processes +- This library obviously has to get recompiled to 64-bit (might require some changes to Win32 API calls for the RInput HWND) ## Building -Compile with Visual Studio (or Visual C++ Express); -- Link the library to MS Detours (MS Detours 1.5 was used back then; http://home.comcast.net/~wiccaan/downloads/Detours.rar) -- Make sure you compile the library with a Multi-Byte Character Set +Compiled with Visual C++ 2008 Express Edition, though you may be able to get it to work with other versions or compilers (available for free @ https://go.microsoft.com/?linkid=7729279); +- Pick Dynamic Library (.dll) as the configuration type +- Compile using Multi-Byte Character Set +- Add the Microsoft SDK (I used v6.0A that came with VC++2008) folders as additional Include directory and additional linker library directory +- Compiled with Multi-threaded DLL runtime library for linking +- Use x86 architecture for compiling (i.e., RInput does not support for x64 applications) +- MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar ## How it works -As most of the developers are not into Win32 API these days, I'll just sum up how the process works: +Summary of the process by the original author: 1. The library gets injected into a process by an injector 2. The injector creates a windows event to communicate with the injected library @@ -22,13 +26,8 @@ As most of the developers are not into Win32 API these days, I'll just sum up ho 5. The library raises the event that has been created by the injector 6. The injector gives the user feedback, based on whether the event was raised or not -## x64 Support -I never added x64 architecture support, due to old games being 32-bit. Windows XP came out in 2001 and was the first Windows OS to support raw input for input devices. In 2003, 64-bit CPUs were introduced. Building applications for x64 specifically and using the old Win32 cursor API calls, would be contradictory in terms of progressiveness. There is little chance a company would do so (unless quality input data is not important at all). For those that are still interested in implementing this, here is what needs to change at least (perhaps I forgot something): - -* The detouring/hooking mechanism needs to support the x64 architectures. This is mainly due to the jumping mechanism for larger pointers. The jumps need to support the whole memory address span over 32-bit -* The injector for RInput needs to support 64-bit injection and support for finding 64-bit processes -* This library obviously has to get recompiled to 64-bit (might require some changes to Win32 API calls for the Rinput HWND) - ## Credits: -Dr3am: for the icons -Microsoft: for preventing me from writing my own detour/hooking stub \ No newline at end of file +BuSheeZy - helping me understand Visual C++ IDE and Git +qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works +about - original author +Dr3am - for the icons \ No newline at end of file diff --git a/main.cpp b/main.cpp index f0a91db..3c787e4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,15 @@ /* - RInput allows you to override low definition windows mouse input with high definition mouse input. + This version of RInput was forked from abort's v1.31 by Vols and + Jezuz (http://steamcommunity.com/profiles/76561198057348857/), with + a lot of help from BuSheeZy (http://BushGaming.com) and qsxcv + (http://www.overclock.net/u/395745/qsxcv). + + ------------------------------------------------------------------ + Comments from original author, abort (http://blog.digitalise.net/) + ------------------------------------------------------------------ + + RInput allows you to override low definition windows mouse input + with high definition mouse input. RInput Copyright (C) 2012, J. Dijkstra (abort@digitalise.net) diff --git a/rawinput.cpp b/rawinput.cpp index 8fbad74..9cf720e 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -1,5 +1,15 @@ /* - RInput allows you to override low definition windows mouse input with high definition mouse input. + This version of RInput was forked from abort's v1.31 by Vols and + Jezuz (http://steamcommunity.com/profiles/76561198057348857/), with + a lot of help from BuSheeZy (http://BushGaming.com) and qsxcv + (http://www.overclock.net/u/395745/qsxcv). + + ------------------------------------------------------------------ + Comments from original author, abort (http://blog.digitalise.net/) + ------------------------------------------------------------------ + + RInput allows you to override low definition windows mouse input + with high definition mouse input. RInput Copyright (C) 2012, J. Dijkstra (abort@digitalise.net) @@ -30,6 +40,8 @@ bool CRawInput::bRegistered = false; HWND CRawInput::hwndInput = NULL; long CRawInput::x = 0; long CRawInput::y = 0; +long CRawInput::set_x = 0; +long CRawInput::set_y = 0; bool CRawInput::initialize(WCHAR* pwszError) { @@ -79,7 +91,11 @@ bool CRawInput::initWindow(WCHAR* pwszError) bool CRawInput::initInput(WCHAR* pwszError) { // Set default coordinates - CRawInput::x = CRawInput::y = 0; + LPPOINT defCor = new tagPOINT; + GetPhysicalCursorPos(defCor); + PhysicalToLogicalPoint(hwndInput, defCor); + CRawInput::x = defCor->x; + CRawInput::y = defCor->y; RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); @@ -145,8 +161,8 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) { if (!TrmpSetCursorPos(x, y)) return 1; - CRawInput::x = (long)x; - CRawInput::y = (long)y; + CRawInput::set_x = (long)x; + CRawInput::set_y = (long)y; #ifdef _DEBUG OutputDebugString("Set coordinates"); @@ -160,6 +176,9 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) lpPoint->x = CRawInput::x; lpPoint->y = CRawInput::y; + CRawInput::x = CRawInput::set_x; + CRawInput::y = CRawInput::set_y; + #ifdef _DEBUG OutputDebugString("Returned coordinates"); #endif diff --git a/versioninfo.h b/versioninfo.h index fc888b3..4bff7d6 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.31 sequential edition" -#define RINPUTFVER 1,31 +#define RINPUTVER "v1.32" +#define RINPUTFVER 1,32 #define IDI_ICON 101 #define RINPUTINT "RInput" From b1caecd251baa9b58fd913bb1ee0a552186584cf Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 5 Oct 2015 13:32:59 -0400 Subject: [PATCH 02/57] forgot rawinput.h, added MS Detours files --- detours.h | 616 ++++++++++++++++++++++++++++++++++++++++++++++++++++ detours.lib | Bin 0 -> 178870 bytes rawinput.h | 6 +- 3 files changed, 621 insertions(+), 1 deletion(-) create mode 100644 detours.h create mode 100644 detours.lib diff --git a/detours.h b/detours.h new file mode 100644 index 0000000..347b3f4 --- /dev/null +++ b/detours.h @@ -0,0 +1,616 @@ +////////////////////////////////////////////////////////////////////////////// +// +// File: detours.h +// Module: detours.lib +// +// Detours for binary functions. Version 1.5 (Build 46) +// +// Copyright 1995-2001, Microsoft Corporation +// + +#pragma once +#ifndef _DETOURS_H_ +#define _DETOURS_H_ + +#pragma comment(lib, "detours.lib") + +////////////////////////////////////////////////////////////////////////////// +// +#ifndef GUID_DEFINED +#define GUID_DEFINED +typedef struct _GUID +{ + DWORD Data1; + WORD Data2; + WORD Data3; + BYTE Data4[ 8 ]; +} GUID; +#endif // !GUID_DEFINED + +#if defined(__cplusplus) +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID & +#endif // !_REFGUID_DEFINED +#else // !__cplusplus +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID * const +#endif // !_REFGUID_DEFINED +#endif // !__cplusplus +// +////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/////////////////////////////////////////////////// Instruction Target Macros. +// +#define DETOUR_INSTRUCTION_TARGET_NONE ((PBYTE)0) +#define DETOUR_INSTRUCTION_TARGET_DYNAMIC ((PBYTE)~0ul) + +/////////////////////////////////////////////////////////// Trampoline Macros. +// +// DETOUR_TRAMPOLINE(trampoline_prototype, target_name) +// +// The naked trampoline must be at least DETOUR_TRAMPOLINE_SIZE bytes. +// +#define DETOUR_TRAMPOLINE_SIZE 32 +#define DETOUR_SECTION_HEADER_SIGNATURE 0x00727444 // "Dtr\0" + +#define DETOUR_TRAMPOLINE(trampoline,target) \ +static PVOID __fastcall _Detours_GetVA_##target(VOID) \ +{ \ + return ⌖ \ +} \ +\ +__declspec(naked) trampoline \ +{ \ + __asm { nop };\ + __asm { nop };\ + __asm { call _Detours_GetVA_##target };\ + __asm { jmp eax };\ + __asm { ret };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ +} + +#define DETOUR_TRAMPOLINE_EMPTY(trampoline) \ +__declspec(naked) trampoline \ +{ \ + __asm { nop };\ + __asm { nop };\ + __asm { xor eax, eax };\ + __asm { mov eax, [eax] };\ + __asm { ret };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ + __asm { nop };\ +} + +/////////////////////////////////////////////////////////// Binary Structures. +// +#pragma pack(push, 8) +typedef struct _DETOUR_SECTION_HEADER +{ + DWORD cbHeaderSize; + DWORD nSignature; + DWORD nDataOffset; + DWORD cbDataSize; + + DWORD nOriginalImportVirtualAddress; + DWORD nOriginalImportSize; + DWORD nOriginalBoundImportVirtualAddress; + DWORD nOriginalBoundImportSize; + + DWORD nOriginalIatVirtualAddress; + DWORD nOriginalIatSize; + DWORD nOriginalSizeOfImage; + DWORD nReserve; +} DETOUR_SECTION_HEADER, *PDETOUR_SECTION_HEADER; + +typedef struct _DETOUR_SECTION_RECORD +{ + DWORD cbBytes; + DWORD nReserved; + GUID guid; +} DETOUR_SECTION_RECORD, *PDETOUR_SECTION_RECORD; +#pragma pack(pop) + +#define DETOUR_SECTION_HEADER_DECLARE(cbSectionSize) \ +{ \ + sizeof(DETOUR_SECTION_HEADER),\ + DETOUR_SECTION_HEADER_SIGNATURE,\ + sizeof(DETOUR_SECTION_HEADER),\ + (cbSectionSize),\ + \ + 0,\ + 0,\ + 0,\ + 0,\ + \ + 0,\ + 0,\ + 0,\ + 0,\ +} + +///////////////////////////////////////////////////////////// Binary Typedefs. +// +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_BYWAY_CALLBACK)(PVOID pContext, + PCHAR pszFile, + PCHAR *ppszOutFile); +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_FILE_CALLBACK)(PVOID pContext, + PCHAR pszOrigFile, + PCHAR pszFile, + PCHAR *ppszOutFile); +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_SYMBOL_CALLBACK)(PVOID pContext, + DWORD nOrdinal, + PCHAR pszOrigSymbol, + PCHAR pszSymbol, + PCHAR *ppszOutSymbol); +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_FINAL_CALLBACK)(PVOID pContext); +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_EXPORT_CALLBACK)(PVOID pContext, + DWORD nOrdinal, + PCHAR pszName, + PBYTE pbCode); + +typedef VOID * PDETOUR_BINARY; +typedef VOID * PDETOUR_LOADED_BINARY; + +//////////////////////////////////////////////////////// Trampoline Functions. +// +PBYTE WINAPI DetourFunction(PBYTE pbTargetFunction, + PBYTE pbDetourFunction); + +BOOL WINAPI DetourFunctionWithEmptyTrampoline(PBYTE pbTrampoline, + PBYTE pbTarget, + PBYTE pbDetour); + +BOOL WINAPI DetourFunctionWithEmptyTrampolineEx(PBYTE pbTrampoline, + PBYTE pbTarget, + PBYTE pbDetour, + PBYTE *ppbRealTrampoline, + PBYTE *ppbRealTarget, + PBYTE *ppbRealDetour); + +BOOL WINAPI DetourFunctionWithTrampoline(PBYTE pbTrampoline, + PBYTE pbDetour); + +BOOL WINAPI DetourFunctionWithTrampolineEx(PBYTE pbTrampoline, + PBYTE pbDetour, + PBYTE *ppbRealTrampoline, + PBYTE *ppbRealTarget); + +BOOL WINAPI DetourRemove(PBYTE pbTrampoline, PBYTE pbDetour); + +////////////////////////////////////////////////////////////// Code Functions. +// +PBYTE WINAPI DetourFindFunction(PCHAR pszModule, PCHAR pszFunction); +PBYTE WINAPI DetourGetFinalCode(PBYTE pbCode, BOOL fSkipJmp); + +PBYTE WINAPI DetourCopyInstruction(PBYTE pbDst, PBYTE pbSrc, PBYTE *ppbTarget); +PBYTE WINAPI DetourCopyInstructionEx(PBYTE pbDst, + PBYTE pbSrc, + PBYTE *ppbTarget, + LONG *plExtra); + +///////////////////////////////////////////////////// Loaded Binary Functions. +// +HMODULE WINAPI DetourEnumerateModules(HMODULE hModuleLast); +PBYTE WINAPI DetourGetEntryPoint(HMODULE hModule); +BOOL WINAPI DetourEnumerateExports(HMODULE hModule, + PVOID pContext, + PF_DETOUR_BINARY_EXPORT_CALLBACK pfExport); + +PBYTE WINAPI DetourFindPayload(HMODULE hModule, REFGUID rguid, DWORD *pcbData); +DWORD WINAPI DetourGetSizeOfPayloads(HMODULE hModule); + +///////////////////////////////////////////////// Persistent Binary Functions. +// +BOOL WINAPI DetourBinaryBindA(PCHAR pszFile, PCHAR pszDll, PCHAR pszPath); +BOOL WINAPI DetourBinaryBindW(PWCHAR pwzFile, PWCHAR pwzDll, PWCHAR pwzPath); +#ifdef UNICODE +#define DetourBinaryBind DetourBinaryBindW +#else +#define DetourBinaryBind DetourBinaryBindA +#endif // !UNICODE + +PDETOUR_BINARY WINAPI DetourBinaryOpen(HANDLE hFile); +PBYTE WINAPI DetourBinaryEnumeratePayloads(PDETOUR_BINARY pBinary, + GUID *pGuid, + DWORD *pcbData, + DWORD *pnIterator); +PBYTE WINAPI DetourBinaryFindPayload(PDETOUR_BINARY pBinary, + REFGUID rguid, + DWORD *pcbData); +PBYTE WINAPI DetourBinarySetPayload(PDETOUR_BINARY pBinary, + REFGUID rguid, + PBYTE pbData, + DWORD cbData); +BOOL WINAPI DetourBinaryDeletePayload(PDETOUR_BINARY pBinary, REFGUID rguid); +BOOL WINAPI DetourBinaryPurgePayloads(PDETOUR_BINARY pBinary); +BOOL WINAPI DetourBinaryResetImports(PDETOUR_BINARY pBinary); +BOOL WINAPI DetourBinaryEditImports(PDETOUR_BINARY pBinary, + PVOID pContext, + PF_DETOUR_BINARY_BYWAY_CALLBACK pfByway, + PF_DETOUR_BINARY_FILE_CALLBACK pfFile, + PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbol, + PF_DETOUR_BINARY_FINAL_CALLBACK pfFinal); +BOOL WINAPI DetourBinaryWrite(PDETOUR_BINARY pBinary, HANDLE hFile); +BOOL WINAPI DetourBinaryClose(PDETOUR_BINARY pBinary); + +/////////////////////////////////////////////// First Chance Exception Filter. +// +LPTOP_LEVEL_EXCEPTION_FILTER WINAPI +DetourFirstChanceExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelFilter); + +///////////////////////////////////////////////// Create Process & Inject Dll. +// +typedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEA) + (LPCSTR lpApplicationName, + LPSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCSTR lpCurrentDirectory, + LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + +typedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEW) + (LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); + +BOOL WINAPI DetourCreateProcessWithDllA(LPCSTR lpApplicationName, + LPSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCSTR lpCurrentDirectory, + LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation, + LPCSTR lpDllName, + PDETOUR_CREATE_PROCESS_ROUTINEA + pfCreateProcessA); + +BOOL WINAPI DetourCreateProcessWithDllW(LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation, + LPCWSTR lpDllName, + PDETOUR_CREATE_PROCESS_ROUTINEW + pfCreateProcessW); + +#ifdef UNICODE +#define DetourCreateProcessWithDll DetourCreateProcessWithDllW +#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEW +#else +#define DetourCreateProcessWithDll DetourCreateProcessWithDllA +#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEA +#endif // !UNICODE + +BOOL WINAPI DetourContinueProcessWithDllA(HANDLE hProcess, LPCSTR lpDllName); +BOOL WINAPI DetourContinueProcessWithDllW(HANDLE hProcess, LPCWSTR lpDllName); + +#ifdef UNICODE +#define DetourContinueProcessWithDll DetourContinueProcessWithDllW +#else +#define DetourContinueProcessWithDll DetourContinueProcessWithDllA +#endif // !UNICODE +// +////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +} +#endif // __cplusplus + +/////////////////////////////////////////////////////////////////// Old Names. +// +#define ContinueProcessWithDll DetourContinueProcessWithDll +#define ContinueProcessWithDllA DetourContinueProcessWithDllA +#define ContinueProcessWithDllW DetourContinueProcessWithDllW +#define CreateProcessWithDll DetourCreateProcessWithDll +#define CreateProcessWithDllA DetourCreateProcessWithDllA +#define CreateProcessWithDllW DetourCreateProcessWithDllW +#define DETOUR_TRAMPOLINE_WO_TARGET DETOUR_TRAMPOLINE_EMPTY +#define DetourBinaryPurgePayload DetourBinaryPurgePayloads +#define DetourEnumerateExportsForInstance DetourEnumerateExports +#define DetourEnumerateInstances DetourEnumerateModules +#define DetourFindEntryPointForInstance DetourGetEntryPoint +#define DetourFindFinalCode DetourGetFinalCode +#define DetourFindPayloadInBinary DetourFindPayload +#define DetourGetSizeOfBinary DetourGetSizeOfPayloads +#define DetourRemoveWithTrampoline DetourRemove +#define PCREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINE +#define PCREATE_PROCESS_ROUTINEA PDETOUR_CREATE_PROCESS_ROUTINEA +#define PCREATE_PROCESS_ROUTINEW PDETOUR_CREATE_PROCESS_ROUTINEW +// + +//////////////////////////////////////////////// Detours Internal Definitions. +// +#ifdef __cplusplus +#ifdef DETOURS_INTERNAL + +////////////////////////////////////////////////////////////////////////////// +// +#ifdef IMAGEAPI // defined by IMAGEHLP.H +typedef LPAPI_VERSION (NTAPI *PF_ImagehlpApiVersionEx)(LPAPI_VERSION AppVersion); + +typedef BOOL (NTAPI *PF_SymInitialize)(IN HANDLE hProcess, + IN LPSTR UserSearchPath, + IN BOOL fInvadeProcess); +typedef DWORD (NTAPI *PF_SymSetOptions)(IN DWORD SymOptions); +typedef DWORD (NTAPI *PF_SymGetOptions)(VOID); +typedef BOOL (NTAPI *PF_SymLoadModule)(IN HANDLE hProcess, + IN HANDLE hFile, + IN PSTR ImageName, + IN PSTR ModuleName, + IN DWORD BaseOfDll, + IN DWORD SizeOfDll); +typedef BOOL (NTAPI *PF_SymGetModuleInfo)(IN HANDLE hProcess, + IN DWORD dwAddr, + OUT PIMAGEHLP_MODULE ModuleInfo); +typedef BOOL (NTAPI *PF_SymGetSymFromName)(IN HANDLE hProcess, + IN LPSTR Name, + OUT PIMAGEHLP_SYMBOL Symbol); +typedef BOOL (NTAPI *PF_BindImage)(IN LPSTR pszImageName, + IN LPSTR pszDllPath, + IN LPSTR pszSymbolPath); + +typedef struct _DETOUR_SYM_INFO +{ + HANDLE hProcess; + HMODULE hImageHlp; + PF_ImagehlpApiVersionEx pfImagehlpApiVersionEx; + PF_SymInitialize pfSymInitialize; + PF_SymSetOptions pfSymSetOptions; + PF_SymGetOptions pfSymGetOptions; + PF_SymLoadModule pfSymLoadModule; + PF_SymGetModuleInfo pfSymGetModuleInfo; + PF_SymGetSymFromName pfSymGetSymFromName; + PF_BindImage pfBindImage; +} DETOUR_SYM_INFO, *PDETOUR_SYM_INFO; + +PDETOUR_SYM_INFO DetourLoadImageHlp(VOID); + +#endif // IMAGEAPI + +////////////////////////////////////////////////////////////////////////////// +// +class CDetourEnableWriteOnCodePage +{ +public: + CDetourEnableWriteOnCodePage(PBYTE pbCode, LONG cbCode = DETOUR_TRAMPOLINE_SIZE) + { + m_pbCode = pbCode; + m_cbCode = cbCode; + m_dwOldPerm = 0; + m_hProcess = GetCurrentProcess(); + + if (m_pbCode && m_cbCode) { + if (!FlushInstructionCache(m_hProcess, pbCode, cbCode)) { + return; + } + if (!VirtualProtect(pbCode, + cbCode, + PAGE_EXECUTE_READWRITE, + &m_dwOldPerm)) { + return; + } + } + } + + ~CDetourEnableWriteOnCodePage() + { + if (m_dwOldPerm && m_pbCode && m_cbCode) { + DWORD dwTemp = 0; + if (!FlushInstructionCache(m_hProcess, m_pbCode, m_cbCode)) { + return; + } + if (!VirtualProtect(m_pbCode, m_cbCode, m_dwOldPerm, &dwTemp)) { + return; + } + } + } + + BOOL SetPermission(DWORD dwPerms) + { + if (m_dwOldPerm && m_pbCode && m_cbCode) { + m_dwOldPerm = dwPerms; + return TRUE; + } + return FALSE; + } + + BOOL IsValid(VOID) + { + return m_pbCode && m_cbCode && m_dwOldPerm; + } + +private: + HANDLE m_hProcess; + PBYTE m_pbCode; + LONG m_cbCode; + DWORD m_dwOldPerm; +}; + +////////////////////////////////////////////////////////////////////////////// +// +inline PBYTE DetourGenMovEax(PBYTE pbCode, UINT32 nValue) +{ + *pbCode++ = 0xB8; + *((UINT32*&)pbCode)++ = nValue; + return pbCode; +} + +inline PBYTE DetourGenMovEbx(PBYTE pbCode, UINT32 nValue) +{ + *pbCode++ = 0xBB; + *((UINT32*&)pbCode)++ = nValue; + return pbCode; +} + +inline PBYTE DetourGenMovEcx(PBYTE pbCode, UINT32 nValue) +{ + *pbCode++ = 0xB9; + *((UINT32*&)pbCode)++ = nValue; + return pbCode; +} + +inline PBYTE DetourGenMovEdx(PBYTE pbCode, UINT32 nValue) +{ + *pbCode++ = 0xBA; + *((UINT32*&)pbCode)++ = nValue; + return pbCode; +} + +inline PBYTE DetourGenMovEsi(PBYTE pbCode, UINT32 nValue) +{ + *pbCode++ = 0xBE; + *((UINT32*&)pbCode)++ = nValue; + return pbCode; +} + +inline PBYTE DetourGenMovEdi(PBYTE pbCode, UINT32 nValue) +{ + *pbCode++ = 0xBF; + *((UINT32*&)pbCode)++ = nValue; + return pbCode; +} + +inline PBYTE DetourGenMovEbp(PBYTE pbCode, UINT32 nValue) +{ + *pbCode++ = 0xBD; + *((UINT32*&)pbCode)++ = nValue; + return pbCode; +} + +inline PBYTE DetourGenMovEsp(PBYTE pbCode, UINT32 nValue) +{ + *pbCode++ = 0xBC; + *((UINT32*&)pbCode)++ = nValue; + return pbCode; +} + +inline PBYTE DetourGenPush(PBYTE pbCode, UINT32 nValue) +{ + *pbCode++ = 0x68; + *((UINT32*&)pbCode)++ = nValue; + return pbCode; +} + +inline PBYTE DetourGenPushad(PBYTE pbCode) +{ + *pbCode++ = 0x60; + return pbCode; +} + +inline PBYTE DetourGenPopad(PBYTE pbCode) +{ + *pbCode++ = 0x61; + return pbCode; +} + +inline PBYTE DetourGenJmp(PBYTE pbCode, PBYTE pbJmpDst, PBYTE pbJmpSrc = 0) +{ + if (pbJmpSrc == 0) { + pbJmpSrc = pbCode; + } + *pbCode++ = 0xE9; + *((INT32*&)pbCode)++ = pbJmpDst - (pbJmpSrc + 5); + return pbCode; +} + +inline PBYTE DetourGenCall(PBYTE pbCode, PBYTE pbJmpDst, PBYTE pbJmpSrc = 0) +{ + if (pbJmpSrc == 0) { + pbJmpSrc = pbCode; + } + *pbCode++ = 0xE8; + *((INT32*&)pbCode)++ = pbJmpDst - (pbJmpSrc + 5); + return pbCode; +} + +inline PBYTE DetourGenBreak(PBYTE pbCode) +{ + *pbCode++ = 0xcc; + return pbCode; +} + +inline PBYTE DetourGenRet(PBYTE pbCode) +{ + *pbCode++ = 0xc3; + return pbCode; +} + +inline PBYTE DetourGenNop(PBYTE pbCode) +{ + *pbCode++ = 0x90; + return pbCode; +} +#endif DETOURS_INTERAL +#endif // __cplusplus + +#endif // _DETOURS_H_ +// +//////////////////////////////////////////////////////////////// End of File. diff --git a/detours.lib b/detours.lib new file mode 100644 index 0000000000000000000000000000000000000000..56c6a8df6139dd9cfa43acc13b3bff2063dd53ec GIT binary patch literal 178870 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%0AH>Ybkf9sm5dZHD8ecyCxRsAx_a%+EJQ44^ z@p@2mZspqpxIV<^hBUrR<@++C&j8F1@Mfd`%*{}~KLD;5@tc~)##aJ5ZUW5iX3Wgv z^CVFFldj#? z1vtKr_!U%`crxK5|E>W{!oX$Ww;R6u5Pm0MYTgYGo=o@_B5osKK8p9#cr)pj7XbGJ z;)f!{mKom#h`R?cFXR0g-c0zYH;y6xiyPp*!Y zzHaatG$>@#bMh)2)7g3b-#JKDG%SJh^z?@Uh?eI$#2up*!$q!UtQ zc<^K@UpeAF4wy&rK8-h%ez^;9KSVq(ERbg`e76JUA-qrE&4lkBV0j7geOt!H_gO@L z$iP%wOIUY$^F2!SBYsD>#+Rx8_%NcsZD1O<5*9--7yWk>F*hN8dk1|+;?2%rpR&I^{W{2Kzo5(5pHolX0+^`- z&{ueKGVt#yz`dU^QH?KC`A#c<4hPKk_hGEC6W=Pq{ZwxJdjSZZiz)Sy4{Cff@yAV` z=OFynn>EaX^4GmTCmr8L{FQMHo2h(gLaGxmFX4R~Zzg)b3%HAKfjvURcrvv&E(I+C z%szAw2k_=#^~8p{)A;oA$i<%oX;FP}{G zdXeuDz#O|B+x0#|I;OkgdmcC*LHrFL6?7TteIiS#uK;Gt$MBsiyk6X;i0*0 zyBqO?-_&_$!nXv`A2l!|^4DEn9st~1i2vl{W8>R_=*JAq!Y2spK0f*uVzwgwTzc?i zD&K0vtpv8$tZXzfEkJ z@v*wT4w&wJO6|nUdoJAY{TgsTL;Sa%h6hh3dY2;ZX9h+9;ke=3hwzg9u$%Dm$%OB0 z#QmFr5kNR@_?YF?XK;BKUOt)dK?SL!fY}8D>S4UO8S3{Ffct+DfAc>QTV{L<5cfI2 z{K&v%;kR4)?nd|lz{I};51vfryAW~r1Lh}q-^9zM5jT8a0Nf-fg~zBE@MOYwG2(s# zn2FEfIt9F$@cjyKe#DS>1MAdR5I^t&v1P``^4$)Y=kUIU zHxs@e6A3z{ALHedseI($?*KFRMU1=fX2SP1z+Hv-qL+v*GrswVI}b2nyj^%R;kyfP z!-&7&d+^}NRK6<^w+b+EyiefG%}~F00`8ZHf8Y0sEi*p$D|Z-}hvcvO_>JxSdBpFe zV!)HBd}WCHjDh)@{B_5NqNx`Uzn6*&&sg}rVPIa6zwY=B0mrWq|I#ahE+f7bh&}|E z^M0t*6?ik%#~B`sKM+5}js#Dp@@+)iJ%GuDfSrLiJA=LZAmG*`KJO>QmKopmh?@tP zPvLzDZzlahburgE9EH?$41n-_q`(TZ)iJ$pu2~=ZqQGV%X z5Ll$}iLx3jqVwZvp+|RL;|>a%KY~7cNIVFR%SvbFr+5X9P+hP*I}zkerZ&S6>h4(szhOV>PRpU^tY@J`R$l&Wn!?A(cj<; z`Z4L4U&=H&Rtz=?uEf{)SOkXW+Tp(HRlb!v(Ks79ry}hn5z+>H8V!tE!8N+aF`6F>wctrS2Ib~#F zT4ZgLZ>6Lr=e`BDW~+68{i`Uv_$*q`AFRT@({iL{w1*!n zv!ZK&rUk4Nh*<^#-(^ffH2Ygv*Ui{Hqtj=n#acw@ttq6Cx#T#ZQ1kvKUp=;C>BwAX zq^V(i*3T|to-x*fI_E+sP%{t;V^@d$%`NCXG|WUNjHM=gYfwYJDt!L%JOeh#2_{)tiX#bo zmVqsH!U}hq*EgVG5F2To6h2m&rJ|)uQMyzpi_+3j*uo*Z*5(*g*e{0qWFG+$0xAcH zg{RC30^QcqSlw7(UGHmdhDKs*VUeaffuJfH$ujH>)Unf?vG~+!pf=#I8O<}R;-@>o zbx!=qWPN>oW3}dlmF)~CWR2fsYw>fYGnS;IOw3h(p|S~^A$9Rial*;&@P-z4TaC>D z@*VmVA|f3mNu8AfT@Nl)`x^9DFk1p_z?OuZ<^&9c8&)---6U(>YM`^7V7mFSPV5GH zx--7Xx7OmDEv@BF094Sr_4rPxX~fibEc6^FjIX-dk6qo=f6aC_bDa>{CP24<-o+wk zo*O_IpO?g^Orex$KQO1W+`u4T_)4o}YXhF05wOWGhK40Q=Vk;9HP#2J*GK$KO^r>M z9@PanFC(}gy{G}EhCW!rejwy)YW7D^g^dk?YV@*KaWlS-%rcR+O#w8G`t{OxGF7&T zu)nJdBg2l&F_BG;jV)HUV57`6k#+>TbfU~R5z==2(xo!kYr#{t{nFeRJi!FRvZ{;J zHZ=w#ECE{uV+%|y!gM6Tu;W$77MfVfk)2PG2^1rX-|*G26RlvV$e_G=vu-$ek_bg?pg~a-%u1RR&n{49!&DA~_U^G2`2>C*dHHU3ej`Dp6 ztbqar?cB6J65v;LMOXN1^w*1N>=lc>&?2^g92u$gp@(EOGX{p6#MqW9vtmS^iGWo2 znx$XW_4U}cn_tRs7##>_Gr-4x zQ9LL?G)x@_a|3Y={F3FVPH2`hkU5vdNEe* zU=v1JU>HaTsLukWC^H!3R%3tz1q?3J++-l=td7)%YJJTu5^e#57yz$lUiBhP1V*8O z7$1X9DVf)Vno`KugaI4~!hDZ3v_$l`rJFJG;FpYFgizqyyHF&)AVmWNdP`qU3-qr8 zE4-!O)4^2mJXgb0J1-yzU+J|{1CXbPSU8px)wo8EV=<+bi~tl+n!Xf7NpUp-YQqg8 znJ^J_xoor&lKR1BeyMj25~1w;4m*bpg6LU<(XbX;_|fX;5TI$7!Gi9i8u6`eQdEb6 zVw@T9HQ;OCD~0xLfTtArnwos;s~W>7AU{V;G!a)kED3W(N$iYCBb%Gm*raGTAb>GNHZG+_F;m%8@e4FKlEvzOB=dlafp7 znK~lbt|0<7Xo;xp0tE5sQ~>4>5sdrQcGk^rkN^d#_Q`;mXWO0z)=;1(*c^}{JIpck zR$)H|G~Uv00U8RU1(v-vRCAI#2O0TXBXP zZf$?>R{8uEX*y<=r*EC_FD*{Ha{ z*1q2UsARzQP&o=f(iM*i*6DE2rvp8zw=^&cprv!L=h^`|7?(3DQoe-B!*pLn>C@Y* zP!V&oL7(b3A0hRY{)02Fv5y}F8xUU11&8Y}43;4(&&s_U+^zP7tE9%+6xoQZ*2vS7 z>xs&IwVqNzQ3i}H&4jr_OCv@Fx+!rAgVMRaS)_3qKseGx5Qn-LpXwnpld{TjR1?F( zCTWF~V!&B~Job4_HVrvcA?oI0(!mJa%5T3&j{-St(9q@s0&$T_h*Y`*Oek(l$ zhx}2k-j5KFBuYfk@(b!dK-DycIl`jYGFMHuK(OL>lJQc`y7~~mEFT6e#kUCM*AbDH zI#}ZoDl@3?35eSZJg5Y!V@{F*r5+Y>mQ2W-j!<+Fz*{dZ@)u;7=0;;k1_||6izGK# zlU6mdP(vw&YF;0#YOKc;hqv^%h=3f`3k%jE74<7=510k7ap*Z9rtlEd4eyI-Xfa}|!f+JCpmfr(E>lxFZR(lJHf}ljQ z)3SOj;-8YtQ#yWX4EEbVU^7s6-OQO+lfy`&wjr{kIwz4+Hp8e+Xr@}l253T1q#hQ5 zO!N9zV(#sPPH9t+bD1`rnwaJ5LwKOMInTAaRyH7sFlwMc6> zTxz#92(xV}3J=+lN)T0C140A-p24k7gM`%zTBNICdt!stnkJ>aGMg1l&-TjCK9fwU zDU=183gd7z4=RjW%W{B9^I?cowSKK{JqEn>^;MXVmR3&HY-?spo-KCFg`&dJh@7yx zY4RZ#6jB-oGX{ua14dI6dBM+ca~%f&l>F{+(f`zCgl?@9d8ACGi4-f@gWv$p4fgi8ixqGt z!p?N#ny50HY0TkYkV7?wE*zIrI$ir0p|YIZkj6vp!>+@%=RjXX?oz8(`)rj;#U}oD{n1(fttMoX6bkRn( zge3AM38<5k4e@IbdBVQxs6Ysw%E?mTzVX~4Ww zitNz3l1e~zvZUAHw^pyLZgylzy~Fr&Hc+99@rRJFFj@LVa*jPSL_HwN@4NUBXvX(w zVWA6l{8qX#5|&L>j5@Z=_W_k|nf$)DHiyO5ptWaJTAHnKHqxErMyT%Vl9aOKmB_6Y zbFVE@G>Qipp=E>l40AYm_e|hDaqv znU!WlkCI15eH=leAEj**tq;YK4pg)hxayG8ImiWV1s957($~*5Qok=K@(?B-)7?;; z<1m(rq?iJ1C=v1`MPjSg%Up~oAQ~c+K8IDoQs)VscIUy--acYaYjI{#l+B4F8&fmM zsR$VkEz%q?#!N@MDKQl%Ey-$MornVmtC5vqsA|&Ez+Whs5n~7>bv_3KwhCeZ+mdkC zYEE23mjJu3pgwI%#U?p$Rv3`lqUJ(_Xo}ka>9ho?dK_~JQh#S7WuT^OpszLB)+t=% z(jahQs;~jy<6ze(BvGcdCT6{|myn{>OYcK-PJTLhPhp@{BjfKeE=D1XSVw}ut|qdS zrZIGv^hFz!t;HxStLmAaOMy&puP2JUl7WzL%fMq};gB1>Usb(Ozm~kTwLS~XPEC3= z{$MpqEmCHl!VlZFG+Nro3>nMeJ@ew&o@x6~cIlAV26r&6Obp;!IBV3)o@yPaN|9%W z1||n+aMIc&Mw*0T4^j&5z>hk;BvS2R-i>h6-W~lKCoO22XvU>kCT#Jv8ze_9u&kcnT0s~HvH+YNGE+l1 zOC0ST>{p}yq6QZz-g*-c9>=vbdhZ@?l^F3ss!#Nci7~U816k6Y!SC!^d!G2QKf3;Z~1m@fHsl zc!1@J`(HnlKjMF#en5_R#1noa_8zZ_t^kfiotJmbgpb6-M`Je}W#a5aP~RRC_rJ!( zuSpvJ8zYsv4uQs}>yUWLRrGYhQyvcRRLzR}4oh&-|kXU`ZX-f$dmA+q#Uy%Hj zXXyE8F%(to%^d5abCBTUu>(1W=2(qs9A>iPr_RX})@{lnTbJXv7TutU)we0>FgI5B z0q$g3%`~izEqEu7&1z19FJHc48}8KY9f#E==~{`cQL~yp&1(7_tcC&?83D^SQQ3&< z*UEy*-RcCW$S+1pU2Llb&&2O+VONgm0DYNMx-2dMlv55ZEDXz}s+oois=!-%A`Egi zMOinnRP`vPkPOWf`ZQDMGpZUlc^ysokH)qaQUAfex(HW#*|HaVHV1*WK0PXW8T$07 z=w;l=bj%@*M=b_|d*#SN3sIfzqwrZek;0gT{SD;kLb$miig~IL{(QMyYl3hwox9f{ zUg`^ft!F$<$25dlkN1SQo1*D#_8x_%8^vDIq}fZKW-onCnL`7P<1_EzYEirj{DfuU z5VZYX=?=62c%Vqgm>F=hv1^5*M8QdC1G^!H3m*LCldkY;(i9%k5avw0e<1(mNwWvI zC}ddzvOr`B*4s!SHUPi;&TPw(f8R!du|5XR^0w(9%Tl0P(fpxL^M^hoe(2nfC4$Ee zAOsf%9*b@Fs=)~eund7?vE?dy8e$TF#NC3&9pZLugsGq~kJ)zIG0TpF1WrWUd-0ax zDppGt*TH8=AbaI(O!JeGl+-*d<+H5Z>dxb3EMpT~UFIU!YSX`T1_g2$lz)InMu zxUkV?DsEp(+G&GDTPD3T6%e@p?@$MD#Fo(}3m-{T<>2F{M-rv@Agbx3V$Y%jKN8#F zRolu4fs5LXLUAN}^zf09ETY2aPtgF_9FFiZ!75z(r^_Y<(icW1YmPOt8E=_fy|)Rz zJo()rVK>3mI_3fRbX~W>ms-QK@H+=HPUI48Ft73nL;!AqG`)!`4F4q61GiYN1Dww3)5{Vf;jC|~&dF|F=k)11r>{WmgR8@yHNFD%rXBWgc9`6I>g=WXY4a$I z^OA%&_Vf%GK+CSo88|m~1MH^>=-@|?64xc%fZ=P&mJ;51>a~<$5!C24NRY@%1WU3# zagK%ohEeD7*nWQgJW&9>2pOx(NnGiT*Lf37Me$IPpq>b+Lxu;DVG+NXj!yIVeA*P* z_)&snK(=oN)n^fA5UeKZhcAw|OnnfpQ|@xa^vV-6HeYVkogjSG^AXtL2xxuZZK=#3 zOqOA4nYtO_Q)QSsUEL4=Oi_sLs_SAq>^*e~jHZaxJikqq3QB~9<(Nxl*rrVs-jYgHEnz(rwN{Kd1eAAWrppzRlZ{%Q=+p8< zpG#khD^6!1#$1kj2(oc3o05mr6n6# z$hm)WCfT5Jp%Ae9Y1>$}Bib>LqK9roII9;AK`DI%?P=V!iC;W)s;nl2UqnC+O68U= zT27V4O3@-lpB6Ft3e*qb>ad?0UxE6w9hL=yPQyWEZGk`(L1b)sDf+{!r|zq~=9x)= zzZY*A?kC`rjn^YaFi#|o9%sxV5FTe0F%4m@E_-2G6)_EAXj<=Q{mSDB4EV3Z=WS#f z-ZEG0N?0h0pH0jTT4ZZ+7FDx^KFt#PoIQ8&@BxINxgFJij`7d1|ClfV9l6A!FXkK~ z4BPkFhHX1q9$FEm7tun|hNOial}3qn3X|Z-eCX@LiPbstUxqCp6PUOM`#2CES&g)o z?Oa4Fp)O}WFDgD9zY@bdou}3H@6T~4=J_ZbE2~VDuC{B3@cUs2;~`|-Rv&`Tt%aVB z&^)yWZoZ6eysA>*YTyho~jFFpP; zVrW3Hx~(U@g{E7xOhXuAeP`?zTEm$%t-nXv4_+jB6Ub&QVDxDLqt8XHYgKH9Q^69eZ_lAg2#b%y`=i4x z-Wg3050SZ{eS`Un_Z{RsuBdxJS#~5;5Bh_VD&aA$fZ#DG`b0^*G!e>)ug*z? zyz$lEL}gLjhcOo7W?&>NT7GFFT!JAO8@fGrT7kS$)hrvrFc$!JZ0Y|zAwVngS79$ z7niW#+P-Y)DlOn-i&Ma}u%J$HJ0EVIs)d^`;%s$0Y$`>n5AH+G=w46trh}h5BpXRwzm7ayEgZrg0!he;#+NcSTIcweVQKngdVh**a4W|(AeH= zxWS@7{NRkIuX%&($y(G%Uvms?{^3Okw3ZAN|Azt!5mBgNX%tB{hHCGXRKwNG#JavEABShJ8m%|iML zq;J$=?1^=lbM=_q1Y50HT&^CAo&bMGcHYJ+#zTNz)^|x^sgK*}E3&4;U3maWOlc?i+pN6A-Lz&}Ce3kuXa_={wYV3J(FUMxDd#8sYS zfR$3^+#;n2w?)>a5SVB#?n6^1zNsjOl{i+oW|1fUTvV z;DOjTMew7GkSZaKKt3UJ*fyPr&CxgxBpZiiJ3q4+Bwm2GOznbOgbub$rU*Fb$x|;O z%p*Qp{bvXqWy3Norox8g4qKuD>27Vc#A z1GrPv&*4r}{{nZq$leSo$xJm9d7moZc$y{Bl`eI25nql}&SwICP!g@ib!l4TVj9Bm z%TcF}(YU&M2ebiwz^!eO3tHyr(=tb&OWPWE9iHmsudxolCXG^d0Bm3ET(UAzs%O7X zXa#=M0tv-MGl4$M1p1Opz~mX4$Y2g8=pe`?7}gcRuI8M}@kAi!01Bq`)Y{qbeGqS% z;>Fdv+}1XqGW^bwH6-_S;e`^PYEqJJrxc{mb}$WLI9{BUi8sralRKOa8?Ah-*^a+x zCd@K6NULTbeVT#vxiWAHzv9F-CG>_E58KT1O2hmp2q{yuGBA(qb!Q$MKN-cY-+IPl z9@7wpSWg?9dG=IMN)Kf*FF6dQ^&$2c%3@6;T3jMgnoIO)F45=8rSZj#<~P2CmVr|g z2zO4Mh?u2!aZWLtJvOJTk;^!YIE9SHoj3vyJ+rpKD!JIW5 zU6g@k>=)cwb}nKlTzsu3UAWR`Ihckp#CrPJEOX3qjKejHA0$EZgFej<`bP1?tW*`I zDynB@qfpo3EmN0|V#aiY^S0Pb%s5Np;W(ajW~49DF%4meb&Sr6YASV+Zj>>|@@mTI z)0EREl*bOsVm2ntZ>ITtY0z67`w6n#g=G}f-2jXs@rd>@(=TyMs3b?Pn5N-&1DQZM zt_F*f3)}}k0z{bfh&`^Y6+b6yBpb-u5pB1m&9j(L&itgUbUUn>Baqhl8V_b$xF&c! z`$m@D8c=iH9MT-Wo%u3TY!(rC%lN^>;bN5S-QYr*Y);sWUuCkHMUUL#_^sz!?uL&7 zSthd|3@=l4NM{*B9Lr_NJV&;4DLE;dZo-K5ifJ@KwZm`Cv)l%Mu}tTcsIS96RdFU~ znwTy#)bHV+DNQHc>^Y7YJ%)tGJQMJj&Fn4nJN-L|OhXuAU6{F9aT&aEFxod5ts304 z32THLD07r*;9Lk%qe-ww5PnviZToX^9CZ0Y=|HH{OnbQ9N zief%w$>m)vz;jI>>b&^T)w7p{j}L4@;I;eCp@QG-p(!YfOjc0%X!dSTxrCx+doT4L z?Z;+$FL#y9M~86jE9bB`sAT_;eN*W?08$<3h~Sa=HH(|LxdAhDKmUzI|;+X$zQf#flC2uN9+=T5&h0JA_8q|ZH~6~M?p|>&2}4x zPVKhFw^{+2sRe;PEeP}#s0V;YhdpR~1?qct*eiCJ^aM0m#*~o_Co3Zt7#TS;DI+(P zSke(Ze(m$;z{;3^Q-P6=ge4vCx240{cDo!!#RXh&^V9~oUNr=_KnA#l(mA+qdgS8% zcoLDm>5*wD5pld@C@hf(YAv&lKB`+EB^jAT;N8gTY`l1!DCj+rXkD%zL{HNb#LDevR;;%$mf(!)#@)e@7^gl z&e&sNpzXnjr9-^~(xH+BwzW3hp<1)>CaAY3xrjl+HIwPnOs3B;6g%>cbW+lw0i}yI zOR1Dn38%ROBb|n`HJOOzPwrpmD*d*1!Oht=VZ?u!+&PqomZoPOvE9aO%Rxn92RoKL zQ9VbreI^q>dKwe?aY_garpMG^)*+6v=F8yW(4Dq%?uBq^+pkQ_bP9NxY?jiM`!Pr5 zvd_wr^DKI|d%i4iPQbbqY3_tuq;7#bQ7oWh*?n4~K8BFV>Q1;*)ZK8))aT&3Z}|I? z#E)%trHcpCP&|mWBC~j0)zROZA|5H`_Gt02W&j+b;M^6{johe!@T5veeaEKM{WI$U zQY(N0X{qwIjm=SfubPRi-8YapOT4JYEmh7c&|*fP7Bl(^)PA7VVc#*n0$C;2VMpvR zhf#klQ3sulo#<%SC6F4kbsb{iE0Msm)W9;B3-3g)(D0Y~k8fMN1;=0@Nn$@~Ig8=* z=oB>B=U!Jb-$Yu-59Qj@mLtpKdMLOE#iMCbCOfk3fiF+J#}v6t-HqRtQlW%qi!RKO z{U*4679o0J-77=A2_k)kYA3=cNjFd|DkXhLmq0w#4;z&Aq^qCwEqzQw7&hBeGRv2k zYAFIXj{ce9?ELwuY$i^zWXS_v@$~76r%&|DuEVdRK=I-8VH%A1MZb*rUqui%Z@z=6 z0ehYF;BQfU>KbpzEN@7n_^vykcoKVvAoe)@?}%CWs6expw`94b@_9`XoNyHQ4<_Mr z5<|p>vo%&%A=u+pMLL3~bRlL7`zI$$>h$bL4__VcAlI~ zSiu9RMBQcaTew8oc?>Wn80ZC8_AbVRO!@ZDNN#PCV(r>K19wXjKl?x%{|E#^r-$8p z{?IFflRNi0K%P3TBmT#LqUZV69`KXTOJQVRn|wf=33{but=}G`=Nf z_|_69A}_jxgN)0Fc7GmK-@ z;D5u=V?thw-SC@Dj#VQc;Dgx{_urZb-nv@jFm&A(yWuu9SmF?ogE!@&dCik} z95;&(UB(9WBwd9)uEX@`#6#!6d#GSU{<0W}Jq(G5vFUZM8k}g&<{exFj0qGzxR(b_ z_Qu2aS{1-m(b&@-OX~PD;lItAi^Jxvy>VYTlJMN{VD7<#xs2b7TEIbo5H?kccXdfT zge`#D?~RAB*J`i5-nrL4EVtL*(}jhQl1qb!`wN9HC71e-_0L3BBfeQeMVOEfS9S1n ze@5%~zXq9#2X9S;Z%vgF7v9xL0n-SWB?QbO0q00OwvNYxI}_oZsrYVUR<1EOkN9`9 zXt5h^Qqh?dm)#;RyA#3PiSSLa%Wk2p9AtFyO*L5r+zsDy_=GRJ<7gG(-SIt_pneiV zbf11bC$=xg=5%~$2?AqJ(pAyR62tT)1U_EcDQuBsp)DmkF7|YeZdQpsl5&4ee9t_j zp*--fBte}F^9MK2vl5|VlTtB@f(ZzmC?9?=fJTP4n{{hybyDC2{d2 zh8cTlaCdQRE$S(;N0L`bFQ`E`jUfMoMr#hA8z8Tyg$NSCn-XQa;(Ma>579jnkvp-K z`;^2mfA0Df>Cou0`|jXxiBtenl*3RTU}I0xRWL0i*Tf$F+?2@LgzbdG0`PQ}rH671 zO7qj{R6_ zM*7binTRI69u3>tJaMa+BV}7(bN0pJkymFJmkhN4j@b_s4@HoAyuM(#618GwQ51W! zj&(Lzl-R@Y_#XaD4AsM(KSY1r|Eejq?yyl|;>o_LK!*4R=zyipNGA+Q>971pIM<|= z|8AKt(m%{BlT{1G{D=#WG@ea~y4jmz2aQ(T6u)w|!9N^gnWWb;oy<@RLvCazV6xh? zjv=>GLtCk?mQ;rsqRANAAgS0vS*hA36+0-MYNWCcemlC8(W^TT7DFz!6cITlSkX6B zAUyUYT@^hw;eQpCd;l`Y00D{*mBUM&z`ugg6oJ2a`c@be+kxeNuhn0Od3`9$nknI= z!6v+UGC+A4wVWqAPQNC;dlB=H_}Dr%+&TE&fnPYL2%iq?uzg>$eFts0nGpI(lE)(W zzKZZN)e4`+atnN%V#3iw+qWM+jX8=+*ReOlr^9IaY?cx{1Rtlz%GA&7*ivY!=LFwZ z;nQiKg-@q_#g4^fp@}`k_Wc8V8uMN7@#J`(dJMj6QJyk&1U`-Bn|W5+gYfCxR_9w` zz3}OEE{Dz6t|Ym*B_n1tqqiz{>YQ;Ntoehd88L z50Ua@9V}l~xxBJbexjVEnJrv5LG;n@P!hmgfG&b&n32mdmm zb-8*J{wvfGxK-*8aI4h}@U%u<4!2Hq!o5=Uz^zw(a96AM!);c#!wsu@;I2^*z+J1p z0(ZUQ7c#F>-+}us^%C5O;Ncc{1EzDE^7lHRLk!@WVB z4flQOe7HBNi{Zu;4a1vMBitdi9_|NJ8{7}7t#EHqF}Sy?VYtKU<8VKu?tpu{x)1J0 z)T3}es-A?qTRjJNk9rC2$JH<4?o|iienR~#+&k3Ua6hf`A(x*~#c=ObbKu^sXbF8* zEr5HE^25DXt%Q5OdKcW!t9G~#sNcZ-f;tTMi|Q!cFR4Gm{ff$kL_egifcrJI3hu*d z4ctdm8{Ds}KDdvn8{s~tZio9#^*Ojts4v2OQauCrDfJ@UZ>wLx-KYKt_i2@j7O`Iy z!2OPz1$RW95BE9cgZqN2hWlOhF1RnMcDUbD?}z(+m4N%Qx()7+)gHJ%RiA7cWop9e&x5E8b^(nYV)IPXJl^3n*_o^K3f2c)p z|DX`(0x@Rif zQ#{k*p6Wq`jM|9F@AbZpaK>2=+c1Ay^O(!imVTIqFvL2>L_$+XYddsnZmGe$skN(H zmKbOnj9UN7Mvi(ulQT)L0%R`IwduJ_&Vs&%Al|Ro+tuEOS+~N{*Ac+$tomwt2kMX) zR-fk~_zjb^e_Lx~f7fR0OzxIF$2DF3a+k>tnW{PfsB&a!E>0*z!o$ggfz~LG#&S;k zhzae%kp+Fmh2;YBG^E6EECmBsGg$EB2xY;$bitEr-wT0=ItI@~z0{7|0sOF1tO{%Je-B?11N7->=Qt19yHTmXTfOCQeWVqTvP&)LoTe@RQ9)e zQjI=6sYYLc954HdguNFi=qpgW;cB?O##f-8vcvYQH(|*+s^=rj;QTkS1OJ2qmaISnu_$3}v!+eMCuaxUIh z`Bh|oZM0{>SwLNH^yzw|&n&j!m^rp_H>d8E;?{f2mRRE#eaH>>9biTYD0oj#_VYV5+usIFxCcJrazPLleXe2U(ha5;mmTc3> zmbH}}xhNo4`4Enek-+uB|#N0rPVbz*z_Q!)moW? z4AJ#QpRPCh3SjZ**h1Jl+Gpw#M+siH55gWtoHckht6rQ3zWM)S?pwgCDz5$a$q5ib zfCPi0qDI6gh`dBZl)Mfkk{5X(P&5P*UWO2pfG_YBw8ju0E%w?LTWjg9-qO~#)V4}h zS}NLNTU*-NmfoVJ|AM5q*rL*w*8lIfX6<$MKIa7ND7 z7!Ik(E_JFr(zBI(jQmt+sff+7w<^kPzX zMM+lsdid>l11A!Dqc{VJ%lxQyAi2ML4!0NMZosTmBynM%oLLx;w}YK8fVIOCCF^}K zgo_x!=dpvU(e@cTa`HZcW!r?k}kJ3`KNngg2xxznHZ^4qo^^1 zj>i&ChJCioV@Ant(b3*a z_?+v_hJC(Q3;P1^TG$tQi(y~nT@QP#*ADw)?^f93yiVBTy?bC!kP4Of(uglY^N_CU zKOo0#u=158jP)1?gG!-#S#ss`PlXjNZ(4G_>z3OqnwlD$+|Y+ZG#lx0@F0yN(raz0 z<(0_oGNNF9MmbOSnH2W47znAS`(O)etBu8+7txL&1-7<6y))p)VOR(J7BeUA24>xc zBpVPo9&=G#YQe6z0qmZOs4yHta7cK(5y!`X_a(uGPzU93P$`r{P0md~_`X9TO^#0! zhb4YHr&9!;DrWX zXSvmvll@}@2bg7SrsIVL@4n_P+w1QfQ`<(q*gP<@Dai-$AAz2G?`(sPf4bq?oTK-DxaG=&unA*nI-niQOVYUNK zzJ#2732ECs64wv~UWVXG0yKIZgZ+2GxSNz`zC=hkWz~fy7cX}Li`;i1Re4=>Bl_-L zpo?~y2NdP~La=w|o23H0>NqB^I?@i6p%L4aPxa^9>0Jf67Ig$PhpnN#;y|d>W%e9$ z&J6Dc(9n0?`E>{jh=(qd99#n8$2o2mKfI>9X8iRx(sl!cJV0(OD<6;>DlmloU#_gi zT#SFY-kZWF+lz?YWs@eax!q-}>0T-P2gt>O8FD6apqz;uB$tN)Ocbpt*Mss>wtpE2;fo$>ns1obpynkW z{9zp8km}{hn&-EFxYeuh<{KMsPC4IYD`Kr68?Atn%H)8}0$ZiTrp6!YQ>;!^2{~0I zlqnq!hwN_003lW1>0RY7X>oSPUedRSM_1mYFV3;wnHOrg7n1$bsH!+zI9~@VJ+cqy zh}#)amxXt&{SM9-B;5Nu+3nJKNmL(wdguDX?p^rhz`&4%$|ikp`C<0SxG(tVp$DLh zJlQpL16xa`HFU${fS};rbMm_CdV6BM7_?$&kImEXX^OuCHyjIfoQZ)4LNNjkj$DwZ z?*PNyzBG8 zVx{SPGXimSJxKWAPch0k_!HpCRZ~~{nLW@GJAmw9xtlLnM*_qyfx2vw(j>|A0j~qE z{tjShgn?L||Ei7etot40d9BX%xn9TZ`Ys$JE?j%O1LKEzWas)3(sEKvBap|mLqk{UwguPc=!$=?93FD5%W~wA zDxNDVzG*1cT&!UEhfhBQ$oUONh=aIJ>tZ|s$WeI*c;OKp&l>pGOkN5&L#`rpx4fb1 zz-N$5Wrlc*0B6g2?Hn214U@{2iyb|jPuF?PxQD3G0B?; zd$Pv?%4IU%o9f*G_zD@BP4^xJe5Lm|>_YDe*hL;IN3o0(W_rH@Jlp#_>@qJ3=~Q|H zVOM#1u;+N^!mjnshds}`81{VcQrHVTuBTk<<-@+tn+3bxn+JQ5w*dBHZxQT9Z#C>D zY|5j=Z-%|xyA$>buM74{?*Z7?%S!nT-hTjY^B#xY?rnj6qqi0IP2N{v-|Br8_PUg< zHrLaqLrT&Q!lg-UwG;2oV;tg;>e#+p?O5ZA4jr_nY`9+vHr)R3l?#=vMXOsnnr`aA zKwi%?Ad%Lk(ghSXG&e79SaLlMX*_#6zRC^`$TAWmps3ri1H35cW%{@R(Au7KkXM5y2azd^nCzL7Uo}=Q| z2~vbIy+yDcUZX;pUbp#ibnoJR)BJXrABO@iF4yrK-s#Av^E*qSOkvacarcPB>oz>r zU+4F<`8{KPvKv#@t7I?S4rE+!JHw5%(mi1MaQ6$^)nn_m`eFGBJ#I!EDINPU*W^iW z89~n`$CkU(d~_VV$movX;@i3mcGgGHz4)sQ>>#lS;3p>QVzwHP(K#)+Hg@U>==T%) zTteijO*hlw*3R!jez~&9<)8-maY-sy`h#->>W1w`%1;7veGkRq@Yp|vm(ozP@rsvY z=Kj*QWyq-5ZT`%ZK4&oA)A7wTSCodzmABb)(ZVpi1>-vWOR$IIJrqa6dtj%&`SCS` za1@_`$2a`}1V_AAB+RuKhd89#%8ZJ!<+kdk*5-yK{$iVd4B!z=*IKcBFRO1%QLA&p z>f7T8A=fa+TWX`gTs0x&stKV?IsEASwt|N9gBsr1%W2izYr1-lVa%4aZ*(Ve)O|OD z{E3y|>D)?oh+=Z)Uz)*Pz`QB!ev8#z(mV${NP*EU5k-$NkRjIcIF^KU&uQicHBF6; zJVv=#jZKPp2_ot@Gq~usR~EDOx`^C*d3pKXp`Ep03t_TxMp)aPod++kqm{YtF5k016n?|u#y#ih!l zW?glUITW{#hr1h@A@1W@Ii5#Rw)k1iT6qCW4?8}!^m;aAW`ku5-xb?39y!_-d!o3b zO_S(;=n;5z)ot^|u$h0$zpjF~i@EBGZFl+UeyCTFU+|F+3-V4M`G_FDq{wrYch&7) z4lgv|k6E$yY>F^b@p1@+?b;BTjIeyqdqGL6e&lxD_ZW)DdG3VM+OV>`{}QgDc0Y2Bdc%_8CgY z4S>Eh)1!y6K)mDi_%=Lv)E(LJMtnycJW%*P8T^yoc-GBC56^Xk1w(6oO9peu<`OyN z%SM4>EEV`~q9b#t@qwsr8)N4;1zp;Y3qYkOwg>eV^KH?W>Yko5P?My3g!NEh`5hWU zQ6OYrJlZ0Q7Ta7El0+~I*g}d;qLTRM20)VKo5z&D)>Xb$%Mn|%%C|;KaKrXfE64ZL zZK-x!6@B4gi#nB>7ZmdLdHa)wtZecHmeunx%Mej-l1vJgWVzp$pOMD(CHsTed%=7YN|>JUpFD!cUP79%W+O z`zj2~$#q+ms{(O$$#z?2>d;VsKW(H-H{D7sLBJZo&`usWayVQH5aDn&KsMQRyB&D^ zTFC5OvO}IUdt_4`6k{@urG2WeE zFu<(%)l=>}z_O8lZbloh&m*qZKi8KQWRH>Es9pFXS8Ojc!o{h*vN*NZ@x?PAm&Y(N z`3*ut;}6ZK?2o-dy6CkRrWj&Gxy%(Gcyu|=Qc}7AEc8K%LCKWpo08Jlz9}&%nG$^u zMpy@2`=s(2eTB$p?Qr#;Gdfzg-xX$8)oACrtDu#V^VL9#z9hHn>gDQl*9!>cG)7y# zo&1Y;u6VsLbi<$Ur*r)QK&*IpgQ-R6+D_R^E4tB>hrG#XEvyc|pac?QjRtP#H)*?8 z|Eq#@VA7AiF%pTq`&O5n(u}_K z4YcE4&o@2%`O{~*ALeTch^0#ar<*HB_Vh3~kYm-s3y~Kd%Mmd-i9E<7Tvt5ALEhR% z;ns!9&@*w6E@DcGJw{@q856QPUQ9WBe-c>_q#*~R0=e|7*|5p9TOwx0|GWl32E@k-U5JgmQtFSOl@HaE#EL%u!@ z5%$NT8a8hN%Ee|$Y_k+77q$!KX$cql2p}#PU|X_5oJTpoJU}k)LO^`UnCrCz;x=jA z^ar~O)N^HHtAk3xVix@Rdy4@L6})sb$1e%q=>~BFs`I_26Y<5?Vv8Tp8>i~_&5s4Nf%c&s^43HTp5iU=pRJ?Ev6~AOAJhY zHS}~q9pd+8KpO$|_ujJ5i6{bBzViS%dYynA-hZ0k&&_Wzc;K29-Au0cT^OmT~Taoh!@U!)4j~WLfY&MQ&;w>Fq=48FFz@o_7ZFdA7$M z^jxnV_Ich?*kfchZ=5XGU*Zi%P9}I4z@8{Fp6pEqJWaNkUEvi1p6->xzS3I)dxmVk zE|ewYn70i+Grc{qOT1sgF7@7kUFN+7yF%U>S9?RDm$lxRu&?nhhCScowzCg-C9toR z&DQna9e^9WhhZ=FJ_CD+_YCYNZwKt9vgP`E*~Z`Oy$+vMve~-DdmHd-kNaxZ;uQ#& zwAaDDMYd4iDqE=6d7I#~-upQ0+r4LD-{GMGd!621*c-feVBhVX1kL=QHx>51-VE5? z9*>i4^cKMG@m9mW-`fQHA@3>JAM&1tjZN6FAMt(w`=hc|`s3a)z?;1h(A7u1QLsPZ zO@RHFcO~pC-W=Hf={3QA!n+yvliq!>Kj%FF`wQMDV1Lp3JnS!dzk>Z`?`_!Iya;sm ztKJ~k-|!~De#YZb$L(Gj?B~1|*e~G1FK(p%Anb3;mgt?bCHgzwXW;X)_ifnU^?na~ zx2#0H;thb#|HwNJ_K&?7>^)v1?4NiK!2Y@SDcF0xufYDb_ifm(dq0N#|GdAz{+$;= zEqT+M0{f76CG5AnI@o{oxH0-q-p64d@pi&~+v92BcRX%`e%HGM_EB#h>_2b!}iI8+KOYT-bvn<6sYo zTnalUG7I*wNGt4S${yXk57lH=uL>w7-9*v1##|WxQGp?TCAs&;j&5w)5DgAl*^qzub^SW#4hT z5a(+X`U&yStyA<};;t+Rz6Ia{-LnXS;ai8jJ|m$w8C(J4c@P-B@8PVM*}@+ z$rRRT2j!H+g9`b51Q)_LH=OO4Vx_ft_3G=_v^sx^V-fso;opKC(9W0A)wGD#Cw_}f~SBt;fio2J$z*BcGQ$TwZ2LdV!uuF=s8AZ)ve5X8{XEt{* z6j(km6Zc94OO6}<*yh2I(dqFtOP95*Zo}rZ#1~SWxU(J97*msD1nK_*XlyBAs2P(L zJN!QCA%wCag<+mKp-z1F6r@=SJhrtkl;v2TCgd&eul%=eiEfa_K}2So1;Zrsd>VXf z6}ZTUqxMD?Cyqo&Vi)*`u@>y1O*kX44%FDZ!I0UJK5nQQ%t0tyKNv3Tdq7%>6dI90 zVutx2HOu1w#4Yj@a*OVVSYE6-$%{wQS*Dr{C1lkPgl7#vK7ko8qBZB{O&Qo`^=B~*LiJ8?AWB2Wk<}W z9o;+l(upH%*IK^~cQpT~ITbGAhYS^kett`y_yU}9h$C|d6ofPO631;@V#Vc!{;^f9 z9XH!im85H%nKM^Vgj_)plAT7%3??kx>=XEWJ1k*~lW(K!*vA#ZW8DJ}FfZJE zf`vwWyp216viaRC{jkXtgu2l@NBoWpx4|$fA8c?}rVjA(5tbnr?5F-dZ7sqnA*z~h z5~Y^7*_CmKgOQ_G+?Ui`m*Rh#RXZ5}oKGQQg+9(2_~rqdBIafko)#C6X9n2xWz!qE zZ4KW5NC`N}6LOL#6qG!!B8jZUUC)$0Zo({X_UNOToIgO5_uSwTr>$gI%ccI4((qmch} z{l0QO;Qpey1F&6&csIb#lo|Y>l(N4W;gkR$-(YYnxPA+ec!wwB5T_l_%oEApzsb`Y z0TaGIs3QX{($kw7i@qim2WP~tI0(7oAQaTu6So%ctErfTJh`TV3jt2mlADTta=fKK zCWyqK`X;J2J1_bluc=79AD3~c+OzSL^(9^z^7<`>8~^$Z#cagstD)f7&l(Dec7k?- z>^qqgaxy0ruL8W-(tm>k?REEK9>dq{q0iA*u@e-EjQcpEs2BJo;?-Ge@tw=8UEmEn z&t5om4H|QIaTc2wIa(BWy(U-c^35IU4~V2YBK&AofTq+ggl7Aps9kbTtGgA~ZLQ#e zilNANuB^`ZHHfWL8jd}%khl@NpKQi(7ikWZFGFX_s!x`DH+YC_Bpxao^s?m(GQ;Gf zR41h@`YoV$qMDW1sxc00)+g{(ouKGL73(NkH7j=(GE>%#5_)*4kILn1%_u}@DQj{E zlc*wG#d0N2$dx>yK1)8@C2isHOFr95ex)3y;T3Bv`J;I2hr3j>mU54$uLd~T<7<{` z07i@f9cOCk@j4}AEDfd)myjPcdXy3KpN_{&mhix>>mv1m&774$l z9*GwoG7fRL%c?ytwMA>?oOWI#K8)73W(ht8o3}#0346ZGE?7F;g;`(kPV7-SYw*<9 zw@Wzl&>{a@?F2NM({OA13pKAiPsT7}J(#^eu$*L73)v zLP%5{#-R|-!Lue=2uoJC{2QgPGC>NwJH%-QAtwbw+I);yz8zURfSk#tFUe0wr3C(t?F{b@zyoDdca~hZoBG^p53okyf!3KJ zb^iC&drHF5ImRK*2l1?pm#jOG=DwI#*^0K@A7W9Z<2N912ppI^_@iAi=CV%6l^7u@ zF`10nN4hd_L=*=+IoS1I!=p=;%-MOU+UJ7Lw7P6WPE`B_ZhEFV4QJ? za{->K5*hc0L(`_cua5}X>Y^_Imah{4AtwMrP5{ASj<1h=;|3vMSE(-=`4BVP>;J}+ zD+m5KpU?H~fbDpVppW3NgA?f}&laI5FNB?X1bGQUC?U2?zVW5zOX;!%Dfw|o^~OXg zIrVT0tz1wcVcB@zxI*-WP1yU=7joppMaYSZ(DB8U+g;9GdiGJ%17wQt9HW*oUpdM{N$Nc|^b(atnW+{Z zN5W*@Ntlq6Frnj1_=IigM3F2{_w?N;{A+D#;&+dNZRzc)CF?%^^p6R*k6KR7oY)9C zu@O4H*f4}XX6t(pblnXyQ>}KMtw(AyItryeV+vEZnajN1$ZG36zi%C?OFjR?&AM zN!)q{mc47gl_gSevkEtM?k8_``%4dyxcD_(qK110&PT|(KXjRza72W0h?9fo?6~d- zci}T|DG$x@l_EQ$;E^xBuVUZeA0)wTEOOS$ANH|eodgIu2@p~Vpw-Xn%1{AhdsGNt z037M|lPOth0kFBGVd}m~6hPuvu^ERrj59hx0L-?X*GtJic@6N-`>}*q%7qjY<-*kZ zk_$O?av|j8LP+H@0z(UvORiKIm-BwWW%XftsT&nvF1`yy;W?-nmnSGjP%h^u$R**F zV@k=;lad*Qr=_y3k!K&}<#|9X6qlog`t5oxUFAkh*EpGRD|_cYZJ*W)yU z6s0RSLay8hNx60Q;wtd?B6pXrMsW6brQeOn=|NoNj^iJ=m*7a}dfaC)bR||c{ZB8) z7c}hh1i3`WO_<%h=Nx35`($&yLfCG-;m^p0o9##BovVLa)>-1qo@s!rRA*rK8S~5C zbg7GdF+wQPV9%4d(vUcnWE|pK z=?Z|5D*!@T0PcQZU-DTiZ}Y!Ra0oBmZ~??<+>$MKDPSywk9Qp9>d{dojV;eO$AGm* zd@|+;J&({6uMbeo+$|J{{$ADSLzychK1t3-vyd5Zj+QuAR%e#-pS&Y*jaw!Db2~(T z976Y3i_+O{i?kw`vT%w|X-D;9kUR-y1Z@#7Mm-}pcU1xv}I}h z?i&ihl_Mcnj)bHf@9fP3C_~k*yL)*p#5ZZX$Fy z5boMk*4D(4)#u?yNd`4zApC+IXktxf9O8Ty&)umTaB9XirWt30TI-Fs$oRq64D}0Y zhI)x+5RJulJ?OW)m~00q<&d&)U6RG<@JNj@S%A(*i1}SA#pyJPkkcqaqEWIXKE^e5 zsEVBK_78C31R2_FgloO-;+u%9XnFRF5%1b>p8_dx-)^py#~Fb7OWFDZk?{!cCwCF1 z9@<@jaGGEVfJmqWCMFDF7>79QLObInP}j0z$?C?Y+SQd!t1xJC{iaG4hn#)UYenT< zf<$7aWZSxAF@1blQ7N6Q2sv31%9M;v@rDoYn7QDRiNAk%_9yGVQ@;7{FVrwYOQtPk z4i+{xb*x^~*1oWL#bO^G*V?$)8@H6#IPl#THnXCCT9#HcZDA2(xbDAwVSC$>g)3Gy zENdFKq_q`TH2F(buWB8)qVc@ZXJFG7Ss&NPdve#36t;%LCsIr>WX8!R%F#9SMDq`v zZ;T2d;Lo^5Y%fwz=06%aQ~y2v)}ullW9OP#QzlKCFo|OU!SQ`2a_A87nJ|7LWf}LG zc=@H1*fIuurcU9DB=s35*K_c{EAD87%{it0*_~V0IL?Ax^g~!Q@=4l6gGq;KfeV%D zCjdLCCYD1gsk->INhc4tN0A@q;Ayr`V#Y0IcjXM5geD4GZiR7vw8u3&9Cl}d+xhpRb|t5gg-dpYJ2m7$&wKEy$OYeS~issDE#E@A{sDf@R8v zG#dFFZ9Fs?#@98@13r@HfVQwxiAGpISpLHgP5E6iWtK;na;8zf4Pi8yNtD?PMI)0X zw7>VY8?%u$G8oMBV8IFMVU|(;jKpMfhCD~uK#|t%`RW~#JkxTLAI>Dq_>!m81;->$ zTJcz?g<}Qe!$DItaxLsMNN?R&KDa5F3zlm%@)&JAG`I)c0dfe)`H+iZfTZ|Eh4T z02jHyi$=Z&h{!=MWXUPAjhJ|7a*Yd~A#^_EVmUz4x^$*^!m3}@v}#G~ z&H5KO0+j{w<9rsI4{?ILGl!1%0>dE@pLg;tIL1435RdhNaI66DCj&1U83j86e*WjZ zxuD^f&Pe0yfKndmv2-2==3{so%3+BK>KM~Ub^C1-#?wNg*j8C z@fkqO({=0u#7Jwis#X6bmNL0;@_9@#oa2`=^DQ_gA5xFUdORE}P|A4-iAKm_Ka}$P zuSUlvm-5+y^Db>XGr53P^)3S5&UA$v_MC*IjRnPL{?wf`dtqNn?~q10K2o zm8~iHdLBMJn7zM-VKhOVXIhSnSHt;Hoo5RwIK~BO#bdn{juqennFbdp!KTh{zw0y% zThs6_*fX9hIGwcd(2O=N^kf=*@<8Xi2qUe>>xe}YVjijkXAg_ zbAAMIgEjX&_(UUYIXMfd%Dn^;6S+8Fa2}*hG??>@i;ogne8|O50FhSfT>8-jxnNqV zQ4;1`J{Rfm5FF!zwBoTi+KZbY7Z<=gDl?7<#=ReZ=jd0HHHr&|(a1Mp`#swQ20f)* zT!6Aji|hl=I6N)>L?pxo({fz!@O|9)Tx7sYaEuGmipN?Xjup_Ti+~r6P+K_K|M{pJ zcPDc(MsT`m6Ak7f%nlW z02gT5z=i7z(!H+R?tDC%i;D$kBW*l1V~vYHDCM!j#XATit@9TUizdhg({fyVIGi8l zf*K__#sz7`V{yw$+ypg>Wrnd|gxZqpjsD78P(%C))$U6K=RVqaXvP^A8R|7oxQHSu zX~x z7mctbXIVV>>}Ygx$V#YIUM@JFqK$`Us&SFV20?sS7LyT`v@TmLp0I*t!L%F~KMLn3 zpizQjT#!~g*3ZJR0$fZ3UNpjfja>Y>=g%{exwt}b?x&51W}0y^PPv%ok&7ANg0za4 z2pX&)7fj1>@r!VN0$d1=aY0(~SicU(3UDzUc+to)*!{7@e%0xt{+P_gm4fp#*nXco z-5`GBkY>7YaW%Lgt(i^Y2`k72({fzw59dd@$U(T^7#E}!kM*~3tN<5R0ncB78RY%+ ze?Eu*$Vy0~P&~l-A#FS~R~Z+$IA48O7Ilca3ICTZbG~6Nn3m(h2nm{By9eQ);20OA z6_4d!i%Aq%!P=dV|ItVuY>q{%zwlsDG8YAc^B8SBH2KEG7Ud$}BNvOnMIOR;EGHIC zuq^WN-*GYACqxumZ=q*Cp7^{V&-VdcT)7dB6)1~BghnHIu*t=s*!scAToeh;r)lG% zDKsu#R4xjI3x0zq58=NQiixD#BER>dk}wCS2%1{g`OM>gcm~bX9bYk@w8Zi2I}2KHj4x7;$KoYOaTBcXSHn9R*-9I4 zj(+~+c|T3&YqsE=i#+=}cD3=G#tkn4BRip|-bNaG-ovI%USi$CQgQ;bIG-lGc^&u*`g` z&(dM)jxT=SFmC+PVciiNvJb<`*|{7m4fqK&^Q$k zO@;CGOU-kI@U;zmk=A*42%1`d0S><(%e4A?^*D#NB($zYwEq1S8BUsjXD0bv5%LMv zN>X<6Srg_|%l+dB7aWrha~Y5IAl2JUP@g+Z4T)&h_U2u#G(u&98m_BZTTvWq58fk(} z?S1|3Ry3=~N=SQa1m}COeKo8$h?jlQRD0y&dx%O}Z+D3&ES70Aj-WY%z97UGQ+Isr zPz>kzd{KJ^$M_=kc&zV-V+FLg79r8dM&yUrd8AzveIc1IE>cA!&jE*RGfl1W)unvZ zddPF+r@$et0~;M)xOAA7 zF8UCV&G75m)KI2{OGW~7YWg_7_Jnh&e4V1P;-y3C@mNQ~u>z&D05PMHXHg&-=eEl_?L*;!qge>PqLFl@ zN?Q4kiYKgK=`bzF#jAd>xPA+egMWf!T#!~g)}e5$K>CxFD^ceBMt`daKYo-ymO1-SKscFCdBN^Yu9X36Aka z>hV}#Rh@UK1xsfUyrL18--!3@gzvqQ+$J;#4y(D}CM+_(o>9IQ316p!FVbrI8Z0qE zE*8PZadEB-RNLnwg8z%~#On@ei4TKht0P@!f?O!p&1XL&B z?4gZ^X0dVcE9GLbaB&`@lGcr17c^KwE|`|%;yuZg;RISX!LhO+t$3^qP>-7+7me`t z_uCBcE_n0#o07R`5}YS#E0IRyA}hmrH3}CKkj`Xy?D~cxhPYr_j*CIz`~V+Cr;Qs6}+H%p?K-k!H-eIl8QWrDMvw%~CytgqO!m5Zgq#dM@YS`(gC#1I!u z%W=U=S>wjnC=9;j(0yE^NGl#IFB~ht#d6?9Bj{R0qi+4-v@OY8tPmWwG(H#0jf;)S z#d6_d7SbWD$H}sophht*#|1BvjT@f}DyQIBS&&vd))nDc0WMYoFB(DCQ5%MMPhIiS zMaf)TFE~G>jfZBXaq*0Du~N9GMmnU`{+yt}3Ua};92ZxG^P?Iy1mS{XT#!~g)|_yx z02j@`!$qgCd0SFh>(+~txxi2cIB(F#L(^2A57q#L1 zC>NaB3660=TJc!R!?6NfaMT)&Oo2`FX7i%4$y}^PNHp>SZ9FtB#>H?QR<@9f&`MV; z@S>6P1s_-1<{!8>neq*SvxBzcwHoEIO1U+Oa+}2Jf!z=LwH|u;NBfd{Jx(p7k&n{G zL(^uIV@kPAB;N+vkk(gr3L31SQegfQvEs zx8fD?gca1NJMiCeaY;Bo0WJi`xFD@~tSRAG0WR(WUNmwQ?0(>4>a9agPUfOha6U;J z56xZ1#hJ>*T~Zc1!3AkOYl|g8E|`|%;_`5Q0$d1=aY0(~SjFL30WLNGFB);P-gNIs zWp+U_7k3NJ*J$IR*no2gu+BV2Hd3(|_m z`c61jfQx&87meh?=1S5j11>w3%*6)Z4H!(pjn3m(> zd*S>9xDXuUg0$kXUJb_zaDm}PfQ$RSw|!4C7hQtG`Lxf)y~f2o%Ei6H#c!31?{Xk5 zCddWTa$LL?&QE{~!3l8@kM-+ttN<7HfpRo512${->(B86LJ|B6_Illda}K2QJbghrtDDUG-yFVuD;SEyo2vLl8H?UQck03(|_mx+WYez{N)3MI(!8W8Ja# z;ljTsbHQ;^H1c`Dfd#YCxcHuOu~E4AE4U!7bJ)3v339=-92X11`O&gqqb)eb1!=`& zHHBjZxVRs9(a4RkInF%)$q74?xp+WuzC#-i&Hcv3KIP(m;bH&~Nb8lK3YuE)D2_** zPDhTMIwfG1e<-ATp#{5b`& z{CNw2GU3E1lk(>m%&RJxmrwlqS;vb2tWt*-3!?FU{b16D7Rq2ER*DR!Mo}ufJUN(3 zepNwiw&X^cOhTXp_yLF7)%h;FA&k<3SPA3T*RNRBT3?TwqskiEJ7R5ZtK0G?LlhGs z7oQWk$tM*N2)cyHl(x@pXkO8HJQTZCtq&xyQ&mt`KdY{^I3K*toXk3f$NIkd`0^z7o)%g_t6_lx0W!K=}o;mmpLT5z*tta2#x>^+XSqa4<$f=@K`n z!XZ?@mf(bS>zaq~FO!%%HZ!h{j9%KXqIvX`iBqRe^=Xuc zG#PpI6~C>ip`(f9fzi66Wm!S~M5_R^n&f^cHjnvJy&1S5-HL}**=gjO*5(yUK%)j< zHECHEK&UDx#*ge4EpJ+Kz3YM6E1H@bo09TQ-GN4oA5V0=FK=nOAwUU1#S&7{bfa8- zmB>1xqm2l5H{>XvZ3Z*o%j5FPFZX5y?&e7(qY|u|GOJ{~#u4(2b2 z6Q495aznWUKHMJQz^CT&GefkdkJKpv{alD*h$;qpZRt$>j@^CZ!jfWRXrjoUknh*C zBKP43rI;Lvy-I0$!K_$)ZAo3l?E2z@+JZ?FrH^5T{3`tq$M@6svnx04pD?!x$t6V+ zaYDO91K%IAWu(3|fBr0vWlBq`ro`q!ugKtx`jVhGf?Ytple@bwhBpa^dVwG#qzFo( z2(Fw_34>IukN0o=V~q}B1aADypD-~ThSp!>B}EI}zK9ANq8YkpkbPV*OTjDi$YAm% zbNP@CTNhuEDKo)hMe}cB3m{FXLa!~AU$DYgQ~7u(lVU>*Whhul*&YQm>$>vLVw$V^>*ajdqouDZTvetCUq#mvfl4lYBK<<=J=61<6o(ZTmD zWVVC_>>A~3Rx~zIFKbtquW9aBQFwDlQ~sohUS{Kp_J;OV<5n+Tc}X4(+MMwdE}MG! zl@UgExiHs~FFg(E0#8GP6S~ z0K{845_@%kSA7ouap#2R<$KwM2dY$2F1BY+`j2Og76C}PfTy7;(S2dzUi$(_}@-I zuMWwbUWF5ccsAfk!%9!8^4JBuXApkpoa5*3XAzvZ2hQ3W%y{weLshBh%?I8^m=||4 z12CL6r_w(b!OOroc`oqrq+#ZtiXQVKJM;tCH-x7ZPhb2#2fX_cUbfJs z-50$J5xf#OAI9@pJbl$)(t94^>$zPD=6Liz4xF8MUc+-ddWRAIh3k$_uO7j>finic z3_NMjgjD76F7Q?$e94*07c=Rqo_<3ALgz1akXAw3JIPc*Z zxP)s{nJM%4ci_bkUerixebd{5ur*pT_+y!r-%E~?*2?gD3Hq}WZaU)H4ST< z8mpU@wdY+-|0*Mau&Rc(hE?s1z%R9Rh&)BT#v9o`+{TNA6E8!HyvS|L+2zo!10(h3ZLm<9T!dGsqU zD8fnd0XYlc<9V7`b!-+!W1>fBCS7187+5m9tXx7i2%*~fHC3^q`ts89Snd3(n1w_= z4VhV57ON;Ik4Z)uC*2RJs;-=cg)U}+{{76qplCLxI}+&(1hz>+V77j4th%POvI053 zn*S!^rQ3jwrwLTlme$TkiOh^u$0~|qQnHNPKNxP=%21M( z<&rIyAdv?;@8Zg$I`OAq=${$(uNOH}PZ>BUA#i4O!7PbQ$!&+^qTamnGD?8a>Ww&q zqlk~{T~bgnD>j?TTTonF9xE@5ReO0F90>*&gG?<%DHtOaBQ1zg6RTyh)!<$PM?RVm z>Zgyo1~HODW+FX^g_4g|7nF&N8Hn$l#dD}7T!2FJv)Dfv?oekIK?%X$Me!nPmo1>uN}^pr|BPTvS;#A7>U^%7cPPH6SR0ol_U9sm1PE zhch^c<2Mrpb5TMCg=H~JHXPcJAetoQHym-?^AvYz5LcvAGrs~EgW8B%QTbS8eiJR_ zj@*T@d`@$csMS?jshzE)P6{G{Q~<@NgZq$C zgGgv9Dk(CYR(GK%2Sd@=mCh`U6(`HgFZfe}=q@E!`el_xP7;2yBZ8R4=r|-Zc1EW{sH>_%i&KL}iQ@;-IWH9&lqXj2 zk~PHh`$Vjc$w0%`o(ua#tg0+4Et+2+tFErB#-<^cfs6V?kD(V;M4V}YV#iKd!uPCFEA2AeD6|JGoOQ@>RDV_UQk+L9A~SywHd5$%tww-#22DQ z^yv-L@K{Yz0S0~?%>Ed9jS{Qjf}R^fvR-XQS!a6a6{_dgm)2mACOuTF7?q}az6=06tgb~V56Jnw(Q6&ij>nZsOqg3qG*nN>3jYHG#bM+P%Mp3k((M4O0=OoMRMHQ}6PsITVYBrhXp zH%h*vVHv7+K{?AGTx8_@Fu^l65Bz52eBXJq_dxTGv@&wO3$L;&iw(q~@MR8|9hB8v z%;#K8KN=CJg&bE?+a$(9c+9M;5cR|O!{y7!LWUVRb6x73rgH`&(RRX*V|2+mvsi+Z zF)J-CsHiQdo+Zqz1rE!upt`zXeqm)DQY1M>1>+W**1a`_3WFGz6+Q&T%j80P#K`4Ik?+Vd_RSrl4X@Ee5+RaaICy=2aBau}t>s0S#9 zOnCJJGo=^=k-*h}%dlQw%!?q&h(i&oXoHW zA<}iAQdCe^C|PG=BUe`5REYT?Q8l}`Bvw`>IFvkaO!&mv?8J&e?R=3CXW$g`{2CGK z$w1*m4PKn7VS43ec#KNcD$8P+)l^GV76#L5AwF#ntg;Ypa8Q6G8 zHUgYOM&)>Ur_=6 z#-uc;x;HMtYdCzRn8EKLISLYsPE<2C6Mh>(iYLWWkyeqa%@z+)j6v7vG4>HhObV0g z)8R064NIEKYIxO`q4AXUv)C-~cMdSQn50_)+KX1Vl{B_htXb96wqi+f)6x|!P3@u# z{TR0{9GMwvg@0*H(+z8yS~^xVG-F>yQ^P7@XAweBYho33<-+YBeQrx7nC*x#uT2TN z3H-O7f)Y3fQBbY?7G7k}r8y2AxeSQjUHquYh-=EsN;nsO)}c_x$bC5ftcexyE#*t@ zU=z=RLe{0tBdr-kqK=oXSS-A-o*_Y0HL2e$`y#*K8G%K|*ivpR4R&6L0HUITEPbGO zRCuT^|1!QY6x)Ld8mQ_MgQJN8W zmsU3|S>4tspMIg%{0>ozFqq*;oyq1g7uL42vKAFsO=V7M5kXsbbX8=HbvICPiBt@v zGIl<~Ea|eA7GJ}~Sw^`DQLt=PIopjAm=Sigei1OUkK(^@Tvl}r+f`O|ib!a%;L4DO z_L=qN%sw%g{lfXEZcK9wNFeuRuEuj9&ejY=@dCGEnWCedAgcK>iRjvMvQs7OXkvoF zK0?@m44Bjn%$bOd_-uvZN-JVzX-dWzehBo0H)+(EZ?=+XmrF&PzK1`hHTcLmHUtQD z{P&z@5SMx*T@wlC}kRHT$BbMhbX62%sUAoT(elm<(5 zQo1#fKkNG0A_rY@j`2-Ete{-vQAavj_gUBQir=dbHKZJSvLR*(1X^8cLx0JnRo+wK zHPK~tZc|(Piq$PmjY^t|&rYhSphN`2QIu>il{USC|3E)Hl41vM8qB&$wGc7*n!#5( z;$c;o3RoScCMj@{0+_DT<)W4_9~hX5P|pWeDHa4;8#`1%26B^fz?2zlYl*jQV1!xxq|i{8~$mJXW$$#)s%g&8^Q(&TSYzAVzSQHnBZ zq+~fZs!tvqvCvo!LcofG@@8+238?VTwP#}3;Tp5rTiXFX-j)YLvwTe`0?W>2=O&YwT@g<6FB8@a(1&G%AQpi zV{wjhI8@@|fHW>#M$zVls?FwAqPkKegB*g17e-;E#DTqz>PKX9se{i*g)+SYK={Eg zrckEW3@A(dS`{J&mM2iq|EwEJ)Qzf}KFo#p zJHxsC2Re6vx*3W$P~A**2dSGi%;68#_(L?_P<68vFGt;B>Yk)-?l2dBxW+$O9-P6=PUEPtxT>LXM{+Sx@EOqk~?`(BPse6vPqldZp=W6`(G~W5@UZ8jvs(X>T zW7Hix%*DT0K3V6tge4vey0APrEZD3rRrX-?re3-)Gb%HLfuMrtJIyNZne5K z>ei}Tr|w*JuTgiNy8cP{1^WL3>Mm6GT6M2ew_e>v>NcpmSmm}v-A46qQg^Ak%hX-2 za$TW2Emo?$ugC5hnr6jYrEZJ5tJQ7Q>q>4=w@ux4bvx8uqwZRDZ&ddtb#GSp7Ikk` zcb&TH)xAyK+ts~8^LeM{|1Qmcr}}SDIoz%8J?ef?-FwyTQujV}yVc#OZjZY6tNVbu z52_pOQRQN7Vffbstgpqw0R_67&l&AJ_kz)qPamPpJDzbssw;Pw+pb z?&IoyTHP&*_ZfBnQ{B(1`@}Gb?mek`@L%fxId!+H>!0EOg8u)ax=(5RFRA-wb-$wS zHg&l+Li4n`UsLz%n(jB$eMa49)!nY{H`RSk-RISPLCblEy5Bn8rSomg-_C0H|2yiw zsCc_nUtSvR!e3VW@1E}D|Gk0E-L38`!`%Px>;E69`$MJss=7Z?_s8n)8RqE!MBSfi zIzLnQHO2e6y1!8Om+J0S_gCuvx4OSpci*r=N#J#L-%$5AT0iy=bLsz9-T$X9$DlL^ z&PWsYpt^6W{vFczy_(P8tNTCd{z2Wt>b|A!AJzSnx<}M~TitggHSDTY_o({+S>3;= z`&V_3srxr||6c7PzNhicQU5;20UE!bx|~_l4AB1>`hTFhnd(1C|7Yp{!RiiCcc{AA z>gK3BOx=^z%~f}}x+kl9in=4zJyqS))ID9@k?NkI?ms*Z(0J#nn=}qk{|nW5Vt9zxoN#lTOSDppx z7OGpMZn3&Cb!VzOOWhK6OVzzv-P!7vsavjYg}Qi)EarcG9H9B=a~z=IP3kUHcbU4& z)m@?PN_DSSw^`-BO5GOqU#)Jd`rn}KYn!_5>UJpJ8gi8Jy7#F2L3Qs{w@cmo)a_Pxqq;rn-mmTh>OQFX zcu3t3srzAdH>vxux*t*ZKh%9h-H)pKF?Bz#?q+o#RreF>ep21XhB-NYO5MlR{j`Q} z(Q^2V{_k@fpz;1o-OnlhR*nC8{r?4Zzo_n0>V8SxFRS|%b+@VeRdt_M_iO5YUCZ?w z>OQ0CJ*(l{)%~Wr&#C*ox-Y1^L)~vF9UBMe|L>^#qPn{j|D|D0elM&0U3I^w?rwEo zQI{`HX?~#Y50ze@;{Xl+Nwtf2!f}A&_c;#Ga2p5c|JRlN8|wZ>{r!7?eyjffPyK(V z?g8~bsP3EUe@OrLs{4C&Z5*Kg4=es#`u~sW{z+XM2k8HI6#rdykE;Kl)%}aQHV)AL ze^b}S0UF;)74O+2p0^5r`g>Uh4ccwc9)tE8wBMj!gN_(<%pl$ZtMW4_*PxLG zjW%ekLA+y;x0&_#c(&$P1K^qO)WYA`VwivY4 zplyI0z3t|=)1ci3?J;PtLHiBrHRuQ+NAH;VrAK|Kh~IH=oR2nWtU>%9feW2( zP_aQ}2Gtt0(4a>zpmeNdIvHdclxxsPgGK{#vL9>EWI&&B`UmJKfrM&@X zK^qL(XwW8uHXF3XpsfaNGibX(I}O@x&>n;K8noY_UW1Mpbj+aiem=ih2IU$w(xA}> zjWuYpLDLN?HmJ;?T7woE)M!w%K^+FIGiZZB8x7iI&}M_S7_`-(Z3b;OXs1ED4ccSS zUW4`<)N9ZYgN_-L-rwYBP_98E4H|9GSc4`TG~J+LgUSr5HE5whjRrLv)M3y%gEknn z(V$HRZ8m6&L0b*lX3%znb{e$XpgjidHE6#==??+G}@rC z22D0-xoh7ggSd9${6-tZ^$eH$$p&$a!QmB~Uzz#U8nn}g$6Ym)ND|P zLF)|KV9-WDE~lFe+HBAkgSHy9&7kcD?KEh&L3<3^YtVj!dJQ^a&@qG3@mgEu2T0{- zek09qv_WGHnrzT?gNh9*GpN>}g$6Ym)C|bwpu?bb7P`TpjTXAe{5Bi3#h|SQZ8K=Q zK|2lF4alXt$Dq9i?Kh~`pd$ty1LS0tKG=t{49YcVq(MB8yh_^1u?9^xXu3hg29+69 zYtTZ28VzbTsKcOj25m5Cqd}Vt+HBAkgSHy9&7kcD?KEh&L3<3^YtVj!dJQ^a&@qG3 zhxl^IGAP%ekp_)6Xskh#4Vn(fm1VI(Wfod%&_aV64Qe*1!=QBrZ7^t~L7NQPY|s{i zwi>j}pzQ|jG-x*<)jxywTIha*dJQ^a&@qG3hnoBh$~9;tpa(?jMjJHNpveYJH>eoU zDv42MP_01=4Qe!~*`N-C))}V^H`1Wd7CP3T$rd`@{J0U*QQ>w;N0~b~9mK8F zF0|47nhokOXr1A0Fu#rFx5@lA8??ost%kSF{I;9lPJ?z^=pKXi8noZ=dd=^MLB|YA z$5u4QQC)X$XYai3SZ-B!dG?&1@+S^Jm|k~XR&{qQqsspO_S$b{d&3ZN5uRL}!}Gib zfq0`1SFm^l>SWky^0nJ2&hOxBX?RBC@y%EOyhH`rFvFO{eM^0=^muns+%OJt&cpK- zGax47P80GBA@`)Uty$86{g1JmKzdLP#q}RnIGRI ziuH;Fu`&*ESUi6^fmr_qp`KWPWYh_ekP{#w6<}6Z`LXWuV@@g6?OdPXb!0$2yJANV z4Mb??j@+*Dquq7Kx?@MX3Nt#_XED4SagQLp?pWuyvb*YzbjLvHNLNV~5eqYRArUA# z$j`}gALb?k^gILG4LvU(PcAm4VuQOt6qx&-UId#tSsjQZBGnTVR6%27TT^>` z-vz~7I;IFYrU)f41s^FXR?YuSrlh1!$P{zwm?GquA|y<8_GX~%Vw1LJAeyhv7e;i& zqy<}(wQ}%#l&v@Eg+u3}H5>YLPEYzz=+VWN%=ILwj8$yU%8$NRo$gvM_RUN1^p~&3 z3=)W)ifc#s-6|fs9Ra%^ZZKii*tcd#&pt%HqdC;8gim(Ls$3_y$8W5 zUl$sTc)Nu*EZmceh8ztxWbdbA&(YTJ=V&fkV=WzRH={jr{WJyO%7Bn7143E`BfH8E zqeJP=*l{qo^Yx*fuczJpa>pgzb%#4|N%uOkHE2-h>jS#Vdk+mkAKsm|<3L8|z5!ja z-tO|=uJQwi#u9y6BKH0S?ET&4`+?nEcc828DEljxWkz?-jyH#NzA@nL9}T_dNn}eC z9D)S9>$2}l&%8JG?$D>p-@Wf41bX+@y)*Rb*gKv3vhRMOqa_d>V~zXDvLpAFzdiKn zy0<~E^Dq6@lmqwOb6-HbyI)u{NV>~(G~kPT`&v(FvYO)ti@2WfI5*`XN(U;g6)E&b>F&l%&tU1z2ckV?0O6ly&+Hv#@~MEU#;yo-iY03?~BMbySV<|><9*LfO5U{fHnxkalO;YsC-2z z-Mb6_`*{z;?(eY#26#^k#tzt--cMm?d2hfT?EL}u5br4LY;Pba=6J(kpX8kfJJ-7e z_6YAP*r$2C6>y~20Q(HD3-(#w$6@DrpNBol<}@rJ~ak~%Ck ztV?XA{jM`{aK|{rA=O`|?mAiDc)O`f z1(-YVz;NXAwvSnZYp$XyFS$h823GNmY;-!a9@^e%M#{?7ILBOOCv zr*)hKJH2E4p~CK1@6hMasUATai{@h&#OQg4#-NoR`h1Se0}gbTWbKNi(L1-huD3g_ zD|VzCNyaAvSy476M_l7PrW{S$kuk4IgMGLwwy$&jK1p?7TE{TJ=^Y~uoeA>y)g8Sz z2NYwyOVJ-M?aDZmLF#DZ(YDFdB3qgtS2-gxop#fRs7$ESy$g{g++_hfLq26cQ08@+ z9&Z87l1_cFG-X3%l94S6ljGfu&=DSchEu&y!am*m2JADWW}NAL8*rXSy*%6d0qk?U zpTR!g`vvR^QYzIxgyi92{q{{_lXF$VXnh|Li9@RUQa3pTjVsr*cj#=J)%5_Xt4&ni zPyfzH%oOP_SzOr)es#_+9*(s85&-JgpqySL!1KYaYh4gWY7)Tq&Jl3|ybB+#(x99p z8qiYN+^B@;ZS5Mas#oum;LVv+7aPxyflH@cgq&yz#YG$Ml#)d(D#z~TQargB zlDOB?xv~++7u!(yrlF-^7eu9d8_#GwzF|9cB4tbz7~@bGNwp7U=5;>CQHLSDV-|WSf61MF;3_=* zWdnrkTkx4e`1F$jQ-4`_8!CgCY*~KGNvWyzh{2Vl-~cJHFH6)^#v#spJjc=0nNCyL z$yhJ;Pgd>0+Q<9>ozfEaMDg)4XY*tmhFI*m6e~EmfOdGy>hBk)3}drq2+csWPL7#1Vl$e|MW4}E%0r1Ooboqx&hxHw*wJO4bQ<4m8z zzNwu@^Exomk0W2OCN$kfoqn$`Z8-cM7Nw-G=|+ZKO@0-S(}4`B=>uiSDN{btoF(-+ z+gk&S9PdupC&|bm*ZUOU;VJ9(XAzQ(=O3xtjKjK3s>iF_uG{{1%!`jxw@*eU*yO?R zofHS3;dT086{qx^au9OLLC7hG^P_S&KT(b>ZEo=r#9!OFbNw+)F1_}1Fc-DiD%YX9 zGwyu-Jw(5}{8&diRvfQ8^u5k2)42{He=FmpxDph>N{~-#6o~g^eFvDHOuC?D=L)5m zqm+AScaT!QuOod~y*(eUoA}N&s11-C#yx1zUO?;#I4!2Q@X1Wr>GJVkT!{=gLq4D~ z(Bq(NkT(bR5U&~bP@N&&3-}~&BkbYc{jg8={sZ<1@8hsf_a1{iGNp!XK}b-$@K^G| zvCmul15=bRFk~F&^)Wm@MSJc{XtA@Vp=CwK&GP-Mnw#4@npTBce2(Qv047T(8SDgF zLP(SY9~kyIoS+2OYp6HDP9zU#lm`s4sb~qw)e39#SaN`?C3Q z!OY?9Gr#@jN109Wh7a$Ux!{tCzkhi4C+ojczWMJj)NmTUWZJ?dxL#yoqx+V?LVSF{ zhsU)xF80PP#jXFq%fa(^=+?5d<9t|PNoy<6VDgvXrnPY^8qXVjhW8#~MZMr>cc_KY zNU>lBt>C9`n73$zAKlRcQN-ZqcA|nKFRg*T_R5oPjZ-7f8E9^ZLt36UnHG|c`z)pp zjPnV8P=~Rjk+HD(R_zDpZb3en>tGK13C?e5alM53R*(z9 zF)m0e9&42!LEIo0+3<;TvEj5Kh{TKqHHvM0n2T)VVuo^&EnIMB&y?K+A zem@;>BJIW8m9O_nyz?AimjEC*o-a~Qrh3#RkpHl~IG4p^?DI5})spwpmUOKX=jc@3`O`EjXSF(n_ZKMl@B>I+GC-kDZkG z$>zG>eEM`c7p$XrjISSEubb?-n4pRXv|n=ZUSN`z8aG2A(iY51x%eidymMSIUT{1Y zCmlrU>Ls6ofv_#6;5i=S=wKOlOnv?P>0C?|988&>iz%Lq<(7*n!UeTa(o&OZ2t>GG zUdqKOMtSGBU>ymL=Yq77sm}Nm41`=jAp;lZ?orbP=P&8wM>oxL(Pg=qCR{v-IMSja zH+5l;VqVI{zqx4ncU-VX36AH2w34a*BbqAcQ9J|^kIjQmUF&S;f;-aNVy57PT+HxX zeA03;L%8@NFiDH%W3DD_;-vqPm+xHtk#=A7LQPh7|a^HMG@ik2tf zLU1BnBvV};O%-r42bAM6_Gg;$+xV$Rx2JP)x#0W(zH5s)KD60#F-N%g8{m)@%>t?m zxnN$(#TC)=ST3mH3XbQ3w34ZuMr0`mT+9V19{W@HG#)tps&VVmxnNI^$8wPKI3<8L z!;YUJ%f(#b;^&AbEgB}LBjyA>5`-Mf+2&abP01?H4vt&wbD6MYj591|xzCOR699FMgJ6i34e4>4xMN#!7= z>{9RqEx9Kx+t;=C^(T6JyL)MOn(5_0P%3q%j5ZD+<*WjqL=T|6^@QOegG{L^6J^&) zCEFL@nQ%rgQcBkn1NG-(Vd>JhM}O(Ew})T-HtAQtUHZ|XbWVQLHZ>!xG*{BTeV*Om z^MLL zn8j*R@G59WJ8DMTd+!zr7wH$!y31iaN{u)&I7K7?f^JFPbXZ+AKD zZ7)=s6tbD@(%uF5o4~n!5f)DP#kqNs!7letfOiz}pI@x>C_gjN+XMJ>z$sq>yB_$B zX6KgTy&LiMOO@Ve<+~lx_X6j)_+7q?lOH(FP`)RC*M;~Q%az`!<;#L?THyTIN@IFg zaXE+SCIh{1q5igF7MS)9rN`+i6Tcv1Znrp($v-v%Gthe+c*hX`zST-^wDO@y<_i|* zCHaqMr1vS{{RiR?RGgb$3!=XQoU`~{SjqOtY)AWRz^g_4YxvQPRz6OQ7gY(0X_EiU zc9Fjdl)Di>Z;j%PM(<)oud_He%Rjb*Gt}RGz`Gsszqhz?JZEZ;8Gv7c!MO~-O8jEr zBonqtW|3;#L6X=lJDRQ?hF2r9JhGqD&Nb%dDG&J zR=!EdCuh6nIaU6%GnVfoKv{wK=PmAN{qHH@{R?nDcPsM7ZyXNaWa4)N3Uu8Lyl(2! zywBAh}Tk}0vdo1mN zp$Pow&Q-n}fV17=jaI%EMDR82p?4Xxu$TRLYR39I4!mQCf23dYEyCYuDS$ zovVDe1LwUKZ?y7F0*(8D^LhNfhTmxO(fz=C2JstiXRf2m`)P>l2F@^k58yW%y$68z zMZ{lo2dM@0f~79u*Wh=f#o;p@_0y}ZaZDHhTseDSpsQm6UX{--c`IP6de1JQOn(Kx zZTMYoXp{tp7*=kfeUB}=D(%}MFNgJIJau*R=5ljU5i2uC4inEQ9gpyw()zA#z1@A? z+xtuAHq9$(=;`m;-E~*jo?A<3;|#Ct(X!Bo@H>lNX-ip2Y3J6Sl56qT)lt&~(6X33FhZl1ymdB4 z(^+DYuyzg3;f@tkbp_V4E-lry!8&paOn5e2Uz-q{oB~0TY#8lZfI$%U5EtwoXcl1Z$lfX#27~iEmBpstf|49p0Q#JEoQ9IFj!lq z0pkRXyJ&(^CV=g7nx~>kPRZuo=G_7n^RhJ5IJT%oxPv{twvDyx7ikd)%c6}v+WLev z7+Z8BAkb#Kg)(pWb+V20C4u>}wwmkf>Y)UTE&3xNq(*2i2-9OlHD8xyb+ z3R8tNmMR5h8kmCB7?}N82ctPG0^xWQWZ{)-uCIgzc_yNX`(DWi%+jQ4GmKQDZ2*K(J-g$Z6~2$!0IH8TUI5S@Ipfgz`|@2qdC#SW^2YFb&Z8l*+{I*Urn2nLly!y zW|LTGd6>Vg!@CBJgss_Rq-|juD0D@{cRI;<4OYW4J?#EEtL`y7mc^=jHAX4MG)$K^ zv^JrsoN4l`5DCU&*ITRN&SrQ#jBiS)@p786bT-4sga~5Mel2XEdn@#=FgPfO63}uS z44}f~bp*-T^&T4{MQC|L!p&Hjs}b_X1^`$b`_+K*1GO|LW8@E3>Znv>e)_IAKINSW>A5EDiq8`x5K zYoW21gt25Dk<1mtYHb~c`jr-MN{ELc(gLB%X{({RmbO+QPtiqWf@Gf4BG|N#1_SWY zJs~YQfZf2E9wOGZuD^ag#!a%--2j~tqUrGCNRkERY74wBC|pqS`mkF>B{x%Akmy0m>puMPJn3vvx{S=G!sH=V*R9JmTe}A~q#N(Qv(I-l&L;4X{$axh>Ju)X*e`$I0pyqoOA;i&|t#=re`X zkznSw85Ykg*Eg)Mg~`U+8`ZcwBeQH|byF>dM%`waI~ftT5tuexn>a&4$?%b5Y-CeI zLyMahWQu0WY#Zq#oW=UFHbTZuLZ(#W=Q@1$?Su?Bg2!1fG^?7n>ZXQzDFH_XWAkh* zN~MuR!%)}Qd>hMtIzx>O5Xsx{4`E0NMt2Ww8k-r-EkqNh#}C8&jVx~WMg znyM=??Nb+I475>+@#?oL&q+(SEm6727xGyGn-VQp5j2XDM-n-f1O`QAvoHE{7FEz# z^lX^;zA@3(P)#m9H0GgXliG6>x237Et)WpH>%CH5XP3OKw!RU=y0)dVu@OrS(XU@Y zK3GK7HgY*}eOq;FT^-KPj1~KlP~`xpOlxjNzgS-jVZ`Z;X`sX*Zhc~7BB^92Ho+8m zvqym@2b2a_gik|pIY5mg6)1pMEUm9b;aW_&%>h~*;#!(ov69l6ZH?%Fz^reCW#siO zSVLf{ge;~K#6m%_Lz2X%1iD0BHL86b3S{T_Hd;S#Xl;>^PUNwI+`67-Y`F-7sY@6p zE&?f9!vybZ+Fy zu$0UdrSuuIBph#Uv`NwS0l*58qOo1RWm9c}qENfOxa>k)qv)pFDCWBNY zbNjBQV_ga@nxLJHk=rVElG%!T6~mcVr_I=GR9Y=`NfNjY;kw4C;b875GQH4XThLM~ z3Sy15vfiHoRoL&YZ?2X74y-^h)wgn)om<394j~s>uq<-xT-!1be`?Fx8tQFB#*3sv z$cJZwZS_77X{>H*zH|4w&VKmSz1_Rlx9?W-!8DaCx(mjbhiFPd(VjRMX?E`zMgPwsp^Kd%Ew~BaMM)qgw{(y6i|_1O%*AHZ@g30dHP| zjZACByP>rTJhAt)49dP>u(jXKCQ9Xh93nme+%=m;&T(H3$#j9ujX4o(7Xu2>kF;w0 z_y8!me!jV0nn(t~&sjMtAQ~EFK~v`j0PBVA?HZc}x-U|o=FRn0u&vDA_De*dx77*z zn~=(zrzWRmt{c^0&Oon1JPk363Z~ggt7eptScGUN8?}$IL0BQ6h&rLz<1MCE)Y{!E z#}pg)W55-A>f9)n((?s?n29FOH1keUwDiu2LLgw$340SG%B6g$TVuNt?bf4sUf9dnrO|dP zs7MMMJg^B)San7Tw|bj8 zdv>*N>+D<8-CNVqyMADIXK&ZGwYdIh4=%~#kjZA+)@Wi1!XChDn>*h%(7C6-t9@5< ze{W~|ZmF4D5QCnTSl?PNgYsi2Aeu?VFDrxQ*rhUR1)uYUJlZLPdJmqx>cc9z6iFcV zpl*sJldi{|bgnROghC5-RbUhVtPBWGsSpau8vyhA5PKS{Bgzm5#Ckw$6MF`BhpGf; z3h*Lx8)>a3Qp=q^oprcti@Z?WAwx(!8PC$Nl-xvSFY;Z805&X1NvpE0s81oa9fv{? zD2787gO2oBfY#<3s@LqrcOZez__g?$7WK|6d?<#b``yrhR2((ztDqZnSJ4#hJVy1V zK*nI5bls;w)Jl{Tp~e37do3fRQjK&bc8HYzz!r)DT-(&St-H5FbOt7+efu@g__;~I zLY9yj>Gl*8oB=XU08mX?ITU|uD!uF@Uo9YLHHW2(1&P8fMY0VOsk*JIYfp!WAgPhH z0M<3Sie_Ob+t$jw%FOn%FzatkE`GRN0&WvGPTAX=x}nfNtSQNBk2c zmKLaM+{pgGA@g|n_pdc=Xj3`v>RgbY|v|HKwNG@SGg6`3pgSK4P-QJ-} zuF$Lim1;=t78$#2KP=a@@tBT!jH_=-Gg*KzcWSEGLf+O&OxU4s?yaezdw$k-v-N)$&X8<7I2t)L^#LN4gc`hG5f*BecV z%6e&rR?@Mun4%7cKuud@QYE?BtbFbJT*7ePI=8*tIpwyqUYjD=s%CloBY@S;}jg#vpA zd$m-j>)3p`F90umDWIC7)FcP)%mPzYSiZQBv3ox-LnVsBAXxcIgI$i4wQIZjdfNN9 z?GU-h_k*B?{lfKVY|atF5<90#K=#hBvk;^7PPPoivJ|~e#62}7c?{czy<-+yP~K{@ za>oIyEqzhKDr6f90pE)L8Lnt1_#`>tJWWo2dMQW7V_r3-PlN83?1eAu=Wa@ZYcz`})!6?dCO{Bo<` z3SbVR&jM}*STIX)Kyo@2qS~KWfxmPhEP$8?HzDi}M3+^+7j*2xQ7N=mCUqQeNWwsu zkmsPj*98wnYgZUaNnz@KL`HIyC>|-arZ=@;TavdDf3m_7QO$CGa%9*#=imp z$m43u;hNY`{Yemv^Je^$2kNr2=i_#KUZelogl}YBYm<)iq`$$Wh~9v^Urr(g!UtMn z*+Z?zhZDzFB?^#Ffkz$^1!}{a7wsAzC*n1__d+>6NeMWfMIw$|7-EqV?(Vc zh7-qDC0^Y(oeaN;bSIi@74W|K`Am9jJrn;;kBF{_#Q<%rz|4Sn3h4*AeOAao^u#ikInXGmxPLYm@QgjNcS z#{o6O17W1gJ>X*aS@PXnj!+qEaMB&Gjw;}flbbUNycC_FeS5NmYEW5Bek6b&fql9;U9>)bXCCJtp z?@&Dh(@}oW2_Hq{AH416Bp{r7y0>rdgPji&Jp~XIl+HWs#hnDYyFa%%iy#E@Vx2H2 z$s>91Coet@FWSLTOQ~@DG3#SC>up>4K@&QGUP)YlXm$|2WQQTWWQUljmpUyf;nf3<|jB|nPM?o2+|!P&aJFn}F` zN^rK|jFoPlD;<2C;W22vymq2=o8RKO6hG(gLV(>V>|&%IcClMhx){?ChjaNYEQC0B z;ZjEB)7#9*7z?#m?{>Wz)WnJg+mb~pkv_*sPnwtY&Ys2Q1&GZ{(3SXU(=wz@%aFZz zkN5rK!mYu*uu`%K;&AXzG*trx;ox0ps%m#dBCh1B$)x}tt{Q`DJYS2p+sp&J_@CmK zhI&F&+kUXkQHeaY!L}o$4MyoisYAKMiqFEhXS3D9A7k)^0eIlq<2BLrJI0RW2 z+D;5RLV_znUsvju)WZWrUtHa+miXKZ<#S(d&XwtlMY zU!2nNOhX(}eLmUpDy*vq_K3|Uk;kN}V=Iuy1s-U{JLid{=nduxH}J^1HYh{dpbXi@ zYY`3Pv{f1g_Z`iiaNq}s#?{|L^+$$VkGwzYeTf&?h^KJ||1@HtfzeXK{MOu|)}yd| z@PevEm%={63;SO`355QOVMXDE{ck+WaK4~ChaeaTI+q7uLRBmgk)nE(UYL%)^O; zC^Hl&jdW4NiGycmf|P?RDo2V3B~hO~^D5w&PH;~kC#uA;!-+#g9j-sLs{ZJ{IjCi^ z9C6HPx=;+?oR7J!{t!wUZ#^{BN_{VJXjS59KZ-<6Cqq6@o%sOZJF~$}y#6Sp<*{IJ z4NNvalLV2gZ9lJ6znfS5Hi60x8ayan_sv<*y9>K8X zhn7}J%mWrhf3cb3L;Tifo4}SKL@`rrIMZvW0U-{?Vzbr5*n%4SAVR8s{*{Nl=tD0f zbho6<%5o|4d}tm*8oS!V*7?voeQ28xQM5Qw!qJ!o1(TBnXZJBOab)95G-9lUpKp5M zkC%CPf?T#(D3>i>BI@WA^AcjGnP0)5ZeE5zQ@#wCWq73rD5C6h%?0r1nHlh}FjvB# zFV;J*G#e4V%G?hBYQt}|%FKJdW$S}F@=>H#HONZua60CwXRm?dw0*>9-p~V0Fu&*p0uZ?s z&y?d~S_&t&GaL(k$mbK$rk(U>Z&m5fkV=1s@}wsztPIo?#s!JR^RSP4*e5(JsdKyen-qIa zH(iDhZzD>t9ts>5D|FP7i0f9N*QZnu(-4PLJKt*c)b`yFF^%B0P1oUAUabU%v=SK7 zO4wJJy^mU90#lvne#zMd${}u99{d+AA_E{C@n3A+FS9PO*q{3S@RM@|Oki%bH-5Wkb+W3{iU=EB8v}nkIxZn4vqsJVneg4RJ{INU~O=Vs7UEwC;1PpGuGeObT_{#X8BTEQu_sIB#iHGo)3`kgIAl zkR!@nA!_gYu`^IS1ltj=il@%zm&GQ(y|XUUOy zcdS%H>NsWf(w(cyQny2xhE>M#N}I0g>~_ey&OO&}MG41*$ zSfe#r-P3<(uyT2eTVKJdDB0q6_ILF|?zDaw z()wXY>*pMs%2dS*-8vOQs921v7IRL6A;QUgYMo3aK5KB_G%>6?4GD&d#0+Zzg%&rW z3v>;<7=3HPH*)vCem}D2x(82Hv1|Dx_lSdRp=>izCQ2JY+rH`9$tb2<)Kbu$!mn7a zx6;t3@Z2geHl%gR@p`oXJ=;sLOU}dwr1tc|-xV(FDcFwprIaa3GTyrhL<4iUDe94HnE6}9wr?LEX62;QV5MZB!NH$_2IW{fjS>fq zmZKElop{@N052?0Ba9v1g6&wIj00=AeD2RX0NbT!C-K37#!Yw<+xM@J+c$MMEdAkZ zvO)*-zi#ka(Iol{si8&lXsU;CGW|i4U3h*`VDEyjFz)(Nfy|NBiNXf)ta$J9cn1*Tz{`=hn~KM2UhzKT@%|Jc zR)CsiRlLVM9%X{UzJ(Cg_Z)N3!ww^)^q%*yA0u=DemRm((^6t7-amP~(+E*!<(Pl< zuvZaMJZ@TP+CLz~=EPzi9baMj2q_-l7b|Q!LfQ)4*ism6i)x4)fEwaEWDRXXh^5Vu z>*W--%g65Zp?iJkJ|Fs^4}IK+Xm(bqd>tX}IWK@dbD%QA+RK)A2D!4uH%_(_@~}xp z4jbT)m)Cd~$Zf0_imjeWve9?3=+>9W1V7ar1?F_~&+uoO|AIfu6eGV>9ZO8^bGxG1E^8yrdU9klMZ$V$%1aki4mafVcmGn6L^EL$>P zlvsxH%tP?m&iQgvD?@qaFX3z2XKhI50NNerE=9A`+eLH0p1A1kG$VB(v+J0ElHw{`V!iSF9v6oA?;3~9SCBz3$0O*YS?#JM?v? z+=hmi*X|DxX6L@4`yLcD`go-@ERScF1C zOt8}P1At7Dxo@(V8lP%7M@=)ohCki>4*o1N9(l|*v*2H97Q>%wu7f|%@OrH)WLCIR zHps6s?**jHd;orVMrrtA#IQTEcRM!@0d(|H#|_gEhgAPIb=+XkbU1Ao^`cwFJoMkw zcb7d&OpD-@z!vJUxv|Wy#FO|tEG{)$_V{4r(eN(2%!h&2cUSemu3hp)A5*hG@(APZ z?v5@>R1Q-1_YVT*h3{231^7__ROb%MyK-e)+phA(I0Ztg5Y}6}cgfZhdli#FQWNBc zIU87qIYTd3A_w9Sbs%vBFC|Y|Rr$VycVwR#1lj%jj#D8T7>`48`+osC z=DrvEvqrw}500D~N*uy_)(KDI1YrG*&@hoTmk4J^K8F{oh>6!+GgNj^P zn=fx9uk^!cE#jRkKxz4me(v2I3@Wsz(4q1W#&-!;E*r@YvujcooTNDi@s!cW(Xi_5 z^u*6_I(ysuJ9V7&p_^v2Qt3+TmM&bjcxf7F$)YsS;$>-|CH!n8h1L>mfaypM#wPFb zmREIrcgIgncW>=NQawed<5f^XwWa~Z`-l`$WWzzE{325JtnaVsZ13poh2+!sd7mY8 zZFipqbHuq($DT0O?1j%zzAPsg$`cFERJ-%dr{Ob{XTA!by(r)Oxeeu+g9x$e^JV{u zp*&gO!~{mgh@m`rC!Hm*e*ui4JW~N*^IdC0c`zbJGji(K1BjU;B9&#x@jbwzh^$pE z@D}l0a^j@Q2?z$r1d$f`*qE6_IsmwVU4JS$q-X7T29b(#IB|lQe$<{g!)h5$oPzL> zp?-3>^`sr@Gj@D8NOwGv?)XR-&h<`svZfd|eP`@A7$9=ICNnrTQsIgR%EMK2hwD$q zD;EzXj`}hj9hsggIRb*C$`8KzKcD>I^i4BXIkH7G6HhtjL+~~9Eg$-c4{?H!@C}1eh{1ZYv(UULBT!!*QHPn2|@vQl#N(^GE#9$e6q<^qotrLmC zlwP;WW89v- z@_9UdW&?3sj}BMu4bNKfWdGp4V`d&XYTs18v@d<&Jq(F}gv@`{J z5+4VUF4e5(_ zg2N1tFv)P@k)gy9(1d85@Z^~5@XbQJvXClS>p`3|qe^!O85A^~Z7sw)g=hD=(M_B{ zJ)HOwI8Ac?rMD|c3q(+Gf8~EBNI4W#XtJ|&f2!EL;bA3sR@hP>s`H`u_|O-8=v9Q2 z-fS%MG_(LA6~|W~_J2bhe+5(~%6jTTyRzdnKUMbdrptk|8HNhiOc|iFWjE?lne1`7 zDd2Nt*XeRu^URY2WLFp-u)ETnfq#`5fq%96HT*J@gVL9qeE18^RQQX{9QaGjT=+{f zig;e5y$R*vs>nH~K9UYaK^UJ&#HXr{q)8FaTz{4-;=THq*xS?H+n=sJE=w2b%hLt_ zvUHKYJYDcFTar=AyB!pM7u>@mXPtYKhZi12iF>wd>E6nGz?Wpfe88h;(fNRiB@e^# z>(?HoySA%WR){GUopRoZX^3+ne*cghX-&7ctJR^5 zy`PJzUT8(_VJM^sY<+~GpYvP3=YmW@ythEv;nVJPGsm* z&Jkn>4JDgy0~7ESp%n5P7PdSGrmi0@4Yi3yqZvQ)-)VZDHFd% z!J1*Y&L&b}#E=RjhVrBV>IBB7WhhTn?s|cJ9WaLS%rgkB7ufe~C{H$u_)G~b1I)_hUevp^8XW+kQy8I~bOx<{&zVaoF=-$(rb{O>K-pJ$Q$ zD0}`4zchhg*-|_TpTKnZdgY z^&Qah+kP+?dhg)fxNZTe?N*<1xb;-LB?jqo6d#{J0 z$=qb%A+{Vd4WaG$6`T9vt5Ex)5B(iND%2AizrxS1p)fs#maVai;8o)o4KqgH@6cghE4y!y|- zREX?MEH6vj@?2iFB6a^_dD)UQP7MH6dA2le1p~ISxTQe_G^)Z*v6A+2 zN?Oa63f!m)`W$M5Z46g{B%>B4^W}jF&G#M;<6=``4|~`bJq*l6CIM2^Nr0u!k#3zj36R=(CxK%+2^>zG zL^*M656576l%GdVATiyyWWn}r>nXYB;Kbl^%Dws%`|nh_7u{e6I{1wL+yJ7wzf8(^1$SSv22W4MA>5&u z7p$#E2cN}kaKy{jBO_}er9mZ**Zn6)KJ8>|sh6=%4YXhN`ILu7@?2B~p{rXHY`OCZ z-AZuHRw$N_M^%JA3)lw*b_yYdaT`KI{Ayl96mnzm%P|A+oq)Xxp)5Hc9Je1v)Butz z$6ChOFM%kA#+&!R*FD}M^D%@c%9RVn^8RR&Y%N@Degybrd6#gBIf?KT8HiKmxC<^z zM7|~FLin>}GMFtFEL>`q0dkpao6Iql2w$F2c(x$s8qnmb!8xaik|uj=7@tXaE=$ow zNt42pxxSPXp2=6ER)(gDx$-nsa{0nEg>!lN^0b5t(_TU?FJGEbWV#hn7RuFoD{LAG z77Hnh^=&GtD7>hCrUz5POHf_sl+GNk!*Lkd zy4bbX`tHT9#GcfEOGG%wvQ!?{zXR_dT_aJT=pvjUU4%2Fi*TIKrZn}UwBUEgryi`h z>A4F)?~jE`^6I#p!d(22E@gyu)9in)1K>Y(x1l_ROQsqK2@6|u}hl5duH4*^HQ3p|9wB{UyTE-0;x`M&`U+SHnlF9B3= zy!m+MmrV6UG*w`uloBi+yNf>h`_QT-|H{1CTaxuNR&ahIIPlhLZn68)9OMln3r;KG+G|p z?ra&s@m!ErGSyF`sRAy@e>`?Od|Igf&?`r=nMLHV-SY*9eTO5KE-%vVw*Yefb`QtJ z%OnDqC%o4o#1dS-j4L6p9?J!(3y$Z4 zw34Y#M^gnYI1za9SPT3t^s*m3@`VNI{g6szJoYvE_|Z*_v>@k<^S59)PXEM~XCioy z$L>LTmf*rlc;Z6dSwiKVay;o=3vz4-j^~}UlBu+Jx^!U+76TZMQ9@EbTR1T7ed#SY zNpQYOpJ;H!5#ApHND}FJt4e`{)ZEztVN{&EH!tE)p(2j(B3L#d;!# zTre-?qB&X~+k2@;2#)81w34ZAh^7j-m<+slYzBPV{r%o2>YzlhVBr{~ejJY-p^qQk zt~P@cwyFWC<#r(h=dEB~;!qjR(%PAj>2;o_Eqpruyq34fqOp zpMscpjJc1&NPpwGm$s$zK2>nIE$(=q;#-ghC+MaK7mp!CS~=B((S%LC3%Ouk%EhlG&UuatQWqT01!*Ny{l+Db|Bwr|Sv)oZe+(qgckcb}v2-qG3Ql3r z7Bf5-y!nZ4hHycxfwcajmN2@I3+AO0HbboD=Y!PEz8zc(3K6M7a0|;E-0U-3h`Y+!GduVGaIO8srxdNZant z-Ml_j9b_hIqp#0C%G>>TY_5Bhf0z6<8<-6!*118dO9_cl?j)T{wZe8X!HLv-36Qns zXM(S!b2ZPpDZwwP^OJvp;NO^ciq8;TSo5WLj>kF#jtlq!NGJcE*BiWc6MCM@mzewg60Sp{{c+W`eX|N;@bN9d$+ZRJwYJ2-&LLVjqUbH z_g$6KzG#^(rySiZbF#gdpFjlvPot>!=8#k_amlf|dfIh`jscLRqSFx_0w z>6b01a}C>T9B@eMR~r>C`GLV{NyT-ed~$&A3;sC@!RG}j5PV(mixiwC0fad`=c*oI z?e#xRgPisdsny;NzO)i|1$^*s)84=0ia%1mN?Z6*#EK+Nq}Q>=v@O02PLs}!9nScI z<9i)zB$?_b(Nwy7P)$1X@r%be1345ePdL7=6r55ZmFOGiFTE7KJ#7XPm-1ZxIVqUhrmM*nlv?L)5(;&;_EOMBtEP#-F(HQ#b zApL^2SO8$OEws$i7P$JJPH-Y^!MwCBj)RM&b8SJLS8#k=klSRc|BR*z+Tv=YjJAcA zCv1x{!QtV_iTD!e(x>l3#gkwt>!|6+V{O3U+Jmmlx5a#08)b%Vu^357>u2qXXU7gt zR|3ksY+K-PB~QTx5S*<{f#7H*&%cE@O9Dt(^9w;aTJu^$srh9(3Ir!o^Q5dbA4fh( z=W6~^fCR_aJb6o|S`keZ)chi(jMluCC#-q)hj@&w%UzjAZdinxB*C!em*6=b`v`sf z=ob5$ueLS67&RY{@r@Q~P4xmd)KZw2a{4;(lFo6;nM-gyr=*ok^@nJxK=v*LUOe^` z{Bd|^*uUz{->3JVWrD+F?`|wF^<3O$xmar0fB1fmwBEcGo;VrHI05z{JTWiZe{cZo z{uIc)O2qC(6__lc3w#7RPN5Q?oA+3eIilGax22+jla@uOSo`{9eWAFdTH9t0Pp^}y|d29K-1ZPAgt)lWEbnoIe5 z$R&{fkS~@nso|5lK#-G>^CkROXXn8dK2f!FdxILQ{y(C;ZF+h`jc8OXch9^VmnM07 z2jn*?Cv)7FOjUiqPJqPMrZw_>qby}ImN_rq3HY^Y3BG*%|AtGLloxS^}J zA1|L9d%OE_*scNxIxOkxT?2hPf?U_MZ`;usWQ|jLYX*9IVIdJ|VPvtdq73<$pFjUi z$lIP$u@}aJ)77gNRcx-@)L6L*MO(dkAs!H}<%7Paj^43cf%qCQQdUNc$gQ^(=Pg0a z^-N^%f_)3 -#include // Required for function hooking +#include "detours.h" // Required for function hooking #define RAWINPUTHDRSIZE sizeof(RAWINPUTHEADER) #define RAWPTRSIZE 40 @@ -12,6 +12,8 @@ #pragma warning(disable: 4100) /** + * Note from original author (abort): + * ---------------------------------- * Sadly everything has been made static, as Win32 API does not support object oriented callbacks, as it has been written in C. * To keep the performance as high as possible, I decided not to work with storing the class instance through Win32 API. * Feel free to rewrite this to something more clean in coding terms :). @@ -44,6 +46,8 @@ class CRawInput { private: static long x; static long y; + static long set_x; + static long set_y; static HWND hwndInput; static bool bRegistered; From caa2270ca48d69b864bcc44645234bcf3e5f0077 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 5 Oct 2015 13:35:15 -0400 Subject: [PATCH 03/57] added project file --- RInput.vcproj | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 RInput.vcproj diff --git a/RInput.vcproj b/RInput.vcproj new file mode 100644 index 0000000..040ab20 --- /dev/null +++ b/RInput.vcproj @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From cb62606c2f07456ef8840a361502e285d95c4ae6 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 5 Oct 2015 13:51:13 -0400 Subject: [PATCH 04/57] added .gitignore along with a bunch of project files that are not to be tracked --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fda10bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +RInput.ncb +RInput.sln +RInput.suo +RInput.vcproj.* +Release/ \ No newline at end of file From 1ed7fa8d5ba3dfbfedc164b94f6ae27fc5eb2e9c Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 5 Oct 2015 14:03:03 -0400 Subject: [PATCH 05/57] updated .gitconfig and .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fda10bb..f444e8b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ RInput.ncb RInput.sln RInput.suo RInput.vcproj.* -Release/ \ No newline at end of file +Release/ +.gitconfig \ No newline at end of file From 87373a89cb0a868455693a641322e971c64175f9 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 7 Oct 2015 04:41:52 -0400 Subject: [PATCH 06/57] actually fixed TF2 backpack this time, made input data handling less confusing --- README | 2 +- README.mdown | 2 +- rawinput.cpp | 27 +++++++++++++++++---------- rawinput.h | 8 ++++++++ versioninfo.h | 4 ++-- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/README b/README index e9cab48..50678f0 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.32 by Vols and Jezuz +RInput Library v1.33 by Vols and Jezuz -------------------------------------- RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. diff --git a/README.mdown b/README.mdown index 43f5b0a..e4f9ad8 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.32 by Vols and Jezuz +# RInput Library v1.33 by Vols and Jezuz RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. ## Note diff --git a/rawinput.cpp b/rawinput.cpp index 9cf720e..f4f4fd3 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -42,6 +42,7 @@ long CRawInput::x = 0; long CRawInput::y = 0; long CRawInput::set_x = 0; long CRawInput::set_y = 0; +bool CRawInput::s = false; bool CRawInput::initialize(WCHAR* pwszError) { @@ -91,15 +92,16 @@ bool CRawInput::initWindow(WCHAR* pwszError) bool CRawInput::initInput(WCHAR* pwszError) { // Set default coordinates + CRawInput::x = CRawInput::y = CRawInput::set_x = CRawInput::set_y = 0; + HWND hwndGame = GetForegroundWindow(); LPPOINT defCor = new tagPOINT; GetPhysicalCursorPos(defCor); - PhysicalToLogicalPoint(hwndInput, defCor); - CRawInput::x = defCor->x; - CRawInput::y = defCor->y; + PhysicalToLogicalPoint(hwndGame, defCor); + CRawInput::set_x = defCor->x; + CRawInput::set_y = defCor->y; RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); - rMouse.dwFlags = 0; rMouse.hwndTarget = CRawInput::hwndInput; rMouse.usUsagePage = 0x01; @@ -163,6 +165,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) CRawInput::set_x = (long)x; CRawInput::set_y = (long)y; + CRawInput::s = true; #ifdef _DEBUG OutputDebugString("Set coordinates"); @@ -173,11 +176,15 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { - lpPoint->x = CRawInput::x; - lpPoint->y = CRawInput::y; - - CRawInput::x = CRawInput::set_x; - CRawInput::y = CRawInput::set_y; + lpPoint->x = CRawInput::set_x + CRawInput::x; + lpPoint->y = CRawInput::set_y + CRawInput::y; + + if (CRawInput::s) + { + CRawInput::x = 0; + CRawInput::y = 0; + CRawInput::s = false; + } #ifdef _DEBUG OutputDebugString("Returned coordinates"); @@ -230,7 +237,7 @@ void CRawInput::unload() RegisterRawInputDevices(&rMouse, 1, sizeof(RAWINPUTDEVICE)); DestroyWindow(hwndInput); - + #ifdef _DEBUG OutputDebugString("Unregistered mouse device"); OutputDebugString("Closed Input Window"); diff --git a/rawinput.h b/rawinput.h index c7830e6..8dab7a1 100644 --- a/rawinput.h +++ b/rawinput.h @@ -48,6 +48,14 @@ class CRawInput { static long y; static long set_x; static long set_y; + static bool s; + //static int arrCRawInputx [1000]; + //static int arrCRawInputy [1000]; + //static int arrIteration; + //static bool started; + + //static int SCPDisagree; + //static int GCPDisagree; static HWND hwndInput; static bool bRegistered; diff --git a/versioninfo.h b/versioninfo.h index 4bff7d6..619dd81 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.32" -#define RINPUTFVER 1,32 +#define RINPUTVER "v1.33" +#define RINPUTFVER 1,33 #define IDI_ICON 101 #define RINPUTINT "RInput" From 80da984a5e8431522481ed8708db389ef84d6d0a Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 7 Oct 2015 04:52:22 -0400 Subject: [PATCH 07/57] removed commented out lines used in testing --- rawinput.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/rawinput.h b/rawinput.h index 8dab7a1..79283cc 100644 --- a/rawinput.h +++ b/rawinput.h @@ -49,13 +49,6 @@ class CRawInput { static long set_x; static long set_y; static bool s; - //static int arrCRawInputx [1000]; - //static int arrCRawInputy [1000]; - //static int arrIteration; - //static bool started; - - //static int SCPDisagree; - //static int GCPDisagree; static HWND hwndInput; static bool bRegistered; From 61cf8101dfa41f4a39cbc642ecb98e1e6459014d Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 7 Oct 2015 04:54:16 -0400 Subject: [PATCH 08/57] readded blankspace for code clarity that was accidentally removed --- rawinput.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rawinput.cpp b/rawinput.cpp index f4f4fd3..8bd76f4 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -102,6 +102,7 @@ bool CRawInput::initInput(WCHAR* pwszError) RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); + rMouse.dwFlags = 0; rMouse.hwndTarget = CRawInput::hwndInput; rMouse.usUsagePage = 0x01; From ef8b3ca1a60e0e460fef75fae1d4651c05b2e272 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 7 Oct 2015 04:56:16 -0400 Subject: [PATCH 09/57] removed accidentally added tab blankspace --- rawinput.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rawinput.cpp b/rawinput.cpp index 8bd76f4..96618ef 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -238,7 +238,7 @@ void CRawInput::unload() RegisterRawInputDevices(&rMouse, 1, sizeof(RAWINPUTDEVICE)); DestroyWindow(hwndInput); - + #ifdef _DEBUG OutputDebugString("Unregistered mouse device"); OutputDebugString("Closed Input Window"); From 5cfb65443fa5b5b010d4fad4ed4cc2f4f8807f59 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 7 Oct 2015 05:19:28 -0400 Subject: [PATCH 10/57] removed unnecessary whitespace tabs and added trailing newlines to text files --- .gitignore | 2 +- README | 2 +- README.mdown | 2 +- rawinput.cpp | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index f444e8b..5b7fde4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ RInput.sln RInput.suo RInput.vcproj.* Release/ -.gitconfig \ No newline at end of file +.gitconfig diff --git a/README b/README index 50678f0..515d467 100644 --- a/README +++ b/README @@ -35,4 +35,4 @@ Credits: BuSheeZy - helping me understand Visual C++ IDE and Git qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works about - original author -Dr3am - for the icons \ No newline at end of file +Dr3am - for the icons diff --git a/README.mdown b/README.mdown index e4f9ad8..a20e15a 100644 --- a/README.mdown +++ b/README.mdown @@ -30,4 +30,4 @@ Summary of the process by the original author: BuSheeZy - helping me understand Visual C++ IDE and Git qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works about - original author -Dr3am - for the icons \ No newline at end of file +Dr3am - for the icons diff --git a/rawinput.cpp b/rawinput.cpp index 96618ef..80ed711 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -70,7 +70,7 @@ bool CRawInput::initWindow(WCHAR* pwszError) lstrcpyW(pwszError, L"Failed to register input window!"); return false; } - + // Create the window to catch WM_INPUT events CRawInput::hwndInput = CreateWindowEx(NULL, INPUTWINDOW, INPUTWINDOW, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); if (!CRawInput::hwndInput) @@ -99,10 +99,10 @@ bool CRawInput::initInput(WCHAR* pwszError) PhysicalToLogicalPoint(hwndGame, defCor); CRawInput::set_x = defCor->x; CRawInput::set_y = defCor->y; - + RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); - + rMouse.dwFlags = 0; rMouse.hwndTarget = CRawInput::hwndInput; rMouse.usUsagePage = 0x01; @@ -179,7 +179,7 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { lpPoint->x = CRawInput::set_x + CRawInput::x; lpPoint->y = CRawInput::set_y + CRawInput::y; - + if (CRawInput::s) { CRawInput::x = 0; From 13c60c81111ae3b114c5c3f12e9aa864c5051c81 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 12 Oct 2015 17:04:25 -0400 Subject: [PATCH 11/57] changed unnecessary functions to get initial cursor position to GetCursorPos --- rawinput.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rawinput.cpp b/rawinput.cpp index 80ed711..65397fb 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -93,10 +93,8 @@ bool CRawInput::initInput(WCHAR* pwszError) { // Set default coordinates CRawInput::x = CRawInput::y = CRawInput::set_x = CRawInput::set_y = 0; - HWND hwndGame = GetForegroundWindow(); LPPOINT defCor = new tagPOINT; - GetPhysicalCursorPos(defCor); - PhysicalToLogicalPoint(hwndGame, defCor); + GetCursorPos(defCor); CRawInput::set_x = defCor->x; CRawInput::set_y = defCor->y; @@ -177,6 +175,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { + lpPoint->x = CRawInput::set_x + CRawInput::x; lpPoint->y = CRawInput::set_y + CRawInput::y; From 0ed86b4f1379618cbaadfa5a7d0cd86f47baf140 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 12 Oct 2015 19:32:19 -0400 Subject: [PATCH 12/57] minor formatting consistency --- RInput.vcproj | 5 +++-- rawinput.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/RInput.vcproj b/RInput.vcproj index 040ab20..2196233 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -111,6 +111,7 @@ OutputDirectory="Release" IntermediateDirectory="Release" ConfigurationType="2" + UseOfMFC="0" CharacterSet="2" ManagedExtensions="0" WholeProgramOptimization="1" @@ -139,7 +140,7 @@ OmitFramePointers="true" WholeProgramOptimization="true" AdditionalIncludeDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include"" - PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;RINPUT_EXPORTS;" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;RINPUT_EXPORTS" ExceptionHandling="1" RuntimeLibrary="2" BufferSecurityCheck="false" @@ -264,4 +265,4 @@ - + \ No newline at end of file diff --git a/rawinput.cpp b/rawinput.cpp index 65397fb..702f27b 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -44,7 +44,7 @@ long CRawInput::set_x = 0; long CRawInput::set_y = 0; bool CRawInput::s = false; -bool CRawInput::initialize(WCHAR* pwszError) +bool CRawInput::initialize(WCHAR* pwszError) { if (!initWindow(pwszError)) return false; @@ -55,7 +55,7 @@ bool CRawInput::initialize(WCHAR* pwszError) return true; } -bool CRawInput::initWindow(WCHAR* pwszError) +bool CRawInput::initWindow(WCHAR* pwszError) { // Register the window to catch WM_INPUT events WNDCLASSEX wcex; @@ -89,7 +89,7 @@ bool CRawInput::initWindow(WCHAR* pwszError) return true; } -bool CRawInput::initInput(WCHAR* pwszError) +bool CRawInput::initInput(WCHAR* pwszError) { // Set default coordinates CRawInput::x = CRawInput::y = CRawInput::set_x = CRawInput::set_y = 0; @@ -115,7 +115,7 @@ bool CRawInput::initInput(WCHAR* pwszError) return (bRegistered = true); } -unsigned int CRawInput::pollInput() +unsigned int CRawInput::pollInput() { MSG msg; From 0bec3f11f626dd1061a6eda93ea9b36805e9072d Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 13 Oct 2015 19:40:17 -0400 Subject: [PATCH 13/57] discovered a few more compiler optimizations and tweaked other settings to maximize compatibility --- RInput.vcproj | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/RInput.vcproj b/RInput.vcproj index 2196233..9122876 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -135,20 +135,30 @@ - \ No newline at end of file + From 79fad81fdff013dcb7a154e76b0bab0353598589 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 14 Oct 2015 05:12:20 -0400 Subject: [PATCH 14/57] better yet compiler optimizations and compatibility settings --- RInput.vcproj | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/RInput.vcproj b/RInput.vcproj index 9122876..ef65bef 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -135,11 +135,12 @@ Date: Wed, 14 Oct 2015 15:41:48 -0400 Subject: [PATCH 15/57] finalized best optimization choices in project file, added notation to changed section of source files, extended/updated documentation, and properly implemented the resource script after changing from VC++2008 Express Edition to Professional --- .gitignore | 2 ++ README | 21 ++++++++++++++------- README.mdown | 18 ++++++++++++------ RInput.vcproj | 6 +++--- rawinput.cpp | 3 +++ rawinput.h | 4 ++++ versioninfo.h | 4 ++-- 7 files changed, 40 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 5b7fde4..863e3a2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ RInput.sln RInput.suo RInput.vcproj.* Release/ +versioninfo.aps .gitconfig +.gitignore diff --git a/README b/README index 515d467..5d3ddbf 100644 --- a/README +++ b/README @@ -1,7 +1,13 @@ -RInput Library v1.33 by Vols and Jezuz +RInput Library v1.34 by Vols and Jezuz -------------------------------------- RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. +Requirements: +-------------------------------------- +- OS: Windows XP or later +- CPU: Intel Pentium 4 or later; or, AMD Opteron, Athlon 64 or later +- Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34) + Note: -------------------------------------- This is a fork of RInput Library v1.31 as originally authored by abort. x64 architecture support was never added by him, but here are some notes on what would need to be changed to accomplish this, in case someone is interested: @@ -11,11 +17,12 @@ This is a fork of RInput Library v1.31 as originally authored by abort. x64 arch Building: -------------------------------------- -Compiled with Visual C++ 2008 Express Edition, though you may be able to get it to work with other versions or compilers (available for free @ https://go.microsoft.com/?linkid=7729279); +Compiled with Visual Studio 2008, though you may be able to get it to work with other versions or compilers (Visual C++ 2008 Express Edition is available for free @ https://go.microsoft.com/?linkid=7729279); - Pick Dynamic Library (.dll) as the configuration type - Compile using Multi-Byte Character Set -- Add the Microsoft SDK (I used v6.0A that came with VC++2008) folders as additional Include directory and additional linker library directory +- Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory - Compiled with Multi-threaded DLL runtime library for linking +- Set the Entry Point as DllMain - Use x86 architecture for compiling (i.e., RInput does not support for x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar @@ -32,7 +39,7 @@ Summary of the process by the original author: Credits: -------------------------------------- -BuSheeZy - helping me understand Visual C++ IDE and Git -qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works -about - original author -Dr3am - for the icons +- BuSheeZy - helping me understand Visual C++ IDE and Git +- qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works +- about - original author +- Dr3am - for the icons diff --git a/README.mdown b/README.mdown index a20e15a..12130d0 100644 --- a/README.mdown +++ b/README.mdown @@ -1,6 +1,11 @@ -# RInput Library v1.33 by Vols and Jezuz +# RInput Library v1.34 by Vols and Jezuz RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. +## Requirements +- OS: Windows XP or later +- CPU: Intel Pentium III or later; or, AMD Opteron, Athlon 64 or later +- Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34) + ## Note This is a fork of RInput Library v1.31 as originally authored by abort. x64 architecture support was never added by him, but here are some notes on what would need to be changed to accomplish this, in case someone is interested: - The detouring/hooking mechanism needs to support the x64 architectures. This is mainly due to the jumping mechanism for larger pointers. The jumps need to support the whole memory address span over 32-bit @@ -11,8 +16,9 @@ This is a fork of RInput Library v1.31 as originally authored by abort. x64 arch Compiled with Visual C++ 2008 Express Edition, though you may be able to get it to work with other versions or compilers (available for free @ https://go.microsoft.com/?linkid=7729279); - Pick Dynamic Library (.dll) as the configuration type - Compile using Multi-Byte Character Set -- Add the Microsoft SDK (I used v6.0A that came with VC++2008) folders as additional Include directory and additional linker library directory +- Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory - Compiled with Multi-threaded DLL runtime library for linking +- Set the Entry Point as DllMain - Use x86 architecture for compiling (i.e., RInput does not support for x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar @@ -27,7 +33,7 @@ Summary of the process by the original author: 6. The injector gives the user feedback, based on whether the event was raised or not ## Credits: -BuSheeZy - helping me understand Visual C++ IDE and Git -qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works -about - original author -Dr3am - for the icons +- BuSheeZy - helping me understand Visual C++ IDE and Git +- qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works +- about - original author +- Dr3am - for the icons diff --git a/RInput.vcproj b/RInput.vcproj index ef65bef..116c368 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -135,7 +135,7 @@ x; @@ -179,6 +181,7 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) lpPoint->x = CRawInput::set_x + CRawInput::x; lpPoint->y = CRawInput::set_y + CRawInput::y; + // raw input data accumulator resets have moved here from hSetCursorPos, so raw input data occurring between GetCursorPos/SetCursorPos paired calls is not lost if (CRawInput::s) { CRawInput::x = 0; diff --git a/rawinput.h b/rawinput.h index 79283cc..ec69fa4 100644 --- a/rawinput.h +++ b/rawinput.h @@ -46,8 +46,12 @@ class CRawInput { private: static long x; static long y; + + // mouse data handling is now split between set_x/y (stores the SetCursorPos starting point) and x/y (accumulates the raw input data), which add together as the next GetCursorPos offset static long set_x; static long set_y; + + // toggle so raw input data is not reset when SetCursorPos isn't called after GetCursorPos, which fixes the odd behavior RInput used to have in TF2's backpack and some game menus static bool s; static HWND hwndInput; diff --git a/versioninfo.h b/versioninfo.h index 619dd81..189d679 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.33" -#define RINPUTFVER 1,33 +#define RINPUTVER "v1.34" +#define RINPUTFVER 1,34 #define IDI_ICON 101 #define RINPUTINT "RInput" From c8f3a9581126b104b4e3adde5de458cafad6b116 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 14 Oct 2015 15:47:17 -0400 Subject: [PATCH 16/57] updated to reflect new requirements due to compilation changes and state different IDE version --- README.mdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.mdown b/README.mdown index 12130d0..8a50bf6 100644 --- a/README.mdown +++ b/README.mdown @@ -3,7 +3,7 @@ RInput allows you to override low definition windows mouse input (accurate until ## Requirements - OS: Windows XP or later -- CPU: Intel Pentium III or later; or, AMD Opteron, Athlon 64 or later +- CPU: Intel Pentium 4 or later; or, AMD Opteron, Athlon 64 or later - Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34) ## Note From d7b703bfb4274216d2ebfe34fc95fb6a385ccce0 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 14 Oct 2015 16:29:13 -0400 Subject: [PATCH 17/57] updated to reflect different IDE version used for compiling --- README.mdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.mdown b/README.mdown index 8a50bf6..c81e68a 100644 --- a/README.mdown +++ b/README.mdown @@ -13,7 +13,7 @@ This is a fork of RInput Library v1.31 as originally authored by abort. x64 arch - This library obviously has to get recompiled to 64-bit (might require some changes to Win32 API calls for the RInput HWND) ## Building -Compiled with Visual C++ 2008 Express Edition, though you may be able to get it to work with other versions or compilers (available for free @ https://go.microsoft.com/?linkid=7729279); +Compiled with Visual Studio 2008, though you may be able to get it to work with other versions or compilers (Visual C++ 2008 Express Edition is available for free @ https://go.microsoft.com/?linkid=7729279); - Pick Dynamic Library (.dll) as the configuration type - Compile using Multi-Byte Character Set - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory From 7d0da64058aa080329dade3c09a1b55fabbdb4de Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 19 Oct 2015 05:02:37 -0400 Subject: [PATCH 18/57] completely fixed mouse data packet handling to be equivalent to m_rawinput 1 --- README | 2 +- README.mdown | 2 +- rawinput.cpp | 16 ++++++---------- rawinput.h | 3 --- versioninfo.h | 4 ++-- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/README b/README index 5d3ddbf..a52b22a 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.34 by Vols and Jezuz +RInput Library v1.35 by Vols and Jezuz -------------------------------------- RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. diff --git a/README.mdown b/README.mdown index c81e68a..31a1706 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.34 by Vols and Jezuz +# RInput Library v1.35 by Vols and Jezuz RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. ## Requirements diff --git a/rawinput.cpp b/rawinput.cpp index 68fb2d8..27fb428 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -42,7 +42,6 @@ long CRawInput::x = 0; long CRawInput::y = 0; long CRawInput::set_x = 0; long CRawInput::set_y = 0; -bool CRawInput::s = false; bool CRawInput::initialize(WCHAR* pwszError) { @@ -166,7 +165,6 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) CRawInput::set_x = (long)x; CRawInput::set_y = (long)y; - CRawInput::s = true; #ifdef _DEBUG OutputDebugString("Set coordinates"); @@ -178,16 +176,14 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { - lpPoint->x = CRawInput::set_x + CRawInput::x; - lpPoint->y = CRawInput::set_y + CRawInput::y; + CRawInput::set_x += CRawInput::x; + CRawInput::set_y += CRawInput::y; + lpPoint->x = CRawInput::set_x; + lpPoint->y = CRawInput::set_y; // raw input data accumulator resets have moved here from hSetCursorPos, so raw input data occurring between GetCursorPos/SetCursorPos paired calls is not lost - if (CRawInput::s) - { - CRawInput::x = 0; - CRawInput::y = 0; - CRawInput::s = false; - } + CRawInput::x = 0; + CRawInput::y = 0; #ifdef _DEBUG OutputDebugString("Returned coordinates"); diff --git a/rawinput.h b/rawinput.h index ec69fa4..a760e0e 100644 --- a/rawinput.h +++ b/rawinput.h @@ -50,9 +50,6 @@ class CRawInput { // mouse data handling is now split between set_x/y (stores the SetCursorPos starting point) and x/y (accumulates the raw input data), which add together as the next GetCursorPos offset static long set_x; static long set_y; - - // toggle so raw input data is not reset when SetCursorPos isn't called after GetCursorPos, which fixes the odd behavior RInput used to have in TF2's backpack and some game menus - static bool s; static HWND hwndInput; static bool bRegistered; diff --git a/versioninfo.h b/versioninfo.h index 189d679..e363578 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.34" -#define RINPUTFVER 1,34 +#define RINPUTVER "v1.35" +#define RINPUTFVER 1,35 #define IDI_ICON 101 #define RINPUTINT "RInput" From 566a6dfd6fa317ffc0940b148747ea4d3e87032c Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 21 Oct 2015 22:36:15 -0400 Subject: [PATCH 19/57] Updated compiler options for debug configuration --- RInput.vcproj | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/RInput.vcproj b/RInput.vcproj index 116c368..5091f8f 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -43,15 +43,20 @@ Name="VCCLCompilerTool" UseUnicodeResponseFiles="false" Optimization="2" + InlineFunctionExpansion="2" EnableIntrinsicFunctions="true" FavorSizeOrSpeed="1" OmitFramePointers="true" AdditionalIncludeDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include"" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;RINPUT_EXPORTS;" + StringPooling="true" MinimalRebuild="false" + ExceptionHandling="0" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" + EnableFunctionLevelLinking="true" + ForceConformanceInForLoopScope="false" UsePrecompiledHeader="0" WarningLevel="3" Detect64BitPortabilityProblems="false" @@ -62,7 +67,7 @@ /> Date: Mon, 26 Oct 2015 19:50:43 -0400 Subject: [PATCH 20/57] Fixed bugs related to in-game console and alt-tabbing --- RInput.vcproj | 2 ++ rawinput.cpp | 39 +++++++++++++++++++++++++++++++++------ rawinput.h | 6 ++++-- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/RInput.vcproj b/RInput.vcproj index 5091f8f..1f91c56 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -80,6 +80,7 @@ AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib"" EnableUAC="true" UACExecutionLevel="0" + IgnoreDefaultLibraryNames="" GenerateDebugInformation="true" SubSystem="2" LargeAddressAware="2" @@ -187,6 +188,7 @@ GenerateManifest="true" EnableUAC="true" UACExecutionLevel="0" + IgnoreDefaultLibraryNames="" GenerateDebugInformation="false" SubSystem="2" LargeAddressAware="2" diff --git a/rawinput.cpp b/rawinput.cpp index 27fb428..475ffdc 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -31,9 +31,9 @@ #include "rawinput.h" -// define the to be hooked functions +// Define the to be hooked functions extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); -extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpSetCursorPos(int X, int Y), SetCursorPos); +extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpSetCursorPos(int x, int y), SetCursorPos); // Initialize static variables bool CRawInput::bRegistered = false; @@ -42,6 +42,9 @@ long CRawInput::x = 0; long CRawInput::y = 0; long CRawInput::set_x = 0; long CRawInput::set_y = 0; +long CRawInput::hold_x = 0; +long CRawInput::hold_y = 0; +int CRawInput::SCP = 0; bool CRawInput::initialize(WCHAR* pwszError) { @@ -92,8 +95,8 @@ bool CRawInput::initInput(WCHAR* pwszError) { // Set default coordinates CRawInput::x = CRawInput::y = CRawInput::set_x = CRawInput::set_y = 0; - - // Update from v1.31, with the initial raw input data accumulators set to the starting cursor position + + // Raw input accumulators initialized to starting cursor position LPPOINT defCor = new tagPOINT; GetCursorPos(defCor); CRawInput::set_x = defCor->x; @@ -102,7 +105,8 @@ bool CRawInput::initInput(WCHAR* pwszError) RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); - rMouse.dwFlags = 0; + // New flag allows accumulation to be maintained while alt-tabbed + rMouse.dwFlags = RIDEV_INPUTSINK; rMouse.hwndTarget = CRawInput::hwndInput; rMouse.usUsagePage = 0x01; rMouse.usUsage = 0x02; @@ -166,6 +170,18 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) CRawInput::set_x = (long)x; CRawInput::set_y = (long)y; + ++CRawInput::SCP; + CRawInput::set_x -= CRawInput::x; + CRawInput::set_y -= CRawInput::y; + if (CRawInput::SCP == 2) + { + CRawInput::SCP = 0; + CRawInput::set_x += CRawInput::x; + CRawInput::set_y += CRawInput::y; + CRawInput::hold_x = CRawInput::set_x; + CRawInput::hold_y = CRawInput::set_y; + } + #ifdef _DEBUG OutputDebugString("Set coordinates"); #endif @@ -175,13 +191,24 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { + CRawInput::SCP = 0; + // Split off raw input handling to accumulate independently CRawInput::set_x += CRawInput::x; CRawInput::set_y += CRawInput::y; + if ((CRawInput::set_x == 0) && (CRawInput::set_y == 0)) + { + if ((CRawInput::x == 0) && (CRawInput::y == 0)) + { + lpPoint->x = CRawInput::hold_x; + lpPoint->y = CRawInput::hold_y; + return 0; + } + } lpPoint->x = CRawInput::set_x; lpPoint->y = CRawInput::set_y; - // raw input data accumulator resets have moved here from hSetCursorPos, so raw input data occurring between GetCursorPos/SetCursorPos paired calls is not lost + // Raw input accumulation resets moved here from hSetCursorPos CRawInput::x = 0; CRawInput::y = 0; diff --git a/rawinput.h b/rawinput.h index a760e0e..4668ab6 100644 --- a/rawinput.h +++ b/rawinput.h @@ -29,6 +29,7 @@ class CRawInput { // Hooked functions handling static int __stdcall hGetCursorPos(LPPOINT lpPoint); static int __stdcall hSetCursorPos(int x, int y); + static int __stdcall hQueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount); // Poll Input static unsigned int pollInput(); @@ -46,10 +47,11 @@ class CRawInput { private: static long x; static long y; - - // mouse data handling is now split between set_x/y (stores the SetCursorPos starting point) and x/y (accumulates the raw input data), which add together as the next GetCursorPos offset static long set_x; static long set_y; + static long hold_x; + static long hold_y; + static int SCP; static HWND hwndInput; static bool bRegistered; From 73904f787085483ea5774d1d3d0c0d3efdfa92ae Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 26 Oct 2015 20:25:58 -0400 Subject: [PATCH 21/57] Fixed alt-tab bug-fix not working if mouse moving --- rawinput.cpp | 23 ++++++++++++++--------- rawinput.h | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/rawinput.cpp b/rawinput.cpp index 475ffdc..27c3856 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -45,6 +45,7 @@ long CRawInput::set_y = 0; long CRawInput::hold_x = 0; long CRawInput::hold_y = 0; int CRawInput::SCP = 0; +bool CRawInput::GCP = false; bool CRawInput::initialize(WCHAR* pwszError) { @@ -170,11 +171,15 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) CRawInput::set_x = (long)x; CRawInput::set_y = (long)y; + if ((CRawInput::set_x == 0) && (CRawInput::set_y == 0)) + CRawInput::GCP = true; + ++CRawInput::SCP; CRawInput::set_x -= CRawInput::x; CRawInput::set_y -= CRawInput::y; if (CRawInput::SCP == 2) { + CRawInput::GCP = false; CRawInput::SCP = 0; CRawInput::set_x += CRawInput::x; CRawInput::set_y += CRawInput::y; @@ -196,17 +201,17 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) // Split off raw input handling to accumulate independently CRawInput::set_x += CRawInput::x; CRawInput::set_y += CRawInput::y; - if ((CRawInput::set_x == 0) && (CRawInput::set_y == 0)) + + if (!CRawInput::GCP) { - if ((CRawInput::x == 0) && (CRawInput::y == 0)) - { - lpPoint->x = CRawInput::hold_x; - lpPoint->y = CRawInput::hold_y; - return 0; - } + lpPoint->x = CRawInput::set_x; + lpPoint->y = CRawInput::set_y; + } + else + { + lpPoint->x = CRawInput::hold_x; + lpPoint->y = CRawInput::hold_y; } - lpPoint->x = CRawInput::set_x; - lpPoint->y = CRawInput::set_y; // Raw input accumulation resets moved here from hSetCursorPos CRawInput::x = 0; diff --git a/rawinput.h b/rawinput.h index 4668ab6..23e8be7 100644 --- a/rawinput.h +++ b/rawinput.h @@ -52,6 +52,7 @@ class CRawInput { static long hold_x; static long hold_y; static int SCP; + static bool GCP; static HWND hwndInput; static bool bRegistered; From 64411a48b167644544966f2934b3c165dbbea02a Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 27 Oct 2015 02:36:33 -0400 Subject: [PATCH 22/57] RInput now detects source games and only applies certain bug fixes to them --- RInput.vcproj | 6 ++++-- rawinput.cpp | 52 +++++++++++++++++++++++++++++++++++---------------- rawinput.h | 3 +++ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/RInput.vcproj b/RInput.vcproj index 1f91c56..73ce7f4 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -51,7 +51,7 @@ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;RINPUT_EXPORTS;" StringPooling="true" MinimalRebuild="false" - ExceptionHandling="0" + ExceptionHandling="1" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" @@ -75,6 +75,7 @@ x = CRawInput::set_x; diff --git a/rawinput.h b/rawinput.h index 23e8be7..3ce090e 100644 --- a/rawinput.h +++ b/rawinput.h @@ -2,6 +2,8 @@ #define RAWINPUT_H_ #include +#include +#include #include "detours.h" // Required for function hooking #define RAWINPUTHDRSIZE sizeof(RAWINPUTHEADER) @@ -53,6 +55,7 @@ class CRawInput { static long hold_y; static int SCP; static bool GCP; + static bool sourceEXE; static HWND hwndInput; static bool bRegistered; From db8ed1db457e1332e898c53bf6fcdc85fb31b610 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 27 Oct 2015 15:57:50 -0400 Subject: [PATCH 23/57] First attempt at fixing buy menu/esc menu bug in source games --- RInput.vcproj | 3 ++- rawinput.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- rawinput.h | 4 ++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/RInput.vcproj b/RInput.vcproj index 73ce7f4..7af014e 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -56,6 +56,7 @@ RuntimeLibrary="3" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" + FloatingPointModel="2" ForceConformanceInForLoopScope="false" UsePrecompiledHeader="0" WarningLevel="3" @@ -159,7 +160,7 @@ BufferSecurityCheck="false" EnableFunctionLevelLinking="true" EnableEnhancedInstructionSet="0" - FloatingPointModel="0" + FloatingPointModel="2" TreatWChar_tAsBuiltInType="true" ForceConformanceInForLoopScope="false" RuntimeTypeInfo="true" diff --git a/rawinput.cpp b/rawinput.cpp index 5c116ca..470b336 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -47,6 +47,10 @@ long CRawInput::hold_y = 0; int CRawInput::SCP = 0; bool CRawInput::GCP = false; bool CRawInput::sourceEXE = false; +LARGE_INTEGER CRawInput::freq; +LARGE_INTEGER CRawInput::old; +LARGE_INTEGER CRawInput::older; +double CRawInput::avg = 0; bool CRawInput::initialize(WCHAR* pwszError) { @@ -108,6 +112,7 @@ bool CRawInput::initInput(WCHAR* pwszError) if ((std::string)szEXEPath == (std::string)*iSource_exes) { CRawInput::sourceEXE = true; + QueryPerformanceFrequency(&CRawInput::freq); break; } } @@ -185,7 +190,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) CRawInput::set_x = (long)x; CRawInput::set_y = (long)y; - + if (CRawInput::sourceEXE) { // Alt-tab bug fix @@ -216,12 +221,43 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { + // Buy menu and escape menu bug fix + if (CRawInput::sourceEXE) + { + LARGE_INTEGER time; + if (!CRawInput::old.QuadPart) + { + QueryPerformanceCounter(&time); + CRawInput::old = time; + } + else if (!CRawInput::older.QuadPart) + { + QueryPerformanceCounter(&time); + CRawInput::older = CRawInput::old; + CRawInput::old = time; + } + else + { + QueryPerformanceCounter(&time); + double currFPS = (double)(CRawInput::freq.QuadPart / (time.QuadPart - CRawInput::old.QuadPart)); + if (!CRawInput::avg) + CRawInput::avg = currFPS; + if (CRawInput::avg < 60) + CRawInput::avg = 60; + CRawInput::avg = ((30. * CRawInput::avg + currFPS) / 31.); + double adj = 0.30 * (CRawInput::avg) + 30.; + if ((double)time.QuadPart > (double)((adj + 1.) * CRawInput::old.QuadPart - adj * CRawInput::older.QuadPart)) + CRawInput::GCP = true; + CRawInput::older = CRawInput::old; + CRawInput::old = time; + } + } + // Split off raw input handling to accumulate independently CRawInput::set_x += CRawInput::x; CRawInput::set_y += CRawInput::y; CRawInput::SCP = 0; - // Alt-tab bug fix if (!CRawInput::GCP) { lpPoint->x = CRawInput::set_x; diff --git a/rawinput.h b/rawinput.h index 3ce090e..119992d 100644 --- a/rawinput.h +++ b/rawinput.h @@ -56,6 +56,10 @@ class CRawInput { static int SCP; static bool GCP; static bool sourceEXE; + static LARGE_INTEGER freq; + static LARGE_INTEGER old; + static LARGE_INTEGER older; + static double avg; static HWND hwndInput; static bool bRegistered; From dcbd4dd7f104526c8be38b63afaaae28f97b2a4c Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 3 Nov 2015 14:44:02 -0500 Subject: [PATCH 24/57] removed buy and escape menu bug fix that dropped some input --- rawinput.cpp | 38 +------------------------------------- rawinput.h | 4 ---- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/rawinput.cpp b/rawinput.cpp index 470b336..7a99841 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -47,10 +47,6 @@ long CRawInput::hold_y = 0; int CRawInput::SCP = 0; bool CRawInput::GCP = false; bool CRawInput::sourceEXE = false; -LARGE_INTEGER CRawInput::freq; -LARGE_INTEGER CRawInput::old; -LARGE_INTEGER CRawInput::older; -double CRawInput::avg = 0; bool CRawInput::initialize(WCHAR* pwszError) { @@ -112,7 +108,6 @@ bool CRawInput::initInput(WCHAR* pwszError) if ((std::string)szEXEPath == (std::string)*iSource_exes) { CRawInput::sourceEXE = true; - QueryPerformanceFrequency(&CRawInput::freq); break; } } @@ -221,43 +216,12 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { - // Buy menu and escape menu bug fix - if (CRawInput::sourceEXE) - { - LARGE_INTEGER time; - if (!CRawInput::old.QuadPart) - { - QueryPerformanceCounter(&time); - CRawInput::old = time; - } - else if (!CRawInput::older.QuadPart) - { - QueryPerformanceCounter(&time); - CRawInput::older = CRawInput::old; - CRawInput::old = time; - } - else - { - QueryPerformanceCounter(&time); - double currFPS = (double)(CRawInput::freq.QuadPart / (time.QuadPart - CRawInput::old.QuadPart)); - if (!CRawInput::avg) - CRawInput::avg = currFPS; - if (CRawInput::avg < 60) - CRawInput::avg = 60; - CRawInput::avg = ((30. * CRawInput::avg + currFPS) / 31.); - double adj = 0.30 * (CRawInput::avg) + 30.; - if ((double)time.QuadPart > (double)((adj + 1.) * CRawInput::old.QuadPart - adj * CRawInput::older.QuadPart)) - CRawInput::GCP = true; - CRawInput::older = CRawInput::old; - CRawInput::old = time; - } - } - // Split off raw input handling to accumulate independently CRawInput::set_x += CRawInput::x; CRawInput::set_y += CRawInput::y; CRawInput::SCP = 0; + // Alt-tab bug fix if (!CRawInput::GCP) { lpPoint->x = CRawInput::set_x; diff --git a/rawinput.h b/rawinput.h index 119992d..3ce090e 100644 --- a/rawinput.h +++ b/rawinput.h @@ -56,10 +56,6 @@ class CRawInput { static int SCP; static bool GCP; static bool sourceEXE; - static LARGE_INTEGER freq; - static LARGE_INTEGER old; - static LARGE_INTEGER older; - static double avg; static HWND hwndInput; static bool bRegistered; From 626d9310a09d6ffbed626628a49f50e643b7688a Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 3 Nov 2015 15:44:31 -0500 Subject: [PATCH 25/57] updated versioning and revised parts of the documentation --- README | 6 +++--- README.mdown | 6 +++--- versioninfo.h | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README b/README index a52b22a..3fe2334 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ -RInput Library v1.35 by Vols and Jezuz +RInput Library v1.36 by Vols and Jezuz -------------------------------------- -RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. +RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. Requirements: -------------------------------------- @@ -23,7 +23,7 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory - Compiled with Multi-threaded DLL runtime library for linking - Set the Entry Point as DllMain -- Use x86 architecture for compiling (i.e., RInput does not support for x64 applications) +- Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar How it works: diff --git a/README.mdown b/README.mdown index 31a1706..431c0f9 100644 --- a/README.mdown +++ b/README.mdown @@ -1,5 +1,5 @@ -# RInput Library v1.35 by Vols and Jezuz -RInput allows you to override low definition windows mouse input (accurate untill 400cpi) with high definition mouse input (raw input, which is more accurate for high cpi mice). This is certainly useful for older games as those engines only support low definition windows mouse input. +# RInput Library v1.36 by Vols and Jezuz +RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements - OS: Windows XP or later @@ -19,7 +19,7 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory - Compiled with Multi-threaded DLL runtime library for linking - Set the Entry Point as DllMain -- Use x86 architecture for compiling (i.e., RInput does not support for x64 applications) +- Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar ## How it works diff --git a/versioninfo.h b/versioninfo.h index e363578..c145e50 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.35" -#define RINPUTFVER 1,35 +#define RINPUTVER "v1.36" +#define RINPUTFVER 1,36 #define IDI_ICON 101 #define RINPUTINT "RInput" From 32216196472ca7b4a9d245d014ee6972cacdf4f8 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 4 Nov 2015 07:09:54 -0500 Subject: [PATCH 26/57] minor revisions to header files --- main.h | 1 + rawinput.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/main.h b/main.h index 3d2a617..a537064 100644 --- a/main.h +++ b/main.h @@ -5,6 +5,7 @@ #define WIN32_LEAN_AND_MEAN #define WINVER 0x0501 // XP atleast +#define _WIN32_WINDOWS 0x0501 // XP atleast #define _WIN32_WINNT 0x0501 // XP atleast #define ERROR_BUFFER_SIZE 256 // amount of bytes to store an error string diff --git a/rawinput.h b/rawinput.h index 3ce090e..f530dc8 100644 --- a/rawinput.h +++ b/rawinput.h @@ -31,7 +31,6 @@ class CRawInput { // Hooked functions handling static int __stdcall hGetCursorPos(LPPOINT lpPoint); static int __stdcall hSetCursorPos(int x, int y); - static int __stdcall hQueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount); // Poll Input static unsigned int pollInput(); From 4c48a9a4a4af224ee2e5efe8922e3baed2533463 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 5 Nov 2015 00:15:12 -0500 Subject: [PATCH 27/57] TF2 backpack now 100% fixed; numerous other small changes --- README | 2 +- README.mdown | 2 +- RInput.vcproj | 5 +++-- main.cpp | 2 +- main.h | 12 ++++++------ rawinput.cpp | 25 ++++++++++++++++++++++--- rawinput.h | 9 +++++---- versioninfo.h | 4 ++-- 8 files changed, 41 insertions(+), 20 deletions(-) diff --git a/README b/README index 3fe2334..ac860f7 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.36 by Vols and Jezuz +RInput Library v1.37 by Vols and Jezuz -------------------------------------- RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. diff --git a/README.mdown b/README.mdown index 431c0f9..ab954c3 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.36 by Vols and Jezuz +# RInput Library v1.37 by Vols and Jezuz RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements diff --git a/RInput.vcproj b/RInput.vcproj index 7af014e..10e5315 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -62,6 +62,7 @@ WarningLevel="3" Detect64BitPortabilityProblems="false" DebugInformationFormat="3" + CallingConvention="0" /> -#include "rawinput.h" // raw input class +#include "rawinput.h" // Raw input class HINSTANCE g_hInstance = NULL; -// only handle the hooking / dll functions here +// Only handle the hooking and dll functions here extern "C" __declspec(dllexport) void entryPoint(); inline bool validateVersion(); void unloadLibrary(); diff --git a/rawinput.cpp b/rawinput.cpp index 7a99841..6bc475e 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -47,6 +47,7 @@ long CRawInput::hold_y = 0; int CRawInput::SCP = 0; bool CRawInput::GCP = false; bool CRawInput::sourceEXE = false; +int CRawInput::consecG = 0; bool CRawInput::initialize(WCHAR* pwszError) { @@ -95,8 +96,9 @@ bool CRawInput::initWindow(WCHAR* pwszError) bool CRawInput::initInput(WCHAR* pwszError) { - // Set default coordinates - CRawInput::x = CRawInput::y = CRawInput::set_x = CRawInput::set_y = 0; + // Set screen center until SetCursorPos is called + CRawInput::hold_x = GetSystemMetrics(SM_CXSCREEN) >> 1; + CRawInput::hold_y = GetSystemMetrics(SM_CYSCREEN) >> 1; // Get process and enable bug fixes for source games char szEXEPath[MAX_PATH]; @@ -181,13 +183,15 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA int __stdcall CRawInput::hSetCursorPos(int x, int y) { - if (!TrmpSetCursorPos(x, y)) return 1; + // Skips unnecessary second SetCursorPos call for source games + if (!CRawInput::SCP && !TrmpSetCursorPos(x, y)) return 1; CRawInput::set_x = (long)x; CRawInput::set_y = (long)y; if (CRawInput::sourceEXE) { + CRawInput::consecG = 0; // Alt-tab bug fix if ((CRawInput::set_x == 0) && (CRawInput::set_y == 0)) CRawInput::GCP = true; @@ -221,6 +225,21 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) CRawInput::set_y += CRawInput::y; CRawInput::SCP = 0; + // Bug fix for cursor hitting side of screen in TF2 backpack + if (CRawInput::consecG < 2) + ++CRawInput::consecG; + if (CRawInput::sourceEXE && (CRawInput::consecG == 2)) + { + if (CRawInput::set_x >= CRawInput::hold_x << 1) + CRawInput::set_x = (CRawInput::hold_x << 1) - 1; + else if (CRawInput::set_x < 0) + CRawInput::set_x = 0; + if (CRawInput::set_y >= CRawInput::hold_y << 1) + CRawInput::set_y = (CRawInput::hold_y << 1) - 1; + else if (CRawInput::set_y < 0) + CRawInput::set_y = 0; + } + // Alt-tab bug fix if (!CRawInput::GCP) { diff --git a/rawinput.h b/rawinput.h index f530dc8..88112c5 100644 --- a/rawinput.h +++ b/rawinput.h @@ -25,24 +25,24 @@ class CRawInput { // Initialize raw input static bool initialize(WCHAR* pwszError); - // Enable/Disable the hooking + // Enables or disables the hooking static bool hookLibrary(bool bInstall); // Hooked functions handling static int __stdcall hGetCursorPos(LPPOINT lpPoint); static int __stdcall hSetCursorPos(int x, int y); - // Poll Input + // Poll input static unsigned int pollInput(); - // Input Window Proc + // Input window proc static LRESULT __stdcall wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); // Initialization static bool initWindow(WCHAR* pwszError); static bool initInput(WCHAR* pwszError); - // Unload Raw Input + // Unload raw input static void unload(); private: @@ -55,6 +55,7 @@ class CRawInput { static int SCP; static bool GCP; static bool sourceEXE; + static int consecG; static HWND hwndInput; static bool bRegistered; diff --git a/versioninfo.h b/versioninfo.h index c145e50..2a22e24 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.36" -#define RINPUTFVER 1,36 +#define RINPUTVER "v1.37" +#define RINPUTFVER 1,37 #define IDI_ICON 101 #define RINPUTINT "RInput" From 140095bac42d38c7b146571bec2b967fb31ecb94 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 12 Nov 2015 10:25:12 -0500 Subject: [PATCH 28/57] changed two generic WINAPI function calls to ANSI function calls --- rawinput.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rawinput.cpp b/rawinput.cpp index 6bc475e..26daa1e 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -103,8 +103,8 @@ bool CRawInput::initInput(WCHAR* pwszError) // Get process and enable bug fixes for source games char szEXEPath[MAX_PATH]; char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", "left4dead.exe", "left4dead2.exe", "dota2.exe", "insurgency.exe", "p3.exe", "bms.exe", NULL}; - GetModuleFileName(NULL, szEXEPath, sizeof(szEXEPath)); - PathStripPath(szEXEPath); + GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath)); + PathStripPathA(szEXEPath); for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) { if ((std::string)szEXEPath == (std::string)*iSource_exes) From 46099172c8670339ca56597da59999c57acc2e96 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 12 Nov 2015 20:15:15 -0500 Subject: [PATCH 29/57] Should now work in windowed mode, made TF2 backpack fixes only be applied for hl2.exe, shortened detected source games to which to apply bug fixes to only tested games (currently CS:GO, TF2/hl2.exe, and Portal 2), and updated versioning --- README | 7 +++++-- README.mdown | 7 +++++-- RInput.vcproj | 4 ++-- rawinput.cpp | 26 +++++++++++++++++++++----- rawinput.h | 1 + versioninfo.h | 4 ++-- 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/README b/README index ac860f7..2211e68 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.37 by Vols and Jezuz +RInput Library v1.38 by Vols and Jezuz -------------------------------------- RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. @@ -17,12 +17,15 @@ This is a fork of RInput Library v1.31 as originally authored by abort. x64 arch Building: -------------------------------------- -Compiled with Visual Studio 2008, though you may be able to get it to work with other versions or compilers (Visual C++ 2008 Express Edition is available for free @ https://go.microsoft.com/?linkid=7729279); +Compiled with Visual Studio 2008, though you may be able to get it to work with other versions or compilers (Visual C++ 2008 Express Edition is available for free @ https://go.microsoft.com/?linkid=7729279). If you want to start from scratch with your own project file, here's what seem to be the most important compiler and project options: - Pick Dynamic Library (.dll) as the configuration type - Compile using Multi-Byte Character Set - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory - Compiled with Multi-threaded DLL runtime library for linking +- It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No +- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well - Set the Entry Point as DllMain +- Add shlwapi.lib as an Additional Dependency in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/README.mdown b/README.mdown index ab954c3..90dcda4 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.37 by Vols and Jezuz +# RInput Library v1.38 by Vols and Jezuz RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements @@ -13,12 +13,15 @@ This is a fork of RInput Library v1.31 as originally authored by abort. x64 arch - This library obviously has to get recompiled to 64-bit (might require some changes to Win32 API calls for the RInput HWND) ## Building -Compiled with Visual Studio 2008, though you may be able to get it to work with other versions or compilers (Visual C++ 2008 Express Edition is available for free @ https://go.microsoft.com/?linkid=7729279); +Compiled with Visual Studio 2008, though you may be able to get it to work with other versions or compilers (Visual C++ 2008 Express Edition is available for free @ https://go.microsoft.com/?linkid=7729279). If you want to start from scratch with your own project file, here's what seem to be the most important compiler and project options: - Pick Dynamic Library (.dll) as the configuration type - Compile using Multi-Byte Character Set - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory - Compiled with Multi-threaded DLL runtime library for linking +- It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No +- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well - Set the Entry Point as DllMain +- Add shlwapi.lib as an Additional Dependency in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/RInput.vcproj b/RInput.vcproj index 10e5315..e7593ad 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -78,7 +78,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="shlwapi.lib" - Version="1.37-debug" + Version="1.38-debug" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib"" EnableUAC="true" @@ -186,7 +186,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="shlwapi.lib" - Version="1.37" + Version="1.38" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib"" GenerateManifest="true" diff --git a/rawinput.cpp b/rawinput.cpp index 26daa1e..15a95f9 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -48,6 +48,7 @@ int CRawInput::SCP = 0; bool CRawInput::GCP = false; bool CRawInput::sourceEXE = false; int CRawInput::consecG = 0; +int CRawInput::n_sourceEXE = 0; bool CRawInput::initialize(WCHAR* pwszError) { @@ -96,17 +97,31 @@ bool CRawInput::initWindow(WCHAR* pwszError) bool CRawInput::initInput(WCHAR* pwszError) { - // Set screen center until SetCursorPos is called - CRawInput::hold_x = GetSystemMetrics(SM_CXSCREEN) >> 1; - CRawInput::hold_y = GetSystemMetrics(SM_CYSCREEN) >> 1; + // Now behaves correctly with windowed mode + HWND client_hwnd = GetForegroundWindow(); + RECT client_rect; + if(GetClientRect(client_hwnd, &client_rect)) + { + long clientx = client_rect.right >> 1; + long clienty = client_rect.bottom >> 1; + LPPOINT client_lpPoint = new tagPOINT; + client_lpPoint->x = clientx; + client_lpPoint->y = clienty; + ClientToScreen(client_hwnd, client_lpPoint); + // Set screen center until SetCursorPos is called + CRawInput::hold_x = client_lpPoint->x; + CRawInput::hold_y = client_lpPoint->y; + } // Get process and enable bug fixes for source games char szEXEPath[MAX_PATH]; - char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", "left4dead.exe", "left4dead2.exe", "dota2.exe", "insurgency.exe", "p3.exe", "bms.exe", NULL}; + // Bug fixes now limited to source games that have been tested + char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath)); PathStripPathA(szEXEPath); for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) { + ++CRawInput::n_sourceEXE; if ((std::string)szEXEPath == (std::string)*iSource_exes) { CRawInput::sourceEXE = true; @@ -228,7 +243,8 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) // Bug fix for cursor hitting side of screen in TF2 backpack if (CRawInput::consecG < 2) ++CRawInput::consecG; - if (CRawInput::sourceEXE && (CRawInput::consecG == 2)) + // Changed to be active for hl2.exe only, the exe that TF2 uses + if ((CRawInput::n_sourceEXE == 2) && (CRawInput::consecG == 2)) { if (CRawInput::set_x >= CRawInput::hold_x << 1) CRawInput::set_x = (CRawInput::hold_x << 1) - 1; diff --git a/rawinput.h b/rawinput.h index 88112c5..11deda4 100644 --- a/rawinput.h +++ b/rawinput.h @@ -56,6 +56,7 @@ class CRawInput { static bool GCP; static bool sourceEXE; static int consecG; + static int n_sourceEXE; static HWND hwndInput; static bool bRegistered; diff --git a/versioninfo.h b/versioninfo.h index 2a22e24..5bad92d 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.37" -#define RINPUTFVER 1,37 +#define RINPUTVER "v1.38" +#define RINPUTFVER 1,38 #define IDI_ICON 101 #define RINPUTINT "RInput" From 55be9c7cddc4a52d460277f31c6a294f29afdb53 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 12 Nov 2015 20:41:19 -0500 Subject: [PATCH 30/57] made formatting more consistent and expanded READMEs slightly --- README | 2 +- README.mdown | 2 +- main.cpp | 24 ++++++++++++------------ rawinput.cpp | 42 +++++++++++++++++++++--------------------- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/README b/README index 2211e68..d8eb223 100644 --- a/README +++ b/README @@ -45,4 +45,4 @@ Credits: - BuSheeZy - helping me understand Visual C++ IDE and Git - qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works - about - original author -- Dr3am - for the icons +- Dr3am - for the icons \ No newline at end of file diff --git a/README.mdown b/README.mdown index 90dcda4..abefe15 100644 --- a/README.mdown +++ b/README.mdown @@ -39,4 +39,4 @@ Summary of the process by the original author: - BuSheeZy - helping me understand Visual C++ IDE and Git - qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works - about - original author -- Dr3am - for the icons +- Dr3am - for the icons \ No newline at end of file diff --git a/main.cpp b/main.cpp index b2e49a4..351fef7 100644 --- a/main.cpp +++ b/main.cpp @@ -3,11 +3,11 @@ Jezuz (http://steamcommunity.com/profiles/76561198057348857/), with a lot of help from BuSheeZy (http://BushGaming.com) and qsxcv (http://www.overclock.net/u/395745/qsxcv). - + ------------------------------------------------------------------ Comments from original author, abort (http://blog.digitalise.net/) ------------------------------------------------------------------ - + RInput allows you to override low definition windows mouse input with high definition mouse input. @@ -15,18 +15,18 @@ This file is part of RInput. - RInput is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + RInput is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - RInput is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + RInput is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with RInput. If not, see . + You should have received a copy of the GNU General Public License + along with RInput. If not, see . */ #include "main.h" diff --git a/rawinput.cpp b/rawinput.cpp index 15a95f9..efdcc25 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -3,11 +3,11 @@ Jezuz (http://steamcommunity.com/profiles/76561198057348857/), with a lot of help from BuSheeZy (http://BushGaming.com) and qsxcv (http://www.overclock.net/u/395745/qsxcv). - + ------------------------------------------------------------------ Comments from original author, abort (http://blog.digitalise.net/) ------------------------------------------------------------------ - + RInput allows you to override low definition windows mouse input with high definition mouse input. @@ -15,18 +15,18 @@ This file is part of RInput. - RInput is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + RInput is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - RInput is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + RInput is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with RInput. If not, see . + You should have received a copy of the GNU General Public License + along with RInput. If not, see . */ #include "rawinput.h" @@ -102,15 +102,15 @@ bool CRawInput::initInput(WCHAR* pwszError) RECT client_rect; if(GetClientRect(client_hwnd, &client_rect)) { - long clientx = client_rect.right >> 1; - long clienty = client_rect.bottom >> 1; - LPPOINT client_lpPoint = new tagPOINT; - client_lpPoint->x = clientx; - client_lpPoint->y = clienty; - ClientToScreen(client_hwnd, client_lpPoint); - // Set screen center until SetCursorPos is called - CRawInput::hold_x = client_lpPoint->x; - CRawInput::hold_y = client_lpPoint->y; + long clientx = client_rect.right >> 1; + long clienty = client_rect.bottom >> 1; + LPPOINT client_lpPoint = new tagPOINT; + client_lpPoint->x = clientx; + client_lpPoint->y = clienty; + ClientToScreen(client_hwnd, client_lpPoint); + // Set screen center until SetCursorPos is called + CRawInput::hold_x = client_lpPoint->x; + CRawInput::hold_y = client_lpPoint->y; } // Get process and enable bug fixes for source games From b502c36324b88ff1a3fb9e822c321653fe3d0b96 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 19 Nov 2015 18:30:06 -0500 Subject: [PATCH 31/57] completely fixed all known bugs for CS:GO and TF2 --- README | 3 +- README.mdown | 3 +- RInput.vcproj | 24 ++++++-- d3d9.cpp | 141 +++++++++++++++++++++++++++++++++++++++++++++ hook.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++++++ hook.h | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ main.cpp | 53 ++++++++++++++++- main.h | 16 +++--- rawinput.cpp | 47 ++++++++------- rawinput.h | 4 -- 10 files changed, 555 insertions(+), 41 deletions(-) create mode 100644 d3d9.cpp create mode 100644 hook.cpp create mode 100644 hook.h diff --git a/README b/README index d8eb223..45c59ed 100644 --- a/README +++ b/README @@ -21,11 +21,12 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Pick Dynamic Library (.dll) as the configuration type - Compile using Multi-Byte Character Set - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory +- Add the Microsoft DirectX SDK(https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) - Compiled with Multi-threaded DLL runtime library for linking - It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No - Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well - Set the Entry Point as DllMain -- Add shlwapi.lib as an Additional Dependency in the Linker options +- Add shlwapi.lib, dxguid.lib, and psapi.lib as an Additional Dependency in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/README.mdown b/README.mdown index abefe15..fbd48ac 100644 --- a/README.mdown +++ b/README.mdown @@ -17,11 +17,12 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Pick Dynamic Library (.dll) as the configuration type - Compile using Multi-Byte Character Set - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory +- Add the Microsoft DirectX SDK(https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) - Compiled with Multi-threaded DLL runtime library for linking - It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No - Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well - Set the Entry Point as DllMain -- Add shlwapi.lib as an Additional Dependency in the Linker options +- Add shlwapi.lib, dxguid.lib, and psapi.lib as an Additional Dependency in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/RInput.vcproj b/RInput.vcproj index e7593ad..de36de4 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -47,7 +47,7 @@ EnableIntrinsicFunctions="true" FavorSizeOrSpeed="1" OmitFramePointers="true" - AdditionalIncludeDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include"" + AdditionalIncludeDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Include"" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;RINPUT_EXPORTS;" StringPooling="true" MinimalRebuild="false" @@ -77,10 +77,10 @@ + + @@ -278,6 +282,14 @@ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > + + + + diff --git a/d3d9.cpp b/d3d9.cpp new file mode 100644 index 0000000..a32f8f6 --- /dev/null +++ b/d3d9.cpp @@ -0,0 +1,141 @@ +/******************************************************************************** + hook.h, hook.cpp, and d3d9.cpp contain the code for fixing the bugs in + the CS:GO buy and escape menus, and are only called for CS:GO. + + Copyright (C) 2012 Hugh Bailey + + This new code was adapted from the Open Broadcaster Software source, + obtained from https://github.com/jp9000/OBS +********************************************************************************/ + +#include "hook.h" +#include + +// EndScene is hooked to detect frame renders +HookData d3d9EndScene; + +extern int consec_endscene; +extern CRITICAL_SECTION d3d9EndMutex; +extern bool bD3D9Hooked; + +LPVOID lpCurrentDevice = NULL; +BOOL bD3D9Ex = FALSE; +HMODULE hD3D9Dll = NULL; + +void SetupD3D9(IDirect3DDevice9 *device); + +typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); + +HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) +{ + EnterCriticalSection(&d3d9EndMutex); + + d3d9EndScene.Unhook(); + + if(lpCurrentDevice == NULL) + { + IDirect3D9 *d3d; + if(SUCCEEDED(device->GetDirect3D(&d3d))) + { + IDirect3D9 *d3d9ex; + if(bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) + d3d9ex->Release(); + d3d->Release(); + } + + if(!bTargetAcquired) + { + lpCurrentDevice = device; + SetupD3D9(device); + bTargetAcquired = true; + } + } + + // EndScene is called twice per frame render + if (consec_endscene < 5) + ++consec_endscene; + + HRESULT hRes = device->EndScene(); + d3d9EndScene.Rehook(); + + LeaveCriticalSection(&d3d9EndMutex); + + return hRes; +} + +void LogPresentParams(D3DPRESENT_PARAMETERS &pp); + +void SetupD3D9(IDirect3DDevice9 *device) +{ + IDirect3D9 *d3d; + if (SUCCEEDED(device->GetDirect3D(&d3d))) + { + IDirect3D9 *d3d9ex; + if (bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) + d3d9ex->Release(); + d3d->Release(); + } +} + +typedef IDirect3D9* (WINAPI*D3D9CREATEPROC)(UINT); +typedef HRESULT (WINAPI*D3D9CREATEEXPROC)(UINT, IDirect3D9Ex**); + +bool InitD3D9Capture() +{ + bool bSuccess = false; + + wchar_t lpD3D9Path[MAX_PATH]; + SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, SHGFP_TYPE_CURRENT, lpD3D9Path); + size_t size = 11; + wchar_t* wa = new wchar_t[size]; + mbstowcs_s(&size, wa, 11, TEXT("\\d3d9.dll"), 11); + wcscat_s(lpD3D9Path, MAX_PATH, wa); + + hD3D9Dll = GetModuleHandleW(lpD3D9Path); + if (hD3D9Dll) + { + D3D9CREATEEXPROC d3d9CreateEx = (D3D9CREATEEXPROC)GetProcAddress(hD3D9Dll, "Direct3DCreate9Ex"); + if (d3d9CreateEx) + { + HRESULT hRes; + + IDirect3D9Ex *d3d9ex; + if (SUCCEEDED(hRes = (*d3d9CreateEx)(D3D_SDK_VERSION, &d3d9ex))) + { + D3DPRESENT_PARAMETERS pp; + ZeroMemory(&pp, sizeof(pp)); + pp.Windowed = 1; + pp.SwapEffect = D3DSWAPEFFECT_FLIP; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.BackBufferCount = 1; + pp.hDeviceWindow = (HWND)hwndSender; + pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + + IDirect3DDevice9Ex *deviceEx; + if (SUCCEEDED(hRes = d3d9ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, hwndSender, D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_NOWINDOWCHANGES, &pp, NULL, &deviceEx))) + { + bSuccess = true; + + UPARAM *vtable = *(UPARAM**)deviceEx; + + d3d9EndScene.Hook((FARPROC)*(vtable+(168/4)), (FARPROC)D3D9EndScene); + + deviceEx->Release(); + + d3d9EndScene.Rehook(); + } + + d3d9ex->Release(); + } + } + } + + return bSuccess; +} + +void CheckD3D9Capture() +{ + EnterCriticalSection(&d3d9EndMutex); + d3d9EndScene.Rehook(true); + LeaveCriticalSection(&d3d9EndMutex); +} \ No newline at end of file diff --git a/hook.cpp b/hook.cpp new file mode 100644 index 0000000..2e23dec --- /dev/null +++ b/hook.cpp @@ -0,0 +1,149 @@ +/******************************************************************************** + hook.h, hook.cpp, and d3d9.cpp contain the code for fixing the bugs in + the CS:GO buy and escape menus, and are only called for CS:GO. + + Copyright (C) 2012 Hugh Bailey + + This new code was adapted from the Open Broadcaster Software source, + obtained from https://github.com/jp9000/OBS +********************************************************************************/ + +#include "hook.h" +#include + +HWND hwndSender = NULL; +bool bTargetAcquired = false; +extern bool bCaptureThreadStop; +extern HANDLE hCaptureThread; +CRITICAL_SECTION d3d9EndMutex; +void CheckD3D9Capture(); +bool bD3D9Hooked = false; +HANDLE dummyEvent = NULL; + +inline bool AttemptToHookSomething() +{ + if (!hwndSender) + return false; + + bool bFoundSomethingToHook = false; + if (!bD3D9Hooked) + { + if (InitD3D9Capture()) + { + bFoundSomethingToHook = true; + bD3D9Hooked = true; + } + } + else + CheckD3D9Capture(); + + return bFoundSomethingToHook; +} + +#define SENDER_WINDOWCLASS TEXT("RInputD3DSender") + +#define TIMER_ID 431879 +BOOL GetMessageTimeout(MSG &msg, DWORD timeout) +{ + BOOL ret; + SetTimer(NULL, TIMER_ID, timeout, NULL); + ret = GetMessage(&msg, NULL, 0, 0); + KillTimer(NULL, TIMER_ID); + return ret; +} + +static inline HWND CreateDummyWindow(LPCTSTR lpClass, LPCTSTR lpName) +{ + return CreateWindowEx (0, lpClass, lpName, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL); +} + +static DWORD WINAPI DummyWindowThread(LPVOID lpBla) +{ + WNDCLASS wc; + ZeroMemory(&wc, sizeof(wc)); + wc.style = CS_OWNDC; + wc.hInstance = g_hInstance; + wc.lpfnWndProc = (WNDPROC)DefWindowProc; + + wc.lpszClassName = SENDER_WINDOWCLASS; + if (RegisterClass(&wc)) + { + hwndSender = CreateDummyWindow(SENDER_WINDOWCLASS, NULL); + if (!hwndSender) + return 0; + else + return 0; + } + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} + +DWORD WINAPI CaptureThread(HANDLE hEntryPointThread) +{ + bool bSuccess = false; + + if (hEntryPointThread) + { + WaitForSingleObject(hEntryPointThread, 150); + CloseHandle(hEntryPointThread); + } + + dummyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + InitializeCriticalSection(&d3d9EndMutex); + + DWORD bla; + HANDLE hWindowThread = CreateThread(NULL, 0, DummyWindowThread, NULL, 0, &bla); + if (!hWindowThread) + return 0; + + CloseHandle(hWindowThread); + + while(!AttemptToHookSomething()) + { + Sleep(50); + } + + for (size_t n = 0; !bCaptureThreadStop; ++n) + { + // Less frequent loop interations reduce system resource usage + if (n % 16 == 0) AttemptToHookSomething(); + // Overall interval for checking EndScene hook is still 4000ms + Sleep(250); + } + + return 0; +} + +static bool hooking = true; + +typedef BOOL (WINAPI *UHWHEXPROC)(HHOOK); + +extern "C" __declspec(dllexport) LRESULT CALLBACK DummyDebugProc(int code, WPARAM wParam, LPARAM lParam) +{ + MSG *msg = (MSG*)lParam; + + if (hooking && msg->message == (WM_USER + 432)) + { + char uhwhexStr[20]; + HMODULE hU32 = GetModuleHandleW(L"USER32"); + + memcpy(uhwhexStr, "PjoinkTkch`yz@de~Qo", 20); + + for (int i = 0; i < 19; i++) uhwhexStr[i] ^= i ^ 5; + + UHWHEXPROC unhookWindowsHookEx = (UHWHEXPROC)GetProcAddress(hU32, uhwhexStr); + if (unhookWindowsHookEx) + unhookWindowsHookEx((HHOOK)msg->lParam); + hooking = false; + } + + return CallNextHookEx(0, code, wParam, lParam); +} \ No newline at end of file diff --git a/hook.h b/hook.h new file mode 100644 index 0000000..ac95222 --- /dev/null +++ b/hook.h @@ -0,0 +1,156 @@ +/******************************************************************************** + hook.h, hook.cpp, and d3d9.cpp contain the code for fixing the bugs in + the CS:GO buy and escape menus, and are only called for CS:GO. + + Copyright (C) 2012 Hugh Bailey + + This new code was adapted from the Open Broadcaster Software source, + obtained from https://github.com/jp9000/OBS +********************************************************************************/ + +#include +#include + +#define PSAPI_VERSION 1 +#include + +#pragma intrinsic(memcpy, memset) + +#include + +typedef unsigned long UPARAM; + +class HookData +{ + BYTE data[14]; + FARPROC func; + FARPROC hookFunc; + bool bHooked; + bool b64bitJump; + bool bAttemptedBounce; + LPVOID bounceAddress; + +public: + inline HookData() : bHooked(false), func(NULL), hookFunc(NULL), b64bitJump(false), bAttemptedBounce(false) {} + + inline bool Hook(FARPROC funcIn, FARPROC hookFuncIn) + { + if (bHooked) + { + if (funcIn == func) + { + if (hookFunc != hookFuncIn) + { + hookFunc = hookFuncIn; + Rehook(); + return true; + } + } + + Unhook(); + } + + func = funcIn; + hookFunc = hookFuncIn; + + DWORD oldProtect; + if (!VirtualProtect((LPVOID)func, 14, PAGE_EXECUTE_READWRITE, &oldProtect)) + return false; + + if (*(BYTE *)func == 0xE9 || *(BYTE *)func == 0xE8) + { + CHAR *modName, *ourName; + CHAR szModName[MAX_PATH]; + CHAR szOurName[MAX_PATH]; + DWORD memAddress; + + MEMORY_BASIC_INFORMATION mem; + + INT_PTR jumpAddress = *(DWORD *)((BYTE *)func + 1) + (DWORD)func; + + if (VirtualQueryEx(GetCurrentProcess(), (LPVOID)jumpAddress, &mem, sizeof(mem)) && mem.State == MEM_COMMIT) + memAddress = (DWORD)mem.AllocationBase; + else + memAddress = jumpAddress; + + if (GetMappedFileNameA(GetCurrentProcess(), (LPVOID)memAddress, szModName, _countof(szModName) - 1)) + modName = szModName; + else if (GetModuleFileNameA((HMODULE)memAddress, szModName, _countof(szModName) - 1)) + modName = szModName; + else + modName = "unknown"; + + if (VirtualQueryEx(GetCurrentProcess(), (LPVOID)func, &mem, sizeof(mem)) && mem.State == MEM_COMMIT) + memAddress = (DWORD)mem.AllocationBase; + else + memAddress = (DWORD)func; + + if (GetMappedFileNameA(GetCurrentProcess(), (LPVOID)memAddress, szOurName, _countof(szOurName) - 1)) + ourName = szOurName; + else if (GetModuleFileNameA((HMODULE)memAddress, szOurName, _countof(szOurName) - 1)) + ourName = szOurName; + else + ourName = "unknown"; + } + + memcpy(data, (const void*)func, 14); + + return true; + } + + inline void Rehook(bool bForce=false) + { + if ((!bForce && bHooked) || !func) + return; + + UPARAM startAddr = UPARAM(func); + UPARAM targetAddr = UPARAM(hookFunc); + ULONG64 offset, diff; + + offset = targetAddr - (startAddr + 5); + + if (startAddr + 5 > targetAddr) + diff = startAddr + 5 - targetAddr; + else + diff = targetAddr - startAddr + 5; + + DWORD oldProtect; + + { + VirtualProtect((LPVOID)func, 5, PAGE_EXECUTE_READWRITE, &oldProtect); + + LPBYTE addrData = (LPBYTE)func; + *addrData = 0xE9; + *(DWORD*)(addrData+1) = DWORD(offset); + } + + bHooked = true; + } + + inline void Unhook() + { + if (!bHooked || !func) + return; + + UINT count = b64bitJump ? 14 : 5; + DWORD oldProtect; + VirtualProtect((LPVOID)func, count, PAGE_EXECUTE_READWRITE, &oldProtect); + memcpy((void*)func, data, count); + + bHooked = false; + } +}; + +inline FARPROC GetVTable(LPVOID ptr, UINT funcOffset) +{ + UPARAM *vtable = *(UPARAM**)ptr; + return (FARPROC)(*(vtable+funcOffset)); +} + +extern HINSTANCE g_hInstance; +extern HWND hwndSender; +extern bool bTargetAcquired; +extern bool bCaptureThreadStop; +extern bool bD3D9Hooked; +DWORD WINAPI CaptureThread(HANDLE hEntryPointThread); +bool InitD3D9Capture(); \ No newline at end of file diff --git a/main.cpp b/main.cpp index 351fef7..084abd7 100644 --- a/main.cpp +++ b/main.cpp @@ -30,6 +30,11 @@ */ #include "main.h" +#include + +bool bCaptureThreadStop = false; +HANDLE hCaptureThread = NULL; +extern HANDLE dummyEvent; int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { @@ -47,9 +52,24 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) #endif break; + case DLL_PROCESS_DETACH: - CRawInput::hookLibrary(false); - CRawInput::unload(); + // Stop D3D hooking for CS:GO + if (n_sourceEXE == 1) + { + bCaptureThreadStop = true; + WaitForSingleObject(hCaptureThread, 300); + + if (dummyEvent) + CloseHandle(dummyEvent); + + if (hwndSender) + DestroyWindow(hwndSender); + } + + CRawInput::hookLibrary(false); + CRawInput::unload(); + #ifdef _DEBUG OutputDebugString("Unloaded RInput"); #endif @@ -76,6 +96,35 @@ extern "C" __declspec(dllexport) void entryPoint() if (!CRawInput::initialize(pwszError)) displayError(pwszError); + else + { + // Get process and enable bug fixes for source games + char szEXEPath[MAX_PATH]; + // Bug fixes now limited to source games that have been tested + char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; + GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath)); + PathStripPathA(szEXEPath); + for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) + { + ++n_sourceEXE; + if ((std::string)szEXEPath == (std::string)*iSource_exes) + { + // Start D3D hooking for CS:GO + if (n_sourceEXE == 1) + { + HANDLE hThread = NULL; + HANDLE hEntryPointThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); + if (!(hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hEntryPointThread, 0, 0))) + CloseHandle(hEntryPointThread); + else + hCaptureThread = hThread; + } + + sourceEXE = true; + break; + } + } + } if (!CRawInput::hookLibrary(true)) displayError(L"Failed to hook Windows API cursor functions."); diff --git a/main.h b/main.h index ab0e0f6..34de7f6 100644 --- a/main.h +++ b/main.h @@ -2,21 +2,23 @@ #define _MAIN_H_ #define STRICT +#define WINVER 0x0501 // Need at least Windows XP +#define _WIN32_WINDOWS 0x0501 // Need at least Windows XP +#define _WIN32_WINNT 0x0501 // Need at least Windows XP #define WIN32_LEAN_AND_MEAN -#define WINVER 0x0501 // Need at least Windows XP -#define _WIN32_WINDOWS 0x0501 // Need at least Windows XP -#define _WIN32_WINNT 0x0501 // Need at least Windows XP - -#define ERROR_BUFFER_SIZE 256 // Amount of bytes to store an error string +#define ERROR_BUFFER_SIZE 256 // Amount of bytes to store an error string #define EVENTNAME "RInputEvent32" #define KERNEL_LIB L"kernel32.dll" -#include -#include "rawinput.h" // Raw input class +#include "rawinput.h" // Raw input class +#include "hook.h" // D3D9 hooking HINSTANCE g_hInstance = NULL; +int n_sourceEXE = 0; +bool sourceEXE = false; +int consec_endscene = 0; // Only handle the hooking and dll functions here extern "C" __declspec(dllexport) void entryPoint(); diff --git a/rawinput.cpp b/rawinput.cpp index efdcc25..3839fa3 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -31,6 +31,8 @@ #include "rawinput.h" +#pragma intrinsic(memset) + // Define the to be hooked functions extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpSetCursorPos(int x, int y), SetCursorPos); @@ -46,9 +48,11 @@ long CRawInput::hold_x = 0; long CRawInput::hold_y = 0; int CRawInput::SCP = 0; bool CRawInput::GCP = false; -bool CRawInput::sourceEXE = false; int CRawInput::consecG = 0; -int CRawInput::n_sourceEXE = 0; + +extern int n_sourceEXE; +extern bool sourceEXE; +extern int consec_endscene; bool CRawInput::initialize(WCHAR* pwszError) { @@ -113,22 +117,6 @@ bool CRawInput::initInput(WCHAR* pwszError) CRawInput::hold_y = client_lpPoint->y; } - // Get process and enable bug fixes for source games - char szEXEPath[MAX_PATH]; - // Bug fixes now limited to source games that have been tested - char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; - GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath)); - PathStripPathA(szEXEPath); - for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) - { - ++CRawInput::n_sourceEXE; - if ((std::string)szEXEPath == (std::string)*iSource_exes) - { - CRawInput::sourceEXE = true; - break; - } - } - // Raw input accumulators initialized to starting cursor position LPPOINT defCor = new tagPOINT; GetCursorPos(defCor); @@ -204,7 +192,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) CRawInput::set_x = (long)x; CRawInput::set_y = (long)y; - if (CRawInput::sourceEXE) + if (sourceEXE) { CRawInput::consecG = 0; // Alt-tab bug fix @@ -219,6 +207,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) } else if (CRawInput::SCP == 2) { + consec_endscene = 0; CRawInput::GCP = false; CRawInput::SCP = 0; CRawInput::hold_x = CRawInput::set_x; @@ -244,7 +233,7 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) if (CRawInput::consecG < 2) ++CRawInput::consecG; // Changed to be active for hl2.exe only, the exe that TF2 uses - if ((CRawInput::n_sourceEXE == 2) && (CRawInput::consecG == 2)) + if ((n_sourceEXE == 2) && (CRawInput::consecG == 2)) { if (CRawInput::set_x >= CRawInput::hold_x << 1) CRawInput::set_x = (CRawInput::hold_x << 1) - 1; @@ -259,6 +248,24 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) // Alt-tab bug fix if (!CRawInput::GCP) { + // Buy and escape menu bug fix + if (consec_endscene == 5) + { + // Respects changes in resolution + HWND new_hwnd = GetForegroundWindow(); + RECT new_rect; + if(GetClientRect(new_hwnd, &new_rect)) + { + long newx = new_rect.right >> 1; + long newy = new_rect.bottom >> 1; + LPPOINT new_lpPoint = new tagPOINT; + new_lpPoint->x = newx; + new_lpPoint->y = newy; + ClientToScreen(new_hwnd, new_lpPoint); + CRawInput::set_x = new_lpPoint->x; + CRawInput::set_y = new_lpPoint->y; + } + } lpPoint->x = CRawInput::set_x; lpPoint->y = CRawInput::set_y; } diff --git a/rawinput.h b/rawinput.h index 11deda4..fc9c781 100644 --- a/rawinput.h +++ b/rawinput.h @@ -2,8 +2,6 @@ #define RAWINPUT_H_ #include -#include -#include #include "detours.h" // Required for function hooking #define RAWINPUTHDRSIZE sizeof(RAWINPUTHEADER) @@ -54,9 +52,7 @@ class CRawInput { static long hold_y; static int SCP; static bool GCP; - static bool sourceEXE; static int consecG; - static int n_sourceEXE; static HWND hwndInput; static bool bRegistered; From 6a899f4f8bcbc526bff6561bf5365ad4abae1c2e Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 19 Nov 2015 22:23:38 -0500 Subject: [PATCH 32/57] corrected improper threading in DllMain and entryPoint, and deleted unused functions in the D3D9 source files --- d3d9.cpp | 56 +++++++++++++++++++++++++------------------------- hook.cpp | 49 ++++++-------------------------------------- main.cpp | 58 ++++++++++++++++++++++++++-------------------------- rawinput.cpp | 16 +++++++-------- 4 files changed, 71 insertions(+), 108 deletions(-) diff --git a/d3d9.cpp b/d3d9.cpp index a32f8f6..9a3d263 100644 --- a/d3d9.cpp +++ b/d3d9.cpp @@ -28,39 +28,39 @@ typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) { - EnterCriticalSection(&d3d9EndMutex); - - d3d9EndScene.Unhook(); - - if(lpCurrentDevice == NULL) - { - IDirect3D9 *d3d; - if(SUCCEEDED(device->GetDirect3D(&d3d))) - { - IDirect3D9 *d3d9ex; - if(bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) - d3d9ex->Release(); - d3d->Release(); - } - - if(!bTargetAcquired) - { - lpCurrentDevice = device; - SetupD3D9(device); - bTargetAcquired = true; - } - } + EnterCriticalSection(&d3d9EndMutex); + + d3d9EndScene.Unhook(); + + if(lpCurrentDevice == NULL) + { + IDirect3D9 *d3d; + if(SUCCEEDED(device->GetDirect3D(&d3d))) + { + IDirect3D9 *d3d9ex; + if(bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) + d3d9ex->Release(); + d3d->Release(); + } + + if(!bTargetAcquired) + { + lpCurrentDevice = device; + SetupD3D9(device); + bTargetAcquired = true; + } + } // EndScene is called twice per frame render if (consec_endscene < 5) ++consec_endscene; - HRESULT hRes = device->EndScene(); - d3d9EndScene.Rehook(); + HRESULT hRes = device->EndScene(); + d3d9EndScene.Rehook(); - LeaveCriticalSection(&d3d9EndMutex); + LeaveCriticalSection(&d3d9EndMutex); - return hRes; + return hRes; } void LogPresentParams(D3DPRESENT_PARAMETERS &pp); @@ -87,8 +87,8 @@ bool InitD3D9Capture() wchar_t lpD3D9Path[MAX_PATH]; SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, SHGFP_TYPE_CURRENT, lpD3D9Path); size_t size = 11; - wchar_t* wa = new wchar_t[size]; - mbstowcs_s(&size, wa, 11, TEXT("\\d3d9.dll"), 11); + wchar_t* wa = new wchar_t[size]; + mbstowcs_s(&size, wa, 11, TEXT("\\d3d9.dll"), 11); wcscat_s(lpD3D9Path, MAX_PATH, wa); hD3D9Dll = GetModuleHandleW(lpD3D9Path); diff --git a/hook.cpp b/hook.cpp index 2e23dec..f55b9dc 100644 --- a/hook.cpp +++ b/hook.cpp @@ -14,7 +14,6 @@ HWND hwndSender = NULL; bool bTargetAcquired = false; extern bool bCaptureThreadStop; -extern HANDLE hCaptureThread; CRITICAL_SECTION d3d9EndMutex; void CheckD3D9Capture(); bool bD3D9Hooked = false; @@ -42,16 +41,6 @@ inline bool AttemptToHookSomething() #define SENDER_WINDOWCLASS TEXT("RInputD3DSender") -#define TIMER_ID 431879 -BOOL GetMessageTimeout(MSG &msg, DWORD timeout) -{ - BOOL ret; - SetTimer(NULL, TIMER_ID, timeout, NULL); - ret = GetMessage(&msg, NULL, 0, 0); - KillTimer(NULL, TIMER_ID); - return ret; -} - static inline HWND CreateDummyWindow(LPCTSTR lpClass, LPCTSTR lpName) { return CreateWindowEx (0, lpClass, lpName, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL); @@ -71,9 +60,9 @@ static DWORD WINAPI DummyWindowThread(LPVOID lpBla) hwndSender = CreateDummyWindow(SENDER_WINDOWCLASS, NULL); if (!hwndSender) return 0; - else - return 0; } + else + return 0; MSG msg; while (GetMessage(&msg, NULL, 0, 0)) @@ -85,14 +74,14 @@ static DWORD WINAPI DummyWindowThread(LPVOID lpBla) return 0; } -DWORD WINAPI CaptureThread(HANDLE hEntryPointThread) +DWORD WINAPI CaptureThread(HANDLE hDllMainThread) { bool bSuccess = false; - if (hEntryPointThread) + if (hDllMainThread) { - WaitForSingleObject(hEntryPointThread, 150); - CloseHandle(hEntryPointThread); + WaitForSingleObject(hDllMainThread, 150); + CloseHandle(hDllMainThread); } dummyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -120,30 +109,4 @@ DWORD WINAPI CaptureThread(HANDLE hEntryPointThread) } return 0; -} - -static bool hooking = true; - -typedef BOOL (WINAPI *UHWHEXPROC)(HHOOK); - -extern "C" __declspec(dllexport) LRESULT CALLBACK DummyDebugProc(int code, WPARAM wParam, LPARAM lParam) -{ - MSG *msg = (MSG*)lParam; - - if (hooking && msg->message == (WM_USER + 432)) - { - char uhwhexStr[20]; - HMODULE hU32 = GetModuleHandleW(L"USER32"); - - memcpy(uhwhexStr, "PjoinkTkch`yz@de~Qo", 20); - - for (int i = 0; i < 19; i++) uhwhexStr[i] ^= i ^ 5; - - UHWHEXPROC unhookWindowsHookEx = (UHWHEXPROC)GetProcAddress(hU32, uhwhexStr); - if (unhookWindowsHookEx) - unhookWindowsHookEx((HHOOK)msg->lParam); - hooking = false; - } - - return CallNextHookEx(0, code, wParam, lParam); } \ No newline at end of file diff --git a/main.cpp b/main.cpp index 084abd7..1ac2c64 100644 --- a/main.cpp +++ b/main.cpp @@ -45,6 +45,35 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) case DLL_PROCESS_ATTACH: // No need for a threaded entry if (!DisableThreadLibraryCalls(hInstance)) return 0; + else + { + // Get process and enable bug fixes for source games + char szEXEPath[MAX_PATH]; + // Bug fixes now limited to source games that have been tested + char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; + GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath)); + PathStripPathA(szEXEPath); + for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) + { + ++n_sourceEXE; + if ((std::string)szEXEPath == (std::string)*iSource_exes) + { + // Start D3D hooking for CS:GO + if (n_sourceEXE == 1) + { + HANDLE hThread = NULL; + HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); + if (!(hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hDllMainThread, 0, 0))) + CloseHandle(hDllMainThread); + else + hCaptureThread = hThread; + } + + sourceEXE = true; + break; + } + } + } g_hInstance = hInstance; #ifdef _DEBUG @@ -96,35 +125,6 @@ extern "C" __declspec(dllexport) void entryPoint() if (!CRawInput::initialize(pwszError)) displayError(pwszError); - else - { - // Get process and enable bug fixes for source games - char szEXEPath[MAX_PATH]; - // Bug fixes now limited to source games that have been tested - char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; - GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath)); - PathStripPathA(szEXEPath); - for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) - { - ++n_sourceEXE; - if ((std::string)szEXEPath == (std::string)*iSource_exes) - { - // Start D3D hooking for CS:GO - if (n_sourceEXE == 1) - { - HANDLE hThread = NULL; - HANDLE hEntryPointThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); - if (!(hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hEntryPointThread, 0, 0))) - CloseHandle(hEntryPointThread); - else - hCaptureThread = hThread; - } - - sourceEXE = true; - break; - } - } - } if (!CRawInput::hookLibrary(true)) displayError(L"Failed to hook Windows API cursor functions."); diff --git a/rawinput.cpp b/rawinput.cpp index 3839fa3..c4e0014 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -256,14 +256,14 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) RECT new_rect; if(GetClientRect(new_hwnd, &new_rect)) { - long newx = new_rect.right >> 1; - long newy = new_rect.bottom >> 1; - LPPOINT new_lpPoint = new tagPOINT; - new_lpPoint->x = newx; - new_lpPoint->y = newy; - ClientToScreen(new_hwnd, new_lpPoint); - CRawInput::set_x = new_lpPoint->x; - CRawInput::set_y = new_lpPoint->y; + long newx = new_rect.right >> 1; + long newy = new_rect.bottom >> 1; + LPPOINT new_lpPoint = new tagPOINT; + new_lpPoint->x = newx; + new_lpPoint->y = newy; + ClientToScreen(new_hwnd, new_lpPoint); + CRawInput::set_x = new_lpPoint->x; + CRawInput::set_y = new_lpPoint->y; } } lpPoint->x = CRawInput::set_x; From e71488ec1ae4ae10aa58aefec4c1e75facf86c39 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Fri, 20 Nov 2015 00:34:59 -0500 Subject: [PATCH 33/57] D3D9 Present hooks now working, to more accurately detect frame renders --- d3d9.cpp | 159 ++++++++++++++++++++++++++++++++++++++++++++++----- main.h | 2 +- rawinput.cpp | 10 ++-- 3 files changed, 152 insertions(+), 19 deletions(-) diff --git a/d3d9.cpp b/d3d9.cpp index 9a3d263..950d1c4 100644 --- a/d3d9.cpp +++ b/d3d9.cpp @@ -11,10 +11,21 @@ #include "hook.h" #include -// EndScene is hooked to detect frame renders +// Prevent empty controlled statement warning, as it's intentional +#pragma warning(disable: 4390) + +typedef HRESULT (WINAPI *PRESENTPROC)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*); +typedef HRESULT (WINAPI *PRESENTEXPROC)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*, DWORD); +typedef HRESULT (WINAPI *SWAPPRESENTPROC)(IDirect3DSwapChain9*, const RECT*, const RECT*, HWND, const RGNDATA*, DWORD); + HookData d3d9EndScene; +HookData d3d9Reset; +HookData d3d9ResetEx; +HookData d3d9Present; +HookData d3d9PresentEx; +HookData d3d9SwapPresent; -extern int consec_endscene; +extern int frame_rendered; extern CRITICAL_SECTION d3d9EndMutex; extern bool bD3D9Hooked; @@ -24,6 +35,8 @@ HMODULE hD3D9Dll = NULL; void SetupD3D9(IDirect3DDevice9 *device); +static int presentRecurse = 0; + typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) @@ -51,10 +64,6 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) } } - // EndScene is called twice per frame render - if (consec_endscene < 5) - ++consec_endscene; - HRESULT hRes = device->EndScene(); d3d9EndScene.Rehook(); @@ -63,17 +72,141 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) return hRes; } -void LogPresentParams(D3DPRESENT_PARAMETERS &pp); +HRESULT STDMETHODCALLTYPE D3D9Present(IDirect3DDevice9 *device, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) +{ + d3d9Present.Unhook(); + + if (!presentRecurse) + { + if (frame_rendered < 3) + ++frame_rendered; + } + presentRecurse++; + HRESULT hRes = device->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); + presentRecurse--; + + d3d9Present.Rehook(); + + return hRes; +} + +HRESULT STDMETHODCALLTYPE D3D9PresentEx(IDirect3DDevice9Ex *device, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) +{ + d3d9PresentEx.Unhook(); + + if (!presentRecurse) + { + if (frame_rendered < 3) + ++frame_rendered; + } + presentRecurse++; + HRESULT hRes = device->PresentEx(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); + presentRecurse--; + + d3d9PresentEx.Rehook(); + + return hRes; +} + +HRESULT STDMETHODCALLTYPE D3D9SwapPresent(IDirect3DSwapChain9 *swap, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) +{ + d3d9SwapPresent.Unhook(); + + if(!presentRecurse) + { + if (frame_rendered < 3) + ++frame_rendered; + } + presentRecurse++; + HRESULT hRes = swap->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); + presentRecurse--; + + d3d9SwapPresent.Rehook(); + + return hRes; +} + +typedef HRESULT(STDMETHODCALLTYPE *D3D9ResetPROC)(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params); + +HRESULT STDMETHODCALLTYPE D3D9Reset(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params) +{ + d3d9Reset.Unhook(); + + HRESULT hRes = device->Reset(params); + + if(lpCurrentDevice == NULL && !bTargetAcquired) + { + lpCurrentDevice = device; + bTargetAcquired = true; + } + + if(lpCurrentDevice == device) + SetupD3D9(device); + + d3d9Reset.Rehook(); + + return hRes; +} + +HRESULT STDMETHODCALLTYPE D3D9ResetEx(IDirect3DDevice9Ex *device, D3DPRESENT_PARAMETERS *params, D3DDISPLAYMODEEX *fullscreenData) +{ + d3d9ResetEx.Unhook(); + d3d9Reset.Unhook(); + + HRESULT hRes = device->ResetEx(params, fullscreenData); + + if(lpCurrentDevice == NULL && !bTargetAcquired) + { + lpCurrentDevice = device; + bTargetAcquired = true; + bD3D9Ex = true; + } + + if(lpCurrentDevice == device) + SetupD3D9(device); + + d3d9Reset.Rehook(); + d3d9ResetEx.Rehook(); + + return hRes; +} void SetupD3D9(IDirect3DDevice9 *device) { - IDirect3D9 *d3d; - if (SUCCEEDED(device->GetDirect3D(&d3d))) + IDirect3DSwapChain9 *swapChain = NULL; + if (SUCCEEDED(device->GetSwapChain(0, &swapChain))) { - IDirect3D9 *d3d9ex; - if (bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) - d3d9ex->Release(); - d3d->Release(); + D3DPRESENT_PARAMETERS pp; + if (SUCCEEDED(swapChain->GetPresentParameters(&pp))) + ; //intentionally empty + + IDirect3D9 *d3d; + if (SUCCEEDED(device->GetDirect3D(&d3d))) + { + IDirect3D9 *d3d9ex; + if (bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) + d3d9ex->Release(); + d3d->Release(); + } + + d3d9Present.Hook(GetVTable(device, (68/4)), (FARPROC)D3D9Present); + if(bD3D9Ex) + { + FARPROC curPresentEx = GetVTable(device, (484/4)); + d3d9PresentEx.Hook(curPresentEx, (FARPROC)D3D9PresentEx); + d3d9ResetEx.Hook(GetVTable(device, (528/4)), (FARPROC)D3D9ResetEx); + } + d3d9Reset.Hook(GetVTable(device, (64/4)), (FARPROC)D3D9Reset); + d3d9SwapPresent.Hook(GetVTable(swapChain, (12/4)), (FARPROC)D3D9SwapPresent); + d3d9Present.Rehook(); + d3d9SwapPresent.Rehook(); + d3d9Reset.Rehook(); + if(bD3D9Ex) { + d3d9PresentEx.Rehook(); + d3d9ResetEx.Rehook(); + } + + swapChain->Release(); } } diff --git a/main.h b/main.h index 34de7f6..6208c5a 100644 --- a/main.h +++ b/main.h @@ -18,7 +18,7 @@ HINSTANCE g_hInstance = NULL; int n_sourceEXE = 0; bool sourceEXE = false; -int consec_endscene = 0; +int frame_rendered = 0; // Only handle the hooking and dll functions here extern "C" __declspec(dllexport) void entryPoint(); diff --git a/rawinput.cpp b/rawinput.cpp index c4e0014..fb96c6d 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -52,7 +52,7 @@ int CRawInput::consecG = 0; extern int n_sourceEXE; extern bool sourceEXE; -extern int consec_endscene; +extern int frame_rendered; bool CRawInput::initialize(WCHAR* pwszError) { @@ -207,7 +207,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) } else if (CRawInput::SCP == 2) { - consec_endscene = 0; + frame_rendered = 0; CRawInput::GCP = false; CRawInput::SCP = 0; CRawInput::hold_x = CRawInput::set_x; @@ -248,10 +248,10 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) // Alt-tab bug fix if (!CRawInput::GCP) { - // Buy and escape menu bug fix - if (consec_endscene == 5) + // Allows 3 frames w/o SetCursorPos to be safe, 2 gives loss + if (frame_rendered == 3) { - // Respects changes in resolution + // Buy and escape menu bug fix--respects resolution changes HWND new_hwnd = GetForegroundWindow(); RECT new_rect; if(GetClientRect(new_hwnd, &new_rect)) From 19dffe1485b51f8cbcc5fa93736ce9cf41ce6e67 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Fri, 20 Nov 2015 01:54:33 -0500 Subject: [PATCH 34/57] changed some coding that had potential for creating very slow memory leaks --- d3d9.cpp | 1 + main.cpp | 1 - rawinput.cpp | 32 ++++++++++++++++---------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/d3d9.cpp b/d3d9.cpp index 950d1c4..7a56c2d 100644 --- a/d3d9.cpp +++ b/d3d9.cpp @@ -223,6 +223,7 @@ bool InitD3D9Capture() wchar_t* wa = new wchar_t[size]; mbstowcs_s(&size, wa, 11, TEXT("\\d3d9.dll"), 11); wcscat_s(lpD3D9Path, MAX_PATH, wa); + delete[] wa; hD3D9Dll = GetModuleHandleW(lpD3D9Path); if (hD3D9Dll) diff --git a/main.cpp b/main.cpp index 1ac2c64..7c3a61d 100644 --- a/main.cpp +++ b/main.cpp @@ -45,7 +45,6 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) case DLL_PROCESS_ATTACH: // No need for a threaded entry if (!DisableThreadLibraryCalls(hInstance)) return 0; - else { // Get process and enable bug fixes for source games char szEXEPath[MAX_PATH]; diff --git a/rawinput.cpp b/rawinput.cpp index fb96c6d..a5b09a6 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -108,20 +108,20 @@ bool CRawInput::initInput(WCHAR* pwszError) { long clientx = client_rect.right >> 1; long clienty = client_rect.bottom >> 1; - LPPOINT client_lpPoint = new tagPOINT; - client_lpPoint->x = clientx; - client_lpPoint->y = clienty; - ClientToScreen(client_hwnd, client_lpPoint); + POINT client_point; + client_point.x = clientx; + client_point.y = clienty; + ClientToScreen(client_hwnd, &client_point); // Set screen center until SetCursorPos is called - CRawInput::hold_x = client_lpPoint->x; - CRawInput::hold_y = client_lpPoint->y; + CRawInput::hold_x = client_point.x; + CRawInput::hold_y = client_point.y; } // Raw input accumulators initialized to starting cursor position - LPPOINT defCor = new tagPOINT; - GetCursorPos(defCor); - CRawInput::set_x = defCor->x; - CRawInput::set_y = defCor->y; + POINT defCor; + GetCursorPos(&defCor); + CRawInput::set_x = defCor.x; + CRawInput::set_y = defCor.y; RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); @@ -258,12 +258,12 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { long newx = new_rect.right >> 1; long newy = new_rect.bottom >> 1; - LPPOINT new_lpPoint = new tagPOINT; - new_lpPoint->x = newx; - new_lpPoint->y = newy; - ClientToScreen(new_hwnd, new_lpPoint); - CRawInput::set_x = new_lpPoint->x; - CRawInput::set_y = new_lpPoint->y; + POINT new_Point; + new_Point.x = newx; + new_Point.y = newy; + ClientToScreen(new_hwnd, &new_Point); + CRawInput::set_x = new_Point.x; + CRawInput::set_y = new_Point.y; } } lpPoint->x = CRawInput::set_x; From 13dbb4ff80616a9dfe83389ec09aafa74d0aee27 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Fri, 20 Nov 2015 04:52:57 -0500 Subject: [PATCH 35/57] more rigorous thread closing --- d3d9.cpp | 10 +++++----- hook.h | 2 +- main.cpp | 10 +++++++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/d3d9.cpp b/d3d9.cpp index 7a56c2d..cab6888 100644 --- a/d3d9.cpp +++ b/d3d9.cpp @@ -201,10 +201,11 @@ void SetupD3D9(IDirect3DDevice9 *device) d3d9Present.Rehook(); d3d9SwapPresent.Rehook(); d3d9Reset.Rehook(); - if(bD3D9Ex) { - d3d9PresentEx.Rehook(); - d3d9ResetEx.Rehook(); - } + if(bD3D9Ex) + { + d3d9PresentEx.Rehook(); + d3d9ResetEx.Rehook(); + } swapChain->Release(); } @@ -258,7 +259,6 @@ bool InitD3D9Capture() d3d9EndScene.Rehook(); } - d3d9ex->Release(); } } diff --git a/hook.h b/hook.h index ac95222..db561e3 100644 --- a/hook.h +++ b/hook.h @@ -152,5 +152,5 @@ extern HWND hwndSender; extern bool bTargetAcquired; extern bool bCaptureThreadStop; extern bool bD3D9Hooked; -DWORD WINAPI CaptureThread(HANDLE hEntryPointThread); +DWORD WINAPI CaptureThread(HANDLE hDllMainThread); bool InitD3D9Capture(); \ No newline at end of file diff --git a/main.cpp b/main.cpp index 7c3a61d..10b76d6 100644 --- a/main.cpp +++ b/main.cpp @@ -63,9 +63,12 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) HANDLE hThread = NULL; HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); if (!(hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hDllMainThread, 0, 0))) + { CloseHandle(hDllMainThread); - else - hCaptureThread = hThread; + return 0; + } + hCaptureThread = hThread; + CloseHandle(hThread); } sourceEXE = true; @@ -90,9 +93,10 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) if (dummyEvent) CloseHandle(dummyEvent); - if (hwndSender) DestroyWindow(hwndSender); + if (hCaptureThread) + CloseHandle(hCaptureThread); } CRawInput::hookLibrary(false); From f1b1c0821203218d019e402a3e2225a34579fc90 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Sat, 21 Nov 2015 01:33:00 -0500 Subject: [PATCH 36/57] reverted back to only hooking EndScene for CS:GO bug fix as it does the job without needing to hook other D3D9 function, deleted some unused code and the debug outputs that hurt readability, made declaration and externs more efficient and consistent with scope, and made code style consistent --- README | 2 +- README.mdown | 2 +- RInput.vcproj | 4 +- d3d9.cpp | 190 +++++--------------------------------------------- hook.cpp | 34 ++++----- hook.h | 60 +++++++--------- main.cpp | 52 +++++++------- main.h | 13 ++-- rawinput.cpp | 95 ++++++++++--------------- rawinput.h | 34 ++++----- versioninfo.h | 4 +- 11 files changed, 145 insertions(+), 345 deletions(-) diff --git a/README b/README index 45c59ed..10e85e7 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.38 by Vols and Jezuz +RInput Library v1.39 by Vols and Jezuz -------------------------------------- RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. diff --git a/README.mdown b/README.mdown index fbd48ac..d3c1ff8 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.38 by Vols and Jezuz +# RInput Library v1.39 by Vols and Jezuz RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements diff --git a/RInput.vcproj b/RInput.vcproj index de36de4..309d2b8 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -78,7 +78,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="shlwapi.lib dxguid.lib psapi.lib" - Version="1.38-debug" + Version="1.39-debug" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" EnableUAC="true" @@ -186,7 +186,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="shlwapi.lib dxguid.lib psapi.lib" - Version="1.38" + Version="1.39" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" GenerateManifest="true" diff --git a/d3d9.cpp b/d3d9.cpp index cab6888..8b0214b 100644 --- a/d3d9.cpp +++ b/d3d9.cpp @@ -11,31 +11,11 @@ #include "hook.h" #include -// Prevent empty controlled statement warning, as it's intentional -#pragma warning(disable: 4390) - -typedef HRESULT (WINAPI *PRESENTPROC)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*); -typedef HRESULT (WINAPI *PRESENTEXPROC)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*, DWORD); -typedef HRESULT (WINAPI *SWAPPRESENTPROC)(IDirect3DSwapChain9*, const RECT*, const RECT*, HWND, const RGNDATA*, DWORD); - HookData d3d9EndScene; -HookData d3d9Reset; -HookData d3d9ResetEx; -HookData d3d9Present; -HookData d3d9PresentEx; -HookData d3d9SwapPresent; - -extern int frame_rendered; -extern CRITICAL_SECTION d3d9EndMutex; -extern bool bD3D9Hooked; -LPVOID lpCurrentDevice = NULL; -BOOL bD3D9Ex = FALSE; -HMODULE hD3D9Dll = NULL; - -void SetupD3D9(IDirect3DDevice9 *device); - -static int presentRecurse = 0; +static LPVOID lpCurrentDevice = NULL; +int consec_endscene = 0; +static HMODULE hD3D9Dll = NULL; typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); @@ -48,169 +28,26 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) if(lpCurrentDevice == NULL) { IDirect3D9 *d3d; + if(SUCCEEDED(device->GetDirect3D(&d3d))) - { - IDirect3D9 *d3d9ex; - if(bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) - d3d9ex->Release(); d3d->Release(); - } - - if(!bTargetAcquired) - { - lpCurrentDevice = device; - SetupD3D9(device); - bTargetAcquired = true; - } - } - - HRESULT hRes = device->EndScene(); - d3d9EndScene.Rehook(); - - LeaveCriticalSection(&d3d9EndMutex); - - return hRes; -} - -HRESULT STDMETHODCALLTYPE D3D9Present(IDirect3DDevice9 *device, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) -{ - d3d9Present.Unhook(); - - if (!presentRecurse) - { - if (frame_rendered < 3) - ++frame_rendered; - } - presentRecurse++; - HRESULT hRes = device->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); - presentRecurse--; - - d3d9Present.Rehook(); - - return hRes; -} - -HRESULT STDMETHODCALLTYPE D3D9PresentEx(IDirect3DDevice9Ex *device, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) -{ - d3d9PresentEx.Unhook(); - - if (!presentRecurse) - { - if (frame_rendered < 3) - ++frame_rendered; - } - presentRecurse++; - HRESULT hRes = device->PresentEx(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); - presentRecurse--; - - d3d9PresentEx.Rehook(); - - return hRes; -} - -HRESULT STDMETHODCALLTYPE D3D9SwapPresent(IDirect3DSwapChain9 *swap, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) -{ - d3d9SwapPresent.Unhook(); - - if(!presentRecurse) - { - if (frame_rendered < 3) - ++frame_rendered; - } - presentRecurse++; - HRESULT hRes = swap->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); - presentRecurse--; - - d3d9SwapPresent.Rehook(); - return hRes; -} - -typedef HRESULT(STDMETHODCALLTYPE *D3D9ResetPROC)(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params); - -HRESULT STDMETHODCALLTYPE D3D9Reset(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params) -{ - d3d9Reset.Unhook(); - - HRESULT hRes = device->Reset(params); - - if(lpCurrentDevice == NULL && !bTargetAcquired) - { lpCurrentDevice = device; - bTargetAcquired = true; } - if(lpCurrentDevice == device) - SetupD3D9(device); - - d3d9Reset.Rehook(); - - return hRes; -} + // EndScene is called twice per frame render + if (consec_endscene < 5) + ++consec_endscene; -HRESULT STDMETHODCALLTYPE D3D9ResetEx(IDirect3DDevice9Ex *device, D3DPRESENT_PARAMETERS *params, D3DDISPLAYMODEEX *fullscreenData) -{ - d3d9ResetEx.Unhook(); - d3d9Reset.Unhook(); - - HRESULT hRes = device->ResetEx(params, fullscreenData); - - if(lpCurrentDevice == NULL && !bTargetAcquired) - { - lpCurrentDevice = device; - bTargetAcquired = true; - bD3D9Ex = true; - } + HRESULT hRes = device->EndScene(); - if(lpCurrentDevice == device) - SetupD3D9(device); + d3d9EndScene.Rehook(); - d3d9Reset.Rehook(); - d3d9ResetEx.Rehook(); + LeaveCriticalSection(&d3d9EndMutex); return hRes; } -void SetupD3D9(IDirect3DDevice9 *device) -{ - IDirect3DSwapChain9 *swapChain = NULL; - if (SUCCEEDED(device->GetSwapChain(0, &swapChain))) - { - D3DPRESENT_PARAMETERS pp; - if (SUCCEEDED(swapChain->GetPresentParameters(&pp))) - ; //intentionally empty - - IDirect3D9 *d3d; - if (SUCCEEDED(device->GetDirect3D(&d3d))) - { - IDirect3D9 *d3d9ex; - if (bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) - d3d9ex->Release(); - d3d->Release(); - } - - d3d9Present.Hook(GetVTable(device, (68/4)), (FARPROC)D3D9Present); - if(bD3D9Ex) - { - FARPROC curPresentEx = GetVTable(device, (484/4)); - d3d9PresentEx.Hook(curPresentEx, (FARPROC)D3D9PresentEx); - d3d9ResetEx.Hook(GetVTable(device, (528/4)), (FARPROC)D3D9ResetEx); - } - d3d9Reset.Hook(GetVTable(device, (64/4)), (FARPROC)D3D9Reset); - d3d9SwapPresent.Hook(GetVTable(swapChain, (12/4)), (FARPROC)D3D9SwapPresent); - d3d9Present.Rehook(); - d3d9SwapPresent.Rehook(); - d3d9Reset.Rehook(); - if(bD3D9Ex) - { - d3d9PresentEx.Rehook(); - d3d9ResetEx.Rehook(); - } - - swapChain->Release(); - } -} - typedef IDirect3D9* (WINAPI*D3D9CREATEPROC)(UINT); typedef HRESULT (WINAPI*D3D9CREATEEXPROC)(UINT, IDirect3D9Ex**); @@ -227,14 +64,17 @@ bool InitD3D9Capture() delete[] wa; hD3D9Dll = GetModuleHandleW(lpD3D9Path); + if (hD3D9Dll) { D3D9CREATEEXPROC d3d9CreateEx = (D3D9CREATEEXPROC)GetProcAddress(hD3D9Dll, "Direct3DCreate9Ex"); + if (d3d9CreateEx) { HRESULT hRes; IDirect3D9Ex *d3d9ex; + if (SUCCEEDED(hRes = (*d3d9CreateEx)(D3D_SDK_VERSION, &d3d9ex))) { D3DPRESENT_PARAMETERS pp; @@ -247,6 +87,7 @@ bool InitD3D9Capture() pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; IDirect3DDevice9Ex *deviceEx; + if (SUCCEEDED(hRes = d3d9ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, hwndSender, D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_NOWINDOWCHANGES, &pp, NULL, &deviceEx))) { bSuccess = true; @@ -259,6 +100,7 @@ bool InitD3D9Capture() d3d9EndScene.Rehook(); } + d3d9ex->Release(); } } @@ -270,6 +112,8 @@ bool InitD3D9Capture() void CheckD3D9Capture() { EnterCriticalSection(&d3d9EndMutex); + d3d9EndScene.Rehook(true); + LeaveCriticalSection(&d3d9EndMutex); } \ No newline at end of file diff --git a/hook.cpp b/hook.cpp index f55b9dc..5599fa3 100644 --- a/hook.cpp +++ b/hook.cpp @@ -9,15 +9,11 @@ ********************************************************************************/ #include "hook.h" -#include HWND hwndSender = NULL; -bool bTargetAcquired = false; -extern bool bCaptureThreadStop; -CRITICAL_SECTION d3d9EndMutex; -void CheckD3D9Capture(); -bool bD3D9Hooked = false; +static bool bD3D9Hooked = false; HANDLE dummyEvent = NULL; +CRITICAL_SECTION d3d9EndMutex; inline bool AttemptToHookSomething() { @@ -25,13 +21,12 @@ inline bool AttemptToHookSomething() return false; bool bFoundSomethingToHook = false; - if (!bD3D9Hooked) + + if (!bD3D9Hooked && InitD3D9Capture()) { - if (InitD3D9Capture()) - { - bFoundSomethingToHook = true; - bD3D9Hooked = true; - } + bFoundSomethingToHook = true; + + bD3D9Hooked = true; } else CheckD3D9Capture(); @@ -39,8 +34,6 @@ inline bool AttemptToHookSomething() return bFoundSomethingToHook; } -#define SENDER_WINDOWCLASS TEXT("RInputD3DSender") - static inline HWND CreateDummyWindow(LPCTSTR lpClass, LPCTSTR lpName) { return CreateWindowEx (0, lpClass, lpName, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL); @@ -53,11 +46,12 @@ static DWORD WINAPI DummyWindowThread(LPVOID lpBla) wc.style = CS_OWNDC; wc.hInstance = g_hInstance; wc.lpfnWndProc = (WNDPROC)DefWindowProc; - - wc.lpszClassName = SENDER_WINDOWCLASS; + wc.lpszClassName = TEXT("RInputD3DSender"); + if (RegisterClass(&wc)) { - hwndSender = CreateDummyWindow(SENDER_WINDOWCLASS, NULL); + hwndSender = CreateDummyWindow(TEXT("RInputD3DSender"), NULL); + if (!hwndSender) return 0; } @@ -65,6 +59,7 @@ static DWORD WINAPI DummyWindowThread(LPVOID lpBla) return 0; MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); @@ -90,19 +85,18 @@ DWORD WINAPI CaptureThread(HANDLE hDllMainThread) DWORD bla; HANDLE hWindowThread = CreateThread(NULL, 0, DummyWindowThread, NULL, 0, &bla); + if (!hWindowThread) return 0; CloseHandle(hWindowThread); while(!AttemptToHookSomething()) - { Sleep(50); - } for (size_t n = 0; !bCaptureThreadStop; ++n) { - // Less frequent loop interations reduce system resource usage + // Less frequent loop interations reduce resource usage if (n % 16 == 0) AttemptToHookSomething(); // Overall interval for checking EndScene hook is still 4000ms Sleep(250); diff --git a/hook.h b/hook.h index db561e3..f4e7daa 100644 --- a/hook.h +++ b/hook.h @@ -1,12 +1,5 @@ -/******************************************************************************** - hook.h, hook.cpp, and d3d9.cpp contain the code for fixing the bugs in - the CS:GO buy and escape menus, and are only called for CS:GO. - - Copyright (C) 2012 Hugh Bailey - - This new code was adapted from the Open Broadcaster Software source, - obtained from https://github.com/jp9000/OBS -********************************************************************************/ +#ifndef _HOOK_H_ +#define _HOOK_H_ #include #include @@ -14,10 +7,20 @@ #define PSAPI_VERSION 1 #include -#pragma intrinsic(memcpy, memset) +#pragma intrinsic(memcpy) #include +extern HINSTANCE g_hInstance; +extern CRITICAL_SECTION d3d9EndMutex; +extern HANDLE dummyEvent; +extern HWND hwndSender; +extern bool bCaptureThreadStop; + +bool InitD3D9Capture(); +DWORD WINAPI CaptureThread(HANDLE hDllMainThread); +void CheckD3D9Capture(); + typedef unsigned long UPARAM; class HookData @@ -37,14 +40,13 @@ class HookData { if (bHooked) { - if (funcIn == func) + if ((funcIn == func) && (hookFunc != hookFuncIn)) { - if (hookFunc != hookFuncIn) - { - hookFunc = hookFuncIn; - Rehook(); - return true; - } + hookFunc = hookFuncIn; + + Rehook(); + + return true; } Unhook(); @@ -54,6 +56,7 @@ class HookData hookFunc = hookFuncIn; DWORD oldProtect; + if (!VirtualProtect((LPVOID)func, 14, PAGE_EXECUTE_READWRITE, &oldProtect)) return false; @@ -62,6 +65,7 @@ class HookData CHAR *modName, *ourName; CHAR szModName[MAX_PATH]; CHAR szOurName[MAX_PATH]; + DWORD memAddress; MEMORY_BASIC_INFORMATION mem; @@ -105,8 +109,8 @@ class HookData UPARAM startAddr = UPARAM(func); UPARAM targetAddr = UPARAM(hookFunc); - ULONG64 offset, diff; + ULONG64 offset, diff; offset = targetAddr - (startAddr + 5); if (startAddr + 5 > targetAddr) @@ -115,14 +119,10 @@ class HookData diff = targetAddr - startAddr + 5; DWORD oldProtect; - - { - VirtualProtect((LPVOID)func, 5, PAGE_EXECUTE_READWRITE, &oldProtect); - - LPBYTE addrData = (LPBYTE)func; - *addrData = 0xE9; - *(DWORD*)(addrData+1) = DWORD(offset); - } + VirtualProtect((LPVOID)func, 5, PAGE_EXECUTE_READWRITE, &oldProtect); + LPBYTE addrData = (LPBYTE)func; + *addrData = 0xE9; + *(DWORD*)(addrData+1) = DWORD(offset); bHooked = true; } @@ -147,10 +147,4 @@ inline FARPROC GetVTable(LPVOID ptr, UINT funcOffset) return (FARPROC)(*(vtable+funcOffset)); } -extern HINSTANCE g_hInstance; -extern HWND hwndSender; -extern bool bTargetAcquired; -extern bool bCaptureThreadStop; -extern bool bD3D9Hooked; -DWORD WINAPI CaptureThread(HANDLE hDllMainThread); -bool InitD3D9Capture(); \ No newline at end of file +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index 10b76d6..bae62f1 100644 --- a/main.cpp +++ b/main.cpp @@ -32,9 +32,12 @@ #include "main.h" #include -bool bCaptureThreadStop = false; +HINSTANCE g_hInstance = NULL; +int n_sourceEXE = 0; +bool sourceEXE = false; HANDLE hCaptureThread = NULL; -extern HANDLE dummyEvent; +bool bCaptureThreadStop = false; +static void unloadLibrary(); int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { @@ -44,17 +47,21 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { case DLL_PROCESS_ATTACH: // No need for a threaded entry - if (!DisableThreadLibraryCalls(hInstance)) return 0; - { - // Get process and enable bug fixes for source games + if (!DisableThreadLibraryCalls(hInstance)) + return 0; + + { // Get process and enable bug fixes for source games char szEXEPath[MAX_PATH]; - // Bug fixes now limited to source games that have been tested - char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath)); PathStripPathA(szEXEPath); + + // Bug fixes now limited to source games that have been tested + char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; + for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) { ++n_sourceEXE; + if ((std::string)szEXEPath == (std::string)*iSource_exes) { // Start D3D hooking for CS:GO @@ -62,39 +69,42 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { HANDLE hThread = NULL; HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); + if (!(hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hDllMainThread, 0, 0))) { CloseHandle(hDllMainThread); return 0; } + hCaptureThread = hThread; CloseHandle(hThread); } sourceEXE = true; + break; } } } - g_hInstance = hInstance; -#ifdef _DEBUG - OutputDebugString("Loaded RInput"); -#endif + g_hInstance = hInstance; break; case DLL_PROCESS_DETACH: - // Stop D3D hooking for CS:GO - if (n_sourceEXE == 1) + if (n_sourceEXE == 1) // Stop D3D hooking for CS:GO { + DeleteCriticalSection(&d3d9EndMutex); + bCaptureThreadStop = true; WaitForSingleObject(hCaptureThread, 300); if (dummyEvent) CloseHandle(dummyEvent); + if (hwndSender) DestroyWindow(hwndSender); + if (hCaptureThread) CloseHandle(hCaptureThread); } @@ -102,9 +112,6 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) CRawInput::hookLibrary(false); CRawInput::unload(); -#ifdef _DEBUG - OutputDebugString("Unloaded RInput"); -#endif break; } @@ -113,10 +120,6 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) extern "C" __declspec(dllexport) void entryPoint() { -#ifdef _DEBUG - OutputDebugString("entryPoint called"); -#endif - HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENTNAME); if (!hEvent) displayError(L"Could not open interprocess communication event."); @@ -140,10 +143,6 @@ extern "C" __declspec(dllexport) void entryPoint() if (!CRawInput::pollInput()) displayError(L"Failed to poll mouse input"); - -#ifdef _DEBUG - OutputDebugString("Finished entryPoint"); -#endif } // Validate that we are working with at least Windows XP @@ -157,13 +156,16 @@ inline bool validateVersion() void displayError(WCHAR* pwszError) { MessageBoxW(NULL, pwszError, L"Raw Input error!", MB_ICONERROR | MB_OK); + CRawInput::hookLibrary(false); + unloadLibrary(); } void unloadLibrary() { - __asm { + __asm + { push -2 push 0 push g_hInstance diff --git a/main.h b/main.h index 6208c5a..39d9a40 100644 --- a/main.h +++ b/main.h @@ -13,17 +13,12 @@ #define KERNEL_LIB L"kernel32.dll" #include "rawinput.h" // Raw input class -#include "hook.h" // D3D9 hooking +#include "hook.h" // D3D9 hooking class -HINSTANCE g_hInstance = NULL; -int n_sourceEXE = 0; -bool sourceEXE = false; -int frame_rendered = 0; - -// Only handle the hooking and dll functions here extern "C" __declspec(dllexport) void entryPoint(); -inline bool validateVersion(); -void unloadLibrary(); + void displayError(WCHAR* pwszError); +inline bool validateVersion(); + #endif \ No newline at end of file diff --git a/rawinput.cpp b/rawinput.cpp index a5b09a6..1f7d5d2 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -33,26 +33,22 @@ #pragma intrinsic(memset) -// Define the to be hooked functions +// Define functions that are to be hooked and detoured extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpSetCursorPos(int x, int y), SetCursorPos); // Initialize static variables -bool CRawInput::bRegistered = false; HWND CRawInput::hwndInput = NULL; -long CRawInput::x = 0; -long CRawInput::y = 0; -long CRawInput::set_x = 0; -long CRawInput::set_y = 0; long CRawInput::hold_x = 0; long CRawInput::hold_y = 0; +long CRawInput::set_x = 0; +long CRawInput::set_y = 0; +bool CRawInput::bRegistered = false; +long CRawInput::x = 0; +long CRawInput::y = 0; int CRawInput::SCP = 0; -bool CRawInput::GCP = false; int CRawInput::consecG = 0; - -extern int n_sourceEXE; -extern bool sourceEXE; -extern int frame_rendered; +bool CRawInput::GCP = false; bool CRawInput::initialize(WCHAR* pwszError) { @@ -70,7 +66,6 @@ bool CRawInput::initWindow(WCHAR* pwszError) // Register the window to catch WM_INPUT events WNDCLASSEX wcex; memset(&wcex, 0, sizeof(WNDCLASSEX)); - wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpfnWndProc = (WNDPROC)wpInput; wcex.lpszClassName = INPUTWINDOW; @@ -83,16 +78,13 @@ bool CRawInput::initWindow(WCHAR* pwszError) // Create the window to catch WM_INPUT events CRawInput::hwndInput = CreateWindowEx(NULL, INPUTWINDOW, INPUTWINDOW, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + if (!CRawInput::hwndInput) { lstrcpyW(pwszError, L"Failed to create input window!"); return false; } -#ifdef _DEBUG - OutputDebugString("Created Raw Input Window"); -#endif - // Unregister the window class UnregisterClass(INPUTWINDOW, NULL); @@ -104,7 +96,8 @@ bool CRawInput::initInput(WCHAR* pwszError) // Now behaves correctly with windowed mode HWND client_hwnd = GetForegroundWindow(); RECT client_rect; - if(GetClientRect(client_hwnd, &client_rect)) + + if (GetClientRect(client_hwnd, &client_rect)) { long clientx = client_rect.right >> 1; long clienty = client_rect.bottom >> 1; @@ -125,7 +118,6 @@ bool CRawInput::initInput(WCHAR* pwszError) RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); - // New flag allows accumulation to be maintained while alt-tabbed rMouse.dwFlags = RIDEV_INPUTSINK; rMouse.hwndTarget = CRawInput::hwndInput; @@ -155,10 +147,8 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA { switch (message) { - case WM_INPUT:{ -#ifdef _DEBUG - OutputDebugString("WM_INPUT event"); -#endif + case WM_INPUT: + { UINT uiSize = RAWPTRSIZE; static unsigned char lpb[RAWPTRSIZE]; RAWINPUT* rwInput; @@ -173,11 +163,14 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA CRawInput::y += rwInput->data.mouse.lLastY; } } + break; } + case WM_DESTROY: PostQuitMessage(0); break; + default: return DefWindowProc(hWnd, message, wParam, lParam); } @@ -187,7 +180,8 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA int __stdcall CRawInput::hSetCursorPos(int x, int y) { // Skips unnecessary second SetCursorPos call for source games - if (!CRawInput::SCP && !TrmpSetCursorPos(x, y)) return 1; + if (!CRawInput::SCP && !TrmpSetCursorPos(x, y)) + return 1; CRawInput::set_x = (long)x; CRawInput::set_y = (long)y; @@ -195,11 +189,14 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) if (sourceEXE) { CRawInput::consecG = 0; + // Alt-tab bug fix if ((CRawInput::set_x == 0) && (CRawInput::set_y == 0)) CRawInput::GCP = true; + // Console bug fix ++CRawInput::SCP; + if (CRawInput::SCP == 1) { CRawInput::set_x -= CRawInput::x; @@ -207,18 +204,17 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) } else if (CRawInput::SCP == 2) { - frame_rendered = 0; + consec_endscene = 0; + CRawInput::GCP = false; + CRawInput::SCP = 0; + CRawInput::hold_x = CRawInput::set_x; CRawInput::hold_y = CRawInput::set_y; } } -#ifdef _DEBUG - OutputDebugString("Set coordinates"); -#endif - return 0; } @@ -229,16 +225,18 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) CRawInput::set_y += CRawInput::y; CRawInput::SCP = 0; - // Bug fix for cursor hitting side of screen in TF2 backpack + if (CRawInput::consecG < 2) ++CRawInput::consecG; - // Changed to be active for hl2.exe only, the exe that TF2 uses + + // Bug fix for cursor hitting side of screen in TF2 backpack if ((n_sourceEXE == 2) && (CRawInput::consecG == 2)) { if (CRawInput::set_x >= CRawInput::hold_x << 1) CRawInput::set_x = (CRawInput::hold_x << 1) - 1; else if (CRawInput::set_x < 0) CRawInput::set_x = 0; + if (CRawInput::set_y >= CRawInput::hold_y << 1) CRawInput::set_y = (CRawInput::hold_y << 1) - 1; else if (CRawInput::set_y < 0) @@ -248,12 +246,12 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) // Alt-tab bug fix if (!CRawInput::GCP) { - // Allows 3 frames w/o SetCursorPos to be safe, 2 gives loss - if (frame_rendered == 3) + // Buy and escape menu bug fix--respects resolution changes + if (consec_endscene == 5) { - // Buy and escape menu bug fix--respects resolution changes HWND new_hwnd = GetForegroundWindow(); RECT new_rect; + if(GetClientRect(new_hwnd, &new_rect)) { long newx = new_rect.right >> 1; @@ -266,6 +264,7 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) CRawInput::set_y = new_Point.y; } } + lpPoint->x = CRawInput::set_x; lpPoint->y = CRawInput::set_y; } @@ -279,10 +278,6 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) CRawInput::x = 0; CRawInput::y = 0; -#ifdef _DEBUG - OutputDebugString("Returned coordinates"); -#endif - return 0; } @@ -292,25 +287,11 @@ bool CRawInput::hookLibrary(bool bInstall) { if (!DetourFunctionWithTrampoline((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos) || !DetourFunctionWithTrampoline((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos)) return false; -#ifdef _DEBUG - else - OutputDebugString("Hooked GetCursorPos and SetCursorPos"); -#endif } else { - if (DetourRemove((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos)) - { -#ifdef _DEBUG - OutputDebugString("Removed GetCursorPos hook"); -#endif - } - if (DetourRemove((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos)) - { -#ifdef _DEBUG - OutputDebugString("Removed SetCursorPos hook"); -#endif - } + DetourRemove((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos); + DetourRemove((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos); } return true; @@ -322,18 +303,12 @@ void CRawInput::unload() { RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); - rMouse.dwFlags |= RIDEV_REMOVE; rMouse.hwndTarget = NULL; rMouse.usUsagePage = 0x01; rMouse.usUsage = 0x02; - RegisterRawInputDevices(&rMouse, 1, sizeof(RAWINPUTDEVICE)); - DestroyWindow(hwndInput); -#ifdef _DEBUG - OutputDebugString("Unregistered mouse device"); - OutputDebugString("Closed Input Window"); -#endif + DestroyWindow(hwndInput); } } \ No newline at end of file diff --git a/rawinput.h b/rawinput.h index fc9c781..6f0a26b 100644 --- a/rawinput.h +++ b/rawinput.h @@ -8,8 +8,12 @@ #define RAWPTRSIZE 40 #define INPUTWINDOW "RInput" -// Prevent warning level 4 warnings for detouring -#pragma warning(disable: 4100) +#pragma warning(disable: 4100) // Prevent warning level 4 warnings for detouring + +extern void displayError(WCHAR* pwszError); +extern bool sourceEXE; +extern int n_sourceEXE; +extern int consec_endscene; /** * Note from original author (abort): @@ -18,46 +22,38 @@ * To keep the performance as high as possible, I decided not to work with storing the class instance through Win32 API. * Feel free to rewrite this to something more clean in coding terms :). */ -class CRawInput { +class CRawInput +{ public: // Initialize raw input static bool initialize(WCHAR* pwszError); - // Enables or disables the hooking static bool hookLibrary(bool bInstall); - // Hooked functions handling static int __stdcall hGetCursorPos(LPPOINT lpPoint); static int __stdcall hSetCursorPos(int x, int y); - // Poll input static unsigned int pollInput(); - // Input window proc static LRESULT __stdcall wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - // Initialization static bool initWindow(WCHAR* pwszError); static bool initInput(WCHAR* pwszError); - // Unload raw input static void unload(); private: - static long x; - static long y; - static long set_x; - static long set_y; + static HWND hwndInput; static long hold_x; static long hold_y; + static long set_x; + static long set_y; + static bool bRegistered; + static long x; + static long y; static int SCP; - static bool GCP; static int consecG; - - static HWND hwndInput; - static bool bRegistered; + static bool GCP; }; -extern void displayError(WCHAR* pwszError); - #endif \ No newline at end of file diff --git a/versioninfo.h b/versioninfo.h index 5bad92d..791e1b1 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.38" -#define RINPUTFVER 1,38 +#define RINPUTVER "v1.39" +#define RINPUTFVER 1,39 #define IDI_ICON 101 #define RINPUTINT "RInput" From 05a5acb28858df79feb9d9ac585e7ed90cd4fe54 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Sat, 21 Nov 2015 05:19:04 -0500 Subject: [PATCH 37/57] reverted some changes to declare functions in main.h that may interact with the RInput.exe injector --- main.cpp | 2 -- main.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index bae62f1..5189983 100644 --- a/main.cpp +++ b/main.cpp @@ -32,12 +32,10 @@ #include "main.h" #include -HINSTANCE g_hInstance = NULL; int n_sourceEXE = 0; bool sourceEXE = false; HANDLE hCaptureThread = NULL; bool bCaptureThreadStop = false; -static void unloadLibrary(); int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { diff --git a/main.h b/main.h index 39d9a40..77cefc9 100644 --- a/main.h +++ b/main.h @@ -17,7 +17,9 @@ extern "C" __declspec(dllexport) void entryPoint(); +HINSTANCE g_hInstance = NULL; void displayError(WCHAR* pwszError); +void unloadLibrary(); inline bool validateVersion(); From 735aeb9855f94b05985a474a3800b47aafc46c44 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 1 Dec 2015 04:35:03 -0500 Subject: [PATCH 38/57] readded more reliable D3D Present hooks, made TF2 detectable from other hl2.exe games, and changed in-code and compiler optimizations --- README | 2 +- README.mdown | 2 +- RInput.vcproj | 15 +++-- d3d9.cpp | 156 ++++++++++++++++++++++++++++++++++++++++++++++++-- main.cpp | 45 ++++++++++++--- rawinput.cpp | 88 ++++++++++++++++------------ rawinput.h | 2 +- versioninfo.h | 4 +- 8 files changed, 253 insertions(+), 61 deletions(-) diff --git a/README b/README index 10e85e7..f41c2c9 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.39 by Vols and Jezuz +RInput Library v1.40 by Vols and Jezuz -------------------------------------- RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. diff --git a/README.mdown b/README.mdown index d3c1ff8..744bf37 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.39 by Vols and Jezuz +# RInput Library v1.40 by Vols and Jezuz RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements diff --git a/RInput.vcproj b/RInput.vcproj index 309d2b8..de64c34 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -42,7 +42,7 @@ +typedef HRESULT (WINAPI *PRESENTPROC)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*); +typedef HRESULT (WINAPI *PRESENTEXPROC)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*, DWORD); +typedef HRESULT (WINAPI *SWAPPRESENTPROC)(IDirect3DSwapChain9*, const RECT*, const RECT*, HWND, const RGNDATA*, DWORD); + HookData d3d9EndScene; +HookData d3d9Reset; +HookData d3d9ResetEx; +HookData d3d9Present; +HookData d3d9PresentEx; +HookData d3d9SwapPresent; static LPVOID lpCurrentDevice = NULL; -int consec_endscene = 0; -static HMODULE hD3D9Dll = NULL; +BOOL bD3D9Ex = FALSE; +void SetupD3D9(IDirect3DDevice9 *device); +static int presentRecurse = 0; +int consec_frames = 0; +HMODULE hD3D9Dll = NULL; typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); @@ -30,15 +42,19 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) IDirect3D9 *d3d; if(SUCCEEDED(device->GetDirect3D(&d3d))) + { + IDirect3D9 *d3d9ex; + + if(bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) + d3d9ex->Release(); + d3d->Release(); + } lpCurrentDevice = device; + SetupD3D9(device); } - // EndScene is called twice per frame render - if (consec_endscene < 5) - ++consec_endscene; - HRESULT hRes = device->EndScene(); d3d9EndScene.Rehook(); @@ -48,6 +64,134 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) return hRes; } +HRESULT STDMETHODCALLTYPE D3D9Present(IDirect3DDevice9 *device, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) +{ + d3d9Present.Unhook(); + + if (!presentRecurse) + { + if (consec_frames < 3) + ++consec_frames; + } + + presentRecurse++; + HRESULT hRes = device->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); + presentRecurse--; + + d3d9Present.Rehook(); + + return hRes; +} + +HRESULT STDMETHODCALLTYPE D3D9PresentEx(IDirect3DDevice9Ex *device, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) +{ + d3d9PresentEx.Unhook(); + + if (!presentRecurse) + { + if (consec_frames < 3) + ++consec_frames; + } + + presentRecurse++; + HRESULT hRes = device->PresentEx(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); + presentRecurse--; + + d3d9PresentEx.Rehook(); + + return hRes; +} + +HRESULT STDMETHODCALLTYPE D3D9SwapPresent(IDirect3DSwapChain9 *swap, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) +{ + d3d9SwapPresent.Unhook(); + + presentRecurse++; + HRESULT hRes = swap->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); + presentRecurse--; + + d3d9SwapPresent.Rehook(); + + return hRes; +} + +typedef HRESULT(STDMETHODCALLTYPE *D3D9ResetPROC)(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params); + +HRESULT STDMETHODCALLTYPE D3D9Reset(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params) +{ + d3d9Reset.Unhook(); + + HRESULT hRes = device->Reset(params); + + SetupD3D9(device); + + d3d9Reset.Rehook(); + + return hRes; +} + + +HRESULT STDMETHODCALLTYPE D3D9ResetEx(IDirect3DDevice9Ex *device, D3DPRESENT_PARAMETERS *params, D3DDISPLAYMODEEX *fullscreenData) +{ + d3d9ResetEx.Unhook(); + d3d9Reset.Unhook(); + + HRESULT hRes = device->ResetEx(params, fullscreenData); + + if(lpCurrentDevice == NULL) + bD3D9Ex = true; + + SetupD3D9(device); + + d3d9Reset.Rehook(); + d3d9ResetEx.Rehook(); + + return hRes; +} + +void SetupD3D9(IDirect3DDevice9 *device) +{ + IDirect3DSwapChain9 *swapChain = NULL; + + if (SUCCEEDED(device->GetSwapChain(0, &swapChain))) + { + IDirect3D9 *d3d; + + if (SUCCEEDED(device->GetDirect3D(&d3d))) + { + IDirect3D9 *d3d9ex; + + if (bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) + d3d9ex->Release(); + + d3d->Release(); + } + + d3d9Present.Hook(GetVTable(device, (68/4)), (FARPROC)D3D9Present); + + if(bD3D9Ex) + { + FARPROC curPresentEx = GetVTable(device, (484/4)); + d3d9PresentEx.Hook(curPresentEx, (FARPROC)D3D9PresentEx); + d3d9ResetEx.Hook(GetVTable(device, (528/4)), (FARPROC)D3D9ResetEx); + } + + d3d9Reset.Hook(GetVTable(device, (64/4)), (FARPROC)D3D9Reset); + d3d9SwapPresent.Hook(GetVTable(swapChain, (12/4)), (FARPROC)D3D9SwapPresent); + d3d9Present.Rehook(); + d3d9SwapPresent.Rehook(); + d3d9Reset.Rehook(); + + if(bD3D9Ex) + { + d3d9PresentEx.Rehook(); + d3d9ResetEx.Rehook(); + } + + swapChain->Release(); + } +} + typedef IDirect3D9* (WINAPI*D3D9CREATEPROC)(UINT); typedef HRESULT (WINAPI*D3D9CREATEEXPROC)(UINT, IDirect3D9Ex**); diff --git a/main.cpp b/main.cpp index 5189983..1dce5e8 100644 --- a/main.cpp +++ b/main.cpp @@ -48,13 +48,16 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) if (!DisableThreadLibraryCalls(hInstance)) return 0; + g_hInstance = hInstance; + { // Get process and enable bug fixes for source games char szEXEPath[MAX_PATH]; GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath)); PathStripPathA(szEXEPath); - // Bug fixes now limited to source games that have been tested + // Bug fixes now limited to tested source games char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; + bool b_sourceEXE = false; for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) { @@ -62,9 +65,34 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) if ((std::string)szEXEPath == (std::string)*iSource_exes) { - // Start D3D hooking for CS:GO - if (n_sourceEXE == 1) + sourceEXE = true; + + if (n_sourceEXE <= 2) // CS:GO and TF2 D3D hooks { + if (n_sourceEXE == 2) + { + // Make sure hl2.exe is TF2 + char TF2path[MAX_PATH]; + GetModuleFileNameA(NULL, TF2path, sizeof(TF2path)); + PathRemoveFileSpecA(TF2path); + std::string sTF2path = (std::string)TF2path; + char testTF2[16]; + + for (size_t j = 1; j <= 15; ++j) + testTF2[j] = TF2path[sTF2path.size() + j - 15]; + + char tf2[16] = "Team Fortress 2"; + + for (int k = 1; k < 16; ++k) + { + if (testTF2[k] != tf2[k]) + { + b_sourceEXE = true; + goto skipd3d; + } + } + } + HANDLE hThread = NULL; HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); @@ -78,19 +106,20 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) CloseHandle(hThread); } - sourceEXE = true; - + skipd3d: break; } } - } - g_hInstance = hInstance; + if (b_sourceEXE) + n_sourceEXE = 5; + + } break; case DLL_PROCESS_DETACH: - if (n_sourceEXE == 1) // Stop D3D hooking for CS:GO + if (n_sourceEXE <= 2) // Stop D3D hooking for CS:GO and TF2 { DeleteCriticalSection(&d3d9EndMutex); diff --git a/rawinput.cpp b/rawinput.cpp index 1f7d5d2..4eba97e 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -99,8 +99,8 @@ bool CRawInput::initInput(WCHAR* pwszError) if (GetClientRect(client_hwnd, &client_rect)) { - long clientx = client_rect.right >> 1; - long clienty = client_rect.bottom >> 1; + long clientx = client_rect.right / 2; + long clienty = client_rect.bottom / 2; POINT client_point; client_point.x = clientx; client_point.y = clienty; @@ -148,30 +148,31 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA switch (message) { case WM_INPUT: - { - UINT uiSize = RAWPTRSIZE; - static unsigned char lpb[RAWPTRSIZE]; - RAWINPUT* rwInput; - - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &uiSize, RAWINPUTHDRSIZE) != -1) { - rwInput = (RAWINPUT*)lpb; + UINT uiSize = RAWPTRSIZE; + static unsigned char lpb[RAWPTRSIZE]; + RAWINPUT* rwInput; - if (!rwInput->header.dwType) + if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &uiSize, RAWINPUTHDRSIZE) != -1) { - CRawInput::x += rwInput->data.mouse.lLastX; - CRawInput::y += rwInput->data.mouse.lLastY; + rwInput = (RAWINPUT*)lpb; + + if (!rwInput->header.dwType) + { + CRawInput::x += rwInput->data.mouse.lLastX; + CRawInput::y += rwInput->data.mouse.lLastY; + } } - } - break; - } + break; + } case WM_DESTROY: PostQuitMessage(0); break; - default: return DefWindowProc(hWnd, message, wParam, lParam); + default: + return DefWindowProc(hWnd, message, wParam, lParam); } return 0; @@ -188,7 +189,16 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) if (sourceEXE) { - CRawInput::consecG = 0; + if (n_sourceEXE == 2) + { + if (((consec_frames == 3) && (CRawInput::consecG == 2)) && !CRawInput::GCP) + goto skipGCPreset; + + CRawInput::consecG = 0; + } + + skipGCPreset: + consec_frames = 0; // Alt-tab bug fix if ((CRawInput::set_x == 0) && (CRawInput::set_y == 0)) @@ -204,8 +214,6 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) } else if (CRawInput::SCP == 2) { - consec_endscene = 0; - CRawInput::GCP = false; CRawInput::SCP = 0; @@ -226,36 +234,43 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) CRawInput::SCP = 0; - if (CRawInput::consecG < 2) - ++CRawInput::consecG; - - // Bug fix for cursor hitting side of screen in TF2 backpack - if ((n_sourceEXE == 2) && (CRawInput::consecG == 2)) + if (n_sourceEXE == 2) { - if (CRawInput::set_x >= CRawInput::hold_x << 1) - CRawInput::set_x = (CRawInput::hold_x << 1) - 1; - else if (CRawInput::set_x < 0) - CRawInput::set_x = 0; - - if (CRawInput::set_y >= CRawInput::hold_y << 1) - CRawInput::set_y = (CRawInput::hold_y << 1) - 1; - else if (CRawInput::set_y < 0) - CRawInput::set_y = 0; + if (CRawInput::consecG < 2) + ++CRawInput::consecG; + + // Bug fix for cursor hitting side of screen in TF2 backpack + if (CRawInput::consecG == 2) + { + if (CRawInput::set_x >= CRawInput::hold_x << 1) + CRawInput::set_x = (CRawInput::hold_x << 1) - 1; + else if (CRawInput::set_x < 0) + CRawInput::set_x = 0; + + if (CRawInput::set_y >= CRawInput::hold_y << 1) + CRawInput::set_y = (CRawInput::hold_y << 1) - 1; + else if (CRawInput::set_y < 0) + CRawInput::set_y = 0; + } } // Alt-tab bug fix if (!CRawInput::GCP) { // Buy and escape menu bug fix--respects resolution changes - if (consec_endscene == 5) + if (consec_frames == 3) { + // Needed to not break backpack in TF2 + if (CRawInput::consecG == 2) + goto skiprecenter; + HWND new_hwnd = GetForegroundWindow(); RECT new_rect; if(GetClientRect(new_hwnd, &new_rect)) { - long newx = new_rect.right >> 1; - long newy = new_rect.bottom >> 1; + long newx = new_rect.right / 2; + long newy = new_rect.bottom / 2; POINT new_Point; new_Point.x = newx; new_Point.y = newy; @@ -265,6 +280,7 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) } } + skiprecenter: lpPoint->x = CRawInput::set_x; lpPoint->y = CRawInput::set_y; } diff --git a/rawinput.h b/rawinput.h index 6f0a26b..4537099 100644 --- a/rawinput.h +++ b/rawinput.h @@ -13,7 +13,7 @@ extern void displayError(WCHAR* pwszError); extern bool sourceEXE; extern int n_sourceEXE; -extern int consec_endscene; +extern int consec_frames; /** * Note from original author (abort): diff --git a/versioninfo.h b/versioninfo.h index 791e1b1..6634c6e 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.39" -#define RINPUTFVER 1,39 +#define RINPUTVER "v1.40" +#define RINPUTFVER 1,40 #define IDI_ICON 101 #define RINPUTINT "RInput" From 4a1a180c7a5fc11087dc7961c8cc3f997ffb82e4 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 3 Dec 2015 04:27:59 -0500 Subject: [PATCH 39/57] Reverted again back to only hooking EndScene as it was made to be equivalent to Present hooking but with less code and overhead, several bug fixes added as noted in the code and release, and some repeated tasks were made into new rawinput class functions --- README | 4 +- README.mdown | 4 +- RInput.vcproj | 14 ++-- d3d9.cpp | 159 ++--------------------------------- hook.cpp | 10 ++- main.cpp | 112 ++++++++++++------------- main.h | 19 +++-- rawinput.cpp | 227 ++++++++++++++++++++++++++++++++++++-------------- rawinput.h | 40 ++++++--- 9 files changed, 283 insertions(+), 306 deletions(-) diff --git a/README b/README index f41c2c9..dbabab8 100644 --- a/README +++ b/README @@ -24,9 +24,9 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Add the Microsoft DirectX SDK(https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) - Compiled with Multi-threaded DLL runtime library for linking - It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No -- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well +- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allow D3D9 to be hooked - Set the Entry Point as DllMain -- Add shlwapi.lib, dxguid.lib, and psapi.lib as an Additional Dependency in the Linker options +- Add shlwapi.lib, dxguid.lib, psapi.lib, and comctl32.lib as an Additional Dependency in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/README.mdown b/README.mdown index 744bf37..1f52064 100644 --- a/README.mdown +++ b/README.mdown @@ -20,9 +20,9 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Add the Microsoft DirectX SDK(https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) - Compiled with Multi-threaded DLL runtime library for linking - It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No -- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well +- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allow D3D9 to be hooked - Set the Entry Point as DllMain -- Add shlwapi.lib, dxguid.lib, and psapi.lib as an Additional Dependency in the Linker options +- Add shlwapi.lib, dxguid.lib, psapi.lib, and comctl32.lib as an Additional Dependency in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/RInput.vcproj b/RInput.vcproj index de64c34..44debca 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -42,7 +42,7 @@ -typedef HRESULT (WINAPI *PRESENTPROC)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*); -typedef HRESULT (WINAPI *PRESENTEXPROC)(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*, DWORD); -typedef HRESULT (WINAPI *SWAPPRESENTPROC)(IDirect3DSwapChain9*, const RECT*, const RECT*, HWND, const RGNDATA*, DWORD); - HookData d3d9EndScene; -HookData d3d9Reset; -HookData d3d9ResetEx; -HookData d3d9Present; -HookData d3d9PresentEx; -HookData d3d9SwapPresent; static LPVOID lpCurrentDevice = NULL; -BOOL bD3D9Ex = FALSE; -void SetupD3D9(IDirect3DDevice9 *device); -static int presentRecurse = 0; -int consec_frames = 0; +int consec_EndScene = 0; HMODULE hD3D9Dll = NULL; typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); @@ -42,21 +30,17 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) IDirect3D9 *d3d; if(SUCCEEDED(device->GetDirect3D(&d3d))) - { - IDirect3D9 *d3d9ex; - - if(bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) - d3d9ex->Release(); - d3d->Release(); - } lpCurrentDevice = device; - SetupD3D9(device); } HRESULT hRes = device->EndScene(); + // Consecutive EndScene calls without Get/SetCursorPos pair + if (consec_EndScene < 6) + ++consec_EndScene; + d3d9EndScene.Rehook(); LeaveCriticalSection(&d3d9EndMutex); @@ -64,137 +48,10 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) return hRes; } -HRESULT STDMETHODCALLTYPE D3D9Present(IDirect3DDevice9 *device, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) -{ - d3d9Present.Unhook(); - - if (!presentRecurse) - { - if (consec_frames < 3) - ++consec_frames; - } - - presentRecurse++; - HRESULT hRes = device->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); - presentRecurse--; - - d3d9Present.Rehook(); - - return hRes; -} - -HRESULT STDMETHODCALLTYPE D3D9PresentEx(IDirect3DDevice9Ex *device, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) -{ - d3d9PresentEx.Unhook(); - - if (!presentRecurse) - { - if (consec_frames < 3) - ++consec_frames; - } - - presentRecurse++; - HRESULT hRes = device->PresentEx(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); - presentRecurse--; - - d3d9PresentEx.Rehook(); - - return hRes; -} - -HRESULT STDMETHODCALLTYPE D3D9SwapPresent(IDirect3DSwapChain9 *swap, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) -{ - d3d9SwapPresent.Unhook(); - - presentRecurse++; - HRESULT hRes = swap->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); - presentRecurse--; - - d3d9SwapPresent.Rehook(); - - return hRes; -} - -typedef HRESULT(STDMETHODCALLTYPE *D3D9ResetPROC)(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params); - -HRESULT STDMETHODCALLTYPE D3D9Reset(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params) -{ - d3d9Reset.Unhook(); - - HRESULT hRes = device->Reset(params); - - SetupD3D9(device); - - d3d9Reset.Rehook(); - - return hRes; -} - - -HRESULT STDMETHODCALLTYPE D3D9ResetEx(IDirect3DDevice9Ex *device, D3DPRESENT_PARAMETERS *params, D3DDISPLAYMODEEX *fullscreenData) -{ - d3d9ResetEx.Unhook(); - d3d9Reset.Unhook(); - - HRESULT hRes = device->ResetEx(params, fullscreenData); - - if(lpCurrentDevice == NULL) - bD3D9Ex = true; - - SetupD3D9(device); - - d3d9Reset.Rehook(); - d3d9ResetEx.Rehook(); - - return hRes; -} - -void SetupD3D9(IDirect3DDevice9 *device) -{ - IDirect3DSwapChain9 *swapChain = NULL; - - if (SUCCEEDED(device->GetSwapChain(0, &swapChain))) - { - IDirect3D9 *d3d; - - if (SUCCEEDED(device->GetDirect3D(&d3d))) - { - IDirect3D9 *d3d9ex; - - if (bD3D9Ex = SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), (void**)&d3d9ex))) - d3d9ex->Release(); - - d3d->Release(); - } - - d3d9Present.Hook(GetVTable(device, (68/4)), (FARPROC)D3D9Present); - - if(bD3D9Ex) - { - FARPROC curPresentEx = GetVTable(device, (484/4)); - d3d9PresentEx.Hook(curPresentEx, (FARPROC)D3D9PresentEx); - d3d9ResetEx.Hook(GetVTable(device, (528/4)), (FARPROC)D3D9ResetEx); - } - - d3d9Reset.Hook(GetVTable(device, (64/4)), (FARPROC)D3D9Reset); - d3d9SwapPresent.Hook(GetVTable(swapChain, (12/4)), (FARPROC)D3D9SwapPresent); - d3d9Present.Rehook(); - d3d9SwapPresent.Rehook(); - d3d9Reset.Rehook(); - - if(bD3D9Ex) - { - d3d9PresentEx.Rehook(); - d3d9ResetEx.Rehook(); - } - - swapChain->Release(); - } -} - typedef IDirect3D9* (WINAPI*D3D9CREATEPROC)(UINT); typedef HRESULT (WINAPI*D3D9CREATEEXPROC)(UINT, IDirect3D9Ex**); +// Figure out the D3D9 device and hook EndScene bool InitD3D9Capture() { bool bSuccess = false; @@ -207,9 +64,7 @@ bool InitD3D9Capture() wcscat_s(lpD3D9Path, MAX_PATH, wa); delete[] wa; - hD3D9Dll = GetModuleHandleW(lpD3D9Path); - - if (hD3D9Dll) + if (hD3D9Dll = GetModuleHandleW(lpD3D9Path)) { D3D9CREATEEXPROC d3d9CreateEx = (D3D9CREATEEXPROC)GetProcAddress(hD3D9Dll, "Direct3DCreate9Ex"); diff --git a/hook.cpp b/hook.cpp index 5599fa3..4e7c4e8 100644 --- a/hook.cpp +++ b/hook.cpp @@ -36,7 +36,7 @@ inline bool AttemptToHookSomething() static inline HWND CreateDummyWindow(LPCTSTR lpClass, LPCTSTR lpName) { - return CreateWindowEx (0, lpClass, lpName, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL); + return CreateWindowEx(0, lpClass, lpName, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL); } static DWORD WINAPI DummyWindowThread(LPVOID lpBla) @@ -60,7 +60,7 @@ static DWORD WINAPI DummyWindowThread(LPVOID lpBla) MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) + while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); @@ -97,10 +97,14 @@ DWORD WINAPI CaptureThread(HANDLE hDllMainThread) for (size_t n = 0; !bCaptureThreadStop; ++n) { // Less frequent loop interations reduce resource usage - if (n % 16 == 0) AttemptToHookSomething(); + if (n % 16 == 0) + AttemptToHookSomething(); + // Overall interval for checking EndScene hook is still 4000ms Sleep(250); } + DeleteCriticalSection(&d3d9EndMutex); + return 0; } \ No newline at end of file diff --git a/main.cpp b/main.cpp index 1dce5e8..abf1c2d 100644 --- a/main.cpp +++ b/main.cpp @@ -32,6 +32,7 @@ #include "main.h" #include +HINSTANCE g_hInstance = NULL; int n_sourceEXE = 0; bool sourceEXE = false; HANDLE hCaptureThread = NULL; @@ -44,85 +45,87 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) switch(dwReason) { case DLL_PROCESS_ATTACH: - // No need for a threaded entry - if (!DisableThreadLibraryCalls(hInstance)) - return 0; + { + // No need for a threaded entry + if (!DisableThreadLibraryCalls(hInstance)) + return 0; - g_hInstance = hInstance; + g_hInstance = hInstance; - { // Get process and enable bug fixes for source games char szEXEPath[MAX_PATH]; - GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath)); - PathStripPathA(szEXEPath); - - // Bug fixes now limited to tested source games - char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; - bool b_sourceEXE = false; - for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) + // Detect source games for enabling specific bug fixes + if (GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath))) { - ++n_sourceEXE; + char TF2path[sizeof(szEXEPath)]; + strcpy_s(TF2path, _countof(TF2path), szEXEPath); + + PathStripPathA(szEXEPath); - if ((std::string)szEXEPath == (std::string)*iSource_exes) + // Bug fixes now limited to tested source games + char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; + bool b_sourceEXE = false; + + for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) { - sourceEXE = true; + ++n_sourceEXE; - if (n_sourceEXE <= 2) // CS:GO and TF2 D3D hooks + if ((std::string)szEXEPath == (std::string)*iSource_exes) { - if (n_sourceEXE == 2) + sourceEXE = true; + + // Start CS:GO and TF2 D3D9 hooking + if (n_sourceEXE <= 2) { - // Make sure hl2.exe is TF2 - char TF2path[MAX_PATH]; - GetModuleFileNameA(NULL, TF2path, sizeof(TF2path)); - PathRemoveFileSpecA(TF2path); - std::string sTF2path = (std::string)TF2path; - char testTF2[16]; + if (n_sourceEXE == 2) + { + // Make sure hl2.exe is TF2 + PathRemoveFileSpecA(TF2path); + std::string sTF2path = (std::string)TF2path; + char testTF2[16]; - for (size_t j = 1; j <= 15; ++j) - testTF2[j] = TF2path[sTF2path.size() + j - 15]; + for (size_t j = 1; j <= 15; ++j) + testTF2[j] = TF2path[sTF2path.size() + j - 15]; - char tf2[16] = "Team Fortress 2"; + char tf2[16] = "Team Fortress 2"; - for (int k = 1; k < 16; ++k) - { - if (testTF2[k] != tf2[k]) + for (int k = 1; k < 16; ++k) { - b_sourceEXE = true; - goto skipd3d; + if (testTF2[k] != tf2[k]) + { + b_sourceEXE = true; + goto skip_d3d; + } } } - } - HANDLE hThread = NULL; - HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); + HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); - if (!(hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hDllMainThread, 0, 0))) - { - CloseHandle(hDllMainThread); - return 0; + if (!(hCaptureThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hDllMainThread, 0, 0))) + { + CloseHandle(hDllMainThread); + return 0; + } } - - hCaptureThread = hThread; - CloseHandle(hThread); +skip_d3d: + break; } - - skipd3d: - break; } - } - if (b_sourceEXE) - n_sourceEXE = 5; + // hl2.exe is not TF2 + if (b_sourceEXE) + n_sourceEXE = 5; + } + else + n_sourceEXE = 4; + break; } - break; - case DLL_PROCESS_DETACH: - if (n_sourceEXE <= 2) // Stop D3D hooking for CS:GO and TF2 + // Stop CS:GO and TF2 D3D9 hooking + if (n_sourceEXE <= 2) { - DeleteCriticalSection(&d3d9EndMutex); - bCaptureThreadStop = true; WaitForSingleObject(hCaptureThread, 300); @@ -131,14 +134,10 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) if (hwndSender) DestroyWindow(hwndSender); - - if (hCaptureThread) - CloseHandle(hCaptureThread); } CRawInput::hookLibrary(false); CRawInput::unload(); - break; } @@ -148,6 +147,7 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) extern "C" __declspec(dllexport) void entryPoint() { HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENTNAME); + if (!hEvent) displayError(L"Could not open interprocess communication event."); diff --git a/main.h b/main.h index 77cefc9..d42f135 100644 --- a/main.h +++ b/main.h @@ -2,22 +2,23 @@ #define _MAIN_H_ #define STRICT -#define WINVER 0x0501 // Need at least Windows XP -#define _WIN32_WINDOWS 0x0501 // Need at least Windows XP -#define _WIN32_WINNT 0x0501 // Need at least Windows XP +// Need at least Windows XP +#define WINVER 0x0501 +#define _WIN32_WINDOWS 0x0501 +#define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN - -#define ERROR_BUFFER_SIZE 256 // Amount of bytes to store an error string - +// Amount of bytes to store an error string +#define ERROR_BUFFER_SIZE 256 #define EVENTNAME "RInputEvent32" #define KERNEL_LIB L"kernel32.dll" -#include "rawinput.h" // Raw input class -#include "hook.h" // D3D9 hooking class +// Raw input class +#include "rawinput.h" +// D3D9 hooking class +#include "hook.h" extern "C" __declspec(dllexport) void entryPoint(); -HINSTANCE g_hInstance = NULL; void displayError(WCHAR* pwszError); void unloadLibrary(); diff --git a/rawinput.cpp b/rawinput.cpp index 4eba97e..de90f54 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -30,6 +30,7 @@ */ #include "rawinput.h" +#include #pragma intrinsic(memset) @@ -37,18 +38,22 @@ extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpSetCursorPos(int x, int y), SetCursorPos); -// Initialize static variables HWND CRawInput::hwndInput = NULL; +HWND CRawInput::hwndClient = NULL; long CRawInput::hold_x = 0; long CRawInput::hold_y = 0; long CRawInput::set_x = 0; long CRawInput::set_y = 0; bool CRawInput::bRegistered = false; +POINT CRawInput::centerPoint; long CRawInput::x = 0; long CRawInput::y = 0; +int CRawInput::signal = 0; +int CRawInput::consecG = 2; +bool CRawInput::alttab = false; int CRawInput::SCP = 0; -int CRawInput::consecG = 0; -bool CRawInput::GCP = false; +bool CRawInput::bSubclass = false; +HANDLE CRawInput::hCreateThread = NULL; bool CRawInput::initialize(WCHAR* pwszError) { @@ -93,33 +98,38 @@ bool CRawInput::initWindow(WCHAR* pwszError) bool CRawInput::initInput(WCHAR* pwszError) { - // Now behaves correctly with windowed mode - HWND client_hwnd = GetForegroundWindow(); - RECT client_rect; + // Identify when the active window matches the injected process + for (size_t p = 0; !CRawInput::hwndClient; ++p) + { + if (!CRawInput::clientWindow()) + Sleep(300); + } - if (GetClientRect(client_hwnd, &client_rect)) + // Set screen center until SetCursorPos is called + if (CRawInput::clientCenter()) { - long clientx = client_rect.right / 2; - long clienty = client_rect.bottom / 2; - POINT client_point; - client_point.x = clientx; - client_point.y = clienty; - ClientToScreen(client_hwnd, &client_point); - // Set screen center until SetCursorPos is called - CRawInput::hold_x = client_point.x; - CRawInput::hold_y = client_point.y; + CRawInput::hold_x = CRawInput::centerPoint.x; + CRawInput::hold_y = CRawInput::centerPoint.y; } - // Raw input accumulators initialized to starting cursor position POINT defCor; - GetCursorPos(&defCor); - CRawInput::set_x = defCor.x; - CRawInput::set_y = defCor.y; + + // Raw input accumulators initialized to starting cursor position + if (GetCursorPos(&defCor)) + { + CRawInput::set_x = defCor.x; + CRawInput::set_y = defCor.y; + } RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); - // New flag allows accumulation to be maintained while alt-tabbed - rMouse.dwFlags = RIDEV_INPUTSINK; + + // Flag allows accumulation to continue for TF2 while alt-tabbed + if (n_sourceEXE == 2) + rMouse.dwFlags = RIDEV_INPUTSINK; + else + rMouse.dwFlags = 0; + rMouse.hwndTarget = CRawInput::hwndInput; rMouse.usUsagePage = 0x01; rMouse.usUsage = 0x02; @@ -133,11 +143,44 @@ bool CRawInput::initInput(WCHAR* pwszError) return (bRegistered = true); } +bool CRawInput::clientWindow() +{ + DWORD ActiveWinID; + HWND hwndActiveWin = GetForegroundWindow(); + GetWindowThreadProcessId(hwndActiveWin, &ActiveWinID); + + if (ActiveWinID == GetCurrentProcessId()) + { + CRawInput::hwndClient = hwndActiveWin; + return true; + } + + return false; +} + +bool CRawInput::clientCenter() +{ + RECT rectClient; + + if (GetClientRect(CRawInput::hwndClient, &rectClient)) + { + long xClient = rectClient.right / 2; + long yClient = rectClient.bottom / 2; + centerPoint.x = xClient; + centerPoint.y = yClient; + + if (ClientToScreen(CRawInput::hwndClient, ¢erPoint)) + return true; + } + + return false; +} + unsigned int CRawInput::pollInput() { MSG msg; - while (GetMessage(&msg, CRawInput::hwndInput, 0, 0) != 0) + while (GetMessage(&msg, CRawInput::hwndInput, 0, 0) > 0) DispatchMessage(&msg); return msg.message; @@ -180,8 +223,7 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA int __stdcall CRawInput::hSetCursorPos(int x, int y) { - // Skips unnecessary second SetCursorPos call for source games - if (!CRawInput::SCP && !TrmpSetCursorPos(x, y)) + if (!TrmpSetCursorPos(x, y)) return 1; CRawInput::set_x = (long)x; @@ -191,22 +233,28 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) { if (n_sourceEXE == 2) { - if (((consec_frames == 3) && (CRawInput::consecG == 2)) && !CRawInput::GCP) - goto skipGCPreset; + ++CRawInput::signal; + + // Bug fix for Steam overlay in TF2 backpack + if (((consec_EndScene == 6) && (CRawInput::consecG == 3)) && !CRawInput::alttab) + { + if (CRawInput::SCP == 0) + CRawInput::SCP = -1; + + goto skipGreset; + } CRawInput::consecG = 0; } - skipGCPreset: - consec_frames = 0; +skipGreset: + ++CRawInput::SCP; // Alt-tab bug fix if ((CRawInput::set_x == 0) && (CRawInput::set_y == 0)) - CRawInput::GCP = true; - - // Console bug fix - ++CRawInput::SCP; + CRawInput::alttab = true; + // Console and buy menu bug fixes if (CRawInput::SCP == 1) { CRawInput::set_x -= CRawInput::x; @@ -214,9 +262,18 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) } else if (CRawInput::SCP == 2) { - CRawInput::GCP = false; + if (n_sourceEXE != 2) + CRawInput::SCP = 0; + else if (CRawInput::bSubclass) + { + // TF2 backpack fix not applied when in actual game + RemoveWindowSubclass(CRawInput::hwndClient, CRawInput::SubclassWndProc, 0); + CRawInput::bSubclass = false; + } - CRawInput::SCP = 0; + consec_EndScene = 0; + + CRawInput::alttab = false; CRawInput::hold_x = CRawInput::set_x; CRawInput::hold_y = CRawInput::set_y; @@ -232,71 +289,115 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) CRawInput::set_x += CRawInput::x; CRawInput::set_y += CRawInput::y; - CRawInput::SCP = 0; + // Raw input accumulation resets moved here from hSetCursorPos + CRawInput::x = 0; + CRawInput::y = 0; if (n_sourceEXE == 2) { - if (CRawInput::consecG < 2) + ++CRawInput::signal; + + if (CRawInput::consecG < 3) ++CRawInput::consecG; // Bug fix for cursor hitting side of screen in TF2 backpack - if (CRawInput::consecG == 2) + if (CRawInput::consecG == 3) { - if (CRawInput::set_x >= CRawInput::hold_x << 1) - CRawInput::set_x = (CRawInput::hold_x << 1) - 1; + if (CRawInput::set_x >= CRawInput::hold_x * 2) + CRawInput::set_x = CRawInput::hold_x * 2 - 1; else if (CRawInput::set_x < 0) CRawInput::set_x = 0; - if (CRawInput::set_y >= CRawInput::hold_y << 1) - CRawInput::set_y = (CRawInput::hold_y << 1) - 1; + if (CRawInput::set_y >= CRawInput::hold_y * 2) + CRawInput::set_y = CRawInput::hold_y * 2 - 1; else if (CRawInput::set_y < 0) CRawInput::set_y = 0; + + // Fix for subtle TF2 backpack bug from alt-tabbing + if (!CRawInput::bSubclass) + { + if (!CRawInput::hwndClient) + { + if (!CRawInput::clientWindow()) + goto subclass_failed; + } + + // When in TF2 backpack, monitor its window messages + SetWindowSubclass(CRawInput::hwndClient, CRawInput::SubclassWndProc, 0, 1); +subclass_failed: + CRawInput::bSubclass = true; + } } } - // Alt-tab bug fix - if (!CRawInput::GCP) + if (!CRawInput::alttab) { - // Buy and escape menu bug fix--respects resolution changes - if (consec_frames == 3) + if (consec_EndScene == 6) { // Needed to not break backpack in TF2 - if (CRawInput::consecG == 2) - goto skiprecenter; + if ((CRawInput::SCP != 1) && (CRawInput::consecG == 3)) + goto skip_recenter; - HWND new_hwnd = GetForegroundWindow(); - RECT new_rect; + // Buy and escape menu bug fixes + if (!CRawInput::hwndClient) + CRawInput::clientWindow(); - if(GetClientRect(new_hwnd, &new_rect)) + if (CRawInput::hwndClient && CRawInput::clientCenter()) { - long newx = new_rect.right / 2; - long newy = new_rect.bottom / 2; - POINT new_Point; - new_Point.x = newx; - new_Point.y = newy; - ClientToScreen(new_hwnd, &new_Point); - CRawInput::set_x = new_Point.x; - CRawInput::set_y = new_Point.y; + CRawInput::set_x = CRawInput::centerPoint.x; + CRawInput::set_y = CRawInput::centerPoint.y; } } - skiprecenter: +skip_recenter: lpPoint->x = CRawInput::set_x; lpPoint->y = CRawInput::set_y; } else { + // Alt-tab bug fix lpPoint->x = CRawInput::hold_x; lpPoint->y = CRawInput::hold_y; } - // Raw input accumulation resets moved here from hSetCursorPos - CRawInput::x = 0; - CRawInput::y = 0; + CRawInput::SCP = 0; return 0; } +LRESULT CALLBACK CRawInput::SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +{ + switch (uMsg) + { + case WM_SETFOCUS: + // Bug fix during alt-tab back into TF2 backpack + CRawInput::signal = 0; + CRawInput::hCreateThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CRawInput::blockInput, NULL, 0, 0); + break; + + case WM_NCDESTROY: + RemoveWindowSubclass(hWnd, CRawInput::SubclassWndProc, uIdSubclass); + break; + + default: + break; + } + + return DefSubclassProc(hWnd, uMsg, wParam, lParam); +} + +void CRawInput::blockInput() +{ + BlockInput(TRUE); + + // Unblock input after 7 SetCursorPos and/or GetCursorPos calls + for (size_t q = 0; CRawInput::signal < 7; ++q) + Sleep(16); + + BlockInput(FALSE); + CloseHandle(CRawInput::hCreateThread); +} + bool CRawInput::hookLibrary(bool bInstall) { if (bInstall) diff --git a/rawinput.h b/rawinput.h index 4537099..acf9975 100644 --- a/rawinput.h +++ b/rawinput.h @@ -2,18 +2,19 @@ #define RAWINPUT_H_ #include -#include "detours.h" // Required for function hooking +#include "detours.h" #define RAWINPUTHDRSIZE sizeof(RAWINPUTHEADER) #define RAWPTRSIZE 40 #define INPUTWINDOW "RInput" -#pragma warning(disable: 4100) // Prevent warning level 4 warnings for detouring +// Prevent warning level 4 warnings for detouring +#pragma warning(disable: 4100) extern void displayError(WCHAR* pwszError); extern bool sourceEXE; extern int n_sourceEXE; -extern int consec_frames; +extern int consec_EndScene; /** * Note from original author (abort): @@ -27,33 +28,46 @@ class CRawInput public: // Initialize raw input static bool initialize(WCHAR* pwszError); - // Enables or disables the hooking - static bool hookLibrary(bool bInstall); - // Hooked functions handling - static int __stdcall hGetCursorPos(LPPOINT lpPoint); - static int __stdcall hSetCursorPos(int x, int y); + // Initialization + static bool initWindow(WCHAR* pwszError); + static bool initInput(WCHAR* pwszError); + // Identify window of the injected process + static bool clientWindow(); + // Find window center of the injected process + static bool clientCenter(); // Poll input static unsigned int pollInput(); // Input window proc static LRESULT __stdcall wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - // Initialization - static bool initWindow(WCHAR* pwszError); - static bool initInput(WCHAR* pwszError); + // Hooked functions handling + static int __stdcall hSetCursorPos(int x, int y); + static int __stdcall hGetCursorPos(LPPOINT lpPoint); + // TF2 subclassing + static LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); + // TF2 input blocking when alt-tabbing back in + static void blockInput(); + // Enables or disables the hooking + static bool hookLibrary(bool bInstall); // Unload raw input static void unload(); private: static HWND hwndInput; + static HWND hwndClient; static long hold_x; static long hold_y; static long set_x; static long set_y; static bool bRegistered; + static POINT centerPoint; static long x; static long y; - static int SCP; + static int signal; static int consecG; - static bool GCP; + static bool alttab; + static int SCP; + static bool bSubclass; + static HANDLE hCreateThread; }; #endif \ No newline at end of file From b7b6511625fc03fbcb177b062af3a426263d73ab Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 3 Dec 2015 07:35:28 -0500 Subject: [PATCH 40/57] rearranged includes slightly --- d3d9.cpp | 1 + hook.h | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/d3d9.cpp b/d3d9.cpp index efcd1ce..414ebfe 100644 --- a/d3d9.cpp +++ b/d3d9.cpp @@ -9,6 +9,7 @@ ********************************************************************************/ #include "hook.h" +#include #include HookData d3d9EndScene; diff --git a/hook.h b/hook.h index f4e7daa..498c45b 100644 --- a/hook.h +++ b/hook.h @@ -2,15 +2,12 @@ #define _HOOK_H_ #include -#include - #define PSAPI_VERSION 1 #include +#include #pragma intrinsic(memcpy) -#include - extern HINSTANCE g_hInstance; extern CRITICAL_SECTION d3d9EndMutex; extern HANDLE dummyEvent; From b0e917bfcda476e3066152141c738ee4c7025c7f Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 7 Dec 2015 19:40:02 -0500 Subject: [PATCH 41/57] very minor code changes --- hook.cpp | 2 +- rawinput.cpp | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/hook.cpp b/hook.cpp index 4e7c4e8..d5a4462 100644 --- a/hook.cpp +++ b/hook.cpp @@ -60,7 +60,7 @@ static DWORD WINAPI DummyWindowThread(LPVOID lpBla) MSG msg; - while (GetMessage(&msg, NULL, 0, 0) > 0) + while (GetMessage(&msg, NULL, 0, 0) != 0) { TranslateMessage(&msg); DispatchMessage(&msg); diff --git a/rawinput.cpp b/rawinput.cpp index de90f54..48dcb20 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -180,7 +180,7 @@ unsigned int CRawInput::pollInput() { MSG msg; - while (GetMessage(&msg, CRawInput::hwndInput, 0, 0) > 0) + while (GetMessage(&msg, CRawInput::hwndInput, 0, 0) != 0) DispatchMessage(&msg); return msg.message; @@ -233,7 +233,8 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) { if (n_sourceEXE == 2) { - ++CRawInput::signal; + if (CRawInput::signal >= 1) + ++CRawInput::signal; // Bug fix for Steam overlay in TF2 backpack if (((consec_EndScene == 6) && (CRawInput::consecG == 3)) && !CRawInput::alttab) @@ -287,15 +288,14 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { // Split off raw input handling to accumulate independently CRawInput::set_x += CRawInput::x; - CRawInput::set_y += CRawInput::y; - - // Raw input accumulation resets moved here from hSetCursorPos CRawInput::x = 0; + CRawInput::set_y += CRawInput::y; CRawInput::y = 0; if (n_sourceEXE == 2) { - ++CRawInput::signal; + if (CRawInput::signal >= 1) + ++CRawInput::signal; if (CRawInput::consecG < 3) ++CRawInput::consecG; @@ -371,7 +371,7 @@ LRESULT CALLBACK CRawInput::SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, { case WM_SETFOCUS: // Bug fix during alt-tab back into TF2 backpack - CRawInput::signal = 0; + CRawInput::signal = 1; CRawInput::hCreateThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CRawInput::blockInput, NULL, 0, 0); break; @@ -391,9 +391,11 @@ void CRawInput::blockInput() BlockInput(TRUE); // Unblock input after 7 SetCursorPos and/or GetCursorPos calls - for (size_t q = 0; CRawInput::signal < 7; ++q) + for (size_t q = 0; CRawInput::signal < 8; ++q) Sleep(16); + CRawInput::signal = 0; + BlockInput(FALSE); CloseHandle(CRawInput::hCreateThread); } From 2af1fd078d669ad3b8f92510784ae12e7c9b9b1f Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 8 Dec 2015 08:57:47 -0500 Subject: [PATCH 42/57] minor changes to READMEs --- README | 4 ++-- README.mdown | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README b/README index dbabab8..50654f5 100644 --- a/README +++ b/README @@ -6,7 +6,7 @@ Requirements: -------------------------------------- - OS: Windows XP or later - CPU: Intel Pentium 4 or later; or, AMD Opteron, Athlon 64 or later -- Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34) +- Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34 but only needed if you get a pop-up error like "MSVCR90.dll is missing" when trying to run RInput) Note: -------------------------------------- @@ -21,7 +21,7 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Pick Dynamic Library (.dll) as the configuration type - Compile using Multi-Byte Character Set - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory -- Add the Microsoft DirectX SDK(https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) +- Add the Microsoft DirectX SDK (https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) - Compiled with Multi-threaded DLL runtime library for linking - It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No - Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allow D3D9 to be hooked diff --git a/README.mdown b/README.mdown index 1f52064..b2cc16a 100644 --- a/README.mdown +++ b/README.mdown @@ -4,7 +4,7 @@ RInput is an alternative to in-game raw input, which some users perceive as havi ## Requirements - OS: Windows XP or later - CPU: Intel Pentium 4 or later; or, AMD Opteron, Athlon 64 or later -- Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34) +- Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34 but only needed if you get a pop-up error like "MSVCR90.dll is missing" when trying to run RInput) ## Note This is a fork of RInput Library v1.31 as originally authored by abort. x64 architecture support was never added by him, but here are some notes on what would need to be changed to accomplish this, in case someone is interested: @@ -17,7 +17,7 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Pick Dynamic Library (.dll) as the configuration type - Compile using Multi-Byte Character Set - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory -- Add the Microsoft DirectX SDK(https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) +- Add the Microsoft DirectX SDK (https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) - Compiled with Multi-threaded DLL runtime library for linking - It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No - Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allow D3D9 to be hooked From ca4fa7f2ce74a392dd96da3010fd2b00be7f7c3a Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 9 Dec 2015 03:00:34 -0500 Subject: [PATCH 43/57] fixed extremely minor packet deviation with a critical section, improved game window detection routine, fixed TF2 backpack for windowed and borderless windowed --- README | 2 +- README.mdown | 2 +- RInput.vcproj | 4 +- main.cpp | 61 ++++++++++---------- rawinput.cpp | 153 +++++++++++++++++++++++++++++++------------------- rawinput.h | 6 +- versioninfo.h | 4 +- 7 files changed, 135 insertions(+), 97 deletions(-) diff --git a/README b/README index 50654f5..2b8ec14 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.40 by Vols and Jezuz +RInput Library v1.41 by Vols and Jezuz -------------------------------------- RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. diff --git a/README.mdown b/README.mdown index b2cc16a..b17117d 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.40 by Vols and Jezuz +# RInput Library v1.41 by Vols and Jezuz RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements diff --git a/RInput.vcproj b/RInput.vcproj index 44debca..017d080 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -80,7 +80,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="shlwapi.lib dxguid.lib psapi.lib comctl32.lib" - Version="1.40-debug" + Version="1.41-debug" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" EnableUAC="true" @@ -190,7 +190,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="shlwapi.lib dxguid.lib psapi.lib comctl32.lib" - Version="1.40" + Version="1.41" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" GenerateManifest="true" diff --git a/main.cpp b/main.cpp index abf1c2d..7a04fb0 100644 --- a/main.cpp +++ b/main.cpp @@ -35,6 +35,7 @@ HINSTANCE g_hInstance = NULL; int n_sourceEXE = 0; bool sourceEXE = false; +HWND hwndClient = NULL; HANDLE hCaptureThread = NULL; bool bCaptureThreadStop = false; @@ -74,51 +75,47 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { sourceEXE = true; - // Start CS:GO and TF2 D3D9 hooking - if (n_sourceEXE <= 2) + if (n_sourceEXE == 2) { - if (n_sourceEXE == 2) - { - // Make sure hl2.exe is TF2 - PathRemoveFileSpecA(TF2path); - std::string sTF2path = (std::string)TF2path; - char testTF2[16]; - - for (size_t j = 1; j <= 15; ++j) - testTF2[j] = TF2path[sTF2path.size() + j - 15]; - - char tf2[16] = "Team Fortress 2"; - - for (int k = 1; k < 16; ++k) - { - if (testTF2[k] != tf2[k]) - { - b_sourceEXE = true; - goto skip_d3d; - } - } - } + // Make sure hl2.exe is TF2 + PathRemoveFileSpecA(TF2path); + std::string sTF2path = (std::string)TF2path; + char testTF2[16]; + + for (size_t j = 1; j <= 15; ++j) + testTF2[j] = TF2path[sTF2path.size() + j - 15]; - HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); + char tf2[16] = "Team Fortress 2"; - if (!(hCaptureThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hDllMainThread, 0, 0))) + for (int k = 1; k < 16; ++k) { - CloseHandle(hDllMainThread); - return 0; + // check hl2.exe is TF2 + if (testTF2[k] != tf2[k]) + n_sourceEXE = 5; } } -skip_d3d: + break; } } - - // hl2.exe is not TF2 - if (b_sourceEXE) - n_sourceEXE = 5; } else n_sourceEXE = 4; + hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); + + // Start CS:GO and TF2 D3D9 hooking + if (n_sourceEXE <= 2) + { + HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); + + if (!(hCaptureThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hDllMainThread, 0, 0))) + { + CloseHandle(hDllMainThread); + return 0; + } + } + break; } diff --git a/rawinput.cpp b/rawinput.cpp index 48dcb20..0ce3066 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -39,7 +39,7 @@ extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), Ge extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpSetCursorPos(int x, int y), SetCursorPos); HWND CRawInput::hwndInput = NULL; -HWND CRawInput::hwndClient = NULL; +bool CRawInput::TF2unblock = false; long CRawInput::hold_x = 0; long CRawInput::hold_y = 0; long CRawInput::set_x = 0; @@ -55,6 +55,15 @@ int CRawInput::SCP = 0; bool CRawInput::bSubclass = false; HANDLE CRawInput::hCreateThread = NULL; +// Fixed rare packet discrepancy +CRITICAL_SECTION rawMouseData; + +struct WindowHandleStructure +{ + unsigned long PID; + HWND WindowHandle; +}; + bool CRawInput::initialize(WCHAR* pwszError) { if (!initWindow(pwszError)) @@ -68,6 +77,35 @@ bool CRawInput::initialize(WCHAR* pwszError) bool CRawInput::initWindow(WCHAR* pwszError) { + // Identify the window that matches the injected process + if (!hwndClient) + hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); + + if (hwndClient) + { + // TF2 Window must be active for backpack fixes to work + if (n_sourceEXE == 2 && GetForegroundWindow() != hwndClient) + { + CRawInput::TF2unblock = true; + BlockInput(TRUE); + ShowWindow(hwndClient, 1); + } + + // Set screen center until SetCursorPos is called + if (CRawInput::clientCenter()) + { + CRawInput::hold_x = CRawInput::centerPoint.x; + CRawInput::hold_y = CRawInput::centerPoint.y; + } + + // Unblock TF2 input after making it foreground + if (CRawInput::TF2unblock) + { + Sleep(1000); + BlockInput(FALSE); + } + } + // Register the window to catch WM_INPUT events WNDCLASSEX wcex; memset(&wcex, 0, sizeof(WNDCLASSEX)); @@ -98,20 +136,6 @@ bool CRawInput::initWindow(WCHAR* pwszError) bool CRawInput::initInput(WCHAR* pwszError) { - // Identify when the active window matches the injected process - for (size_t p = 0; !CRawInput::hwndClient; ++p) - { - if (!CRawInput::clientWindow()) - Sleep(300); - } - - // Set screen center until SetCursorPos is called - if (CRawInput::clientCenter()) - { - CRawInput::hold_x = CRawInput::centerPoint.x; - CRawInput::hold_y = CRawInput::centerPoint.y; - } - POINT defCor; // Raw input accumulators initialized to starting cursor position @@ -143,33 +167,38 @@ bool CRawInput::initInput(WCHAR* pwszError) return (bRegistered = true); } -bool CRawInput::clientWindow() +BOOL CALLBACK CRawInput::EnumWindowsProc(HWND WindowHandle, LPARAM lParam) { - DWORD ActiveWinID; - HWND hwndActiveWin = GetForegroundWindow(); - GetWindowThreadProcessId(hwndActiveWin, &ActiveWinID); + unsigned long PID = 0; + WindowHandleStructure* data = reinterpret_cast(lParam); + GetWindowThreadProcessId(WindowHandle, &PID); - if (ActiveWinID == GetCurrentProcessId()) - { - CRawInput::hwndClient = hwndActiveWin; - return true; - } + if (data->PID != PID || (GetWindow(WindowHandle, GW_OWNER) || !IsWindowVisible(WindowHandle))) + return TRUE; - return false; + data->WindowHandle = WindowHandle; + return FALSE; +} + +HWND CRawInput::clientWindow(unsigned long PID) +{ + WindowHandleStructure data = {PID, NULL}; + EnumWindows(EnumWindowsProc, reinterpret_cast(&data)); + return data.WindowHandle; } bool CRawInput::clientCenter() { RECT rectClient; - if (GetClientRect(CRawInput::hwndClient, &rectClient)) + if (GetClientRect(hwndClient, &rectClient)) { long xClient = rectClient.right / 2; long yClient = rectClient.bottom / 2; centerPoint.x = xClient; centerPoint.y = yClient; - if (ClientToScreen(CRawInput::hwndClient, ¢erPoint)) + if (ClientToScreen(hwndClient, ¢erPoint)) return true; } @@ -202,8 +231,12 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA if (!rwInput->header.dwType) { + EnterCriticalSection(&rawMouseData); + CRawInput::x += rwInput->data.mouse.lLastX; CRawInput::y += rwInput->data.mouse.lLastY; + + LeaveCriticalSection(&rawMouseData); } } @@ -237,7 +270,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) ++CRawInput::signal; // Bug fix for Steam overlay in TF2 backpack - if (((consec_EndScene == 6) && (CRawInput::consecG == 3)) && !CRawInput::alttab) + if ((consec_EndScene == 6 && CRawInput::consecG == 3) && !CRawInput::alttab) { if (CRawInput::SCP == 0) CRawInput::SCP = -1; @@ -252,7 +285,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) ++CRawInput::SCP; // Alt-tab bug fix - if ((CRawInput::set_x == 0) && (CRawInput::set_y == 0)) + if (CRawInput::set_x == 0 && CRawInput::set_y == 0) CRawInput::alttab = true; // Console and buy menu bug fixes @@ -265,12 +298,6 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) { if (n_sourceEXE != 2) CRawInput::SCP = 0; - else if (CRawInput::bSubclass) - { - // TF2 backpack fix not applied when in actual game - RemoveWindowSubclass(CRawInput::hwndClient, CRawInput::SubclassWndProc, 0); - CRawInput::bSubclass = false; - } consec_EndScene = 0; @@ -286,16 +313,26 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { + EnterCriticalSection(&rawMouseData); + // Split off raw input handling to accumulate independently CRawInput::set_x += CRawInput::x; - CRawInput::x = 0; CRawInput::set_y += CRawInput::y; + CRawInput::x = 0; CRawInput::y = 0; + LeaveCriticalSection(&rawMouseData); + if (n_sourceEXE == 2) { if (CRawInput::signal >= 1) ++CRawInput::signal; + else if (CRawInput::bSubclass) + { + // TF2 backpack fix not applied when in actual game + RemoveWindowSubclass(hwndClient, CRawInput::SubclassWndProc, 0); + CRawInput::bSubclass = false; + } if (CRawInput::consecG < 3) ++CRawInput::consecG; @@ -316,16 +353,15 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) // Fix for subtle TF2 backpack bug from alt-tabbing if (!CRawInput::bSubclass) { - if (!CRawInput::hwndClient) + if (!hwndClient) + hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); + + if (hwndClient) { - if (!CRawInput::clientWindow()) - goto subclass_failed; + // When in TF2 backpack, monitor its window messages + SetWindowSubclass(hwndClient, CRawInput::SubclassWndProc, 0, 1); + CRawInput::bSubclass = true; } - - // When in TF2 backpack, monitor its window messages - SetWindowSubclass(CRawInput::hwndClient, CRawInput::SubclassWndProc, 0, 1); -subclass_failed: - CRawInput::bSubclass = true; } } } @@ -335,21 +371,20 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) if (consec_EndScene == 6) { // Needed to not break backpack in TF2 - if ((CRawInput::SCP != 1) && (CRawInput::consecG == 3)) - goto skip_recenter; - - // Buy and escape menu bug fixes - if (!CRawInput::hwndClient) - CRawInput::clientWindow(); - - if (CRawInput::hwndClient && CRawInput::clientCenter()) + if (!(CRawInput::SCP != 1 && CRawInput::consecG == 3)) { - CRawInput::set_x = CRawInput::centerPoint.x; - CRawInput::set_y = CRawInput::centerPoint.y; + if (!hwndClient) + hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); + + // Buy and escape menu bug fixes + if (hwndClient && CRawInput::clientCenter()) + { + CRawInput::set_x = CRawInput::centerPoint.x; + CRawInput::set_y = CRawInput::centerPoint.y; + } } } -skip_recenter: lpPoint->x = CRawInput::set_x; lpPoint->y = CRawInput::set_y; } @@ -390,8 +425,8 @@ void CRawInput::blockInput() { BlockInput(TRUE); - // Unblock input after 7 SetCursorPos and/or GetCursorPos calls - for (size_t q = 0; CRawInput::signal < 8; ++q) + // Unblock input after 8 SetCursorPos and/or GetCursorPos calls + for (size_t q = 0; CRawInput::signal < 9; ++q) Sleep(16); CRawInput::signal = 0; @@ -406,11 +441,15 @@ bool CRawInput::hookLibrary(bool bInstall) { if (!DetourFunctionWithTrampoline((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos) || !DetourFunctionWithTrampoline((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos)) return false; + + InitializeCriticalSection(&rawMouseData); } else { DetourRemove((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos); DetourRemove((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos); + + DeleteCriticalSection(&rawMouseData); } return true; diff --git a/rawinput.h b/rawinput.h index acf9975..d15e037 100644 --- a/rawinput.h +++ b/rawinput.h @@ -11,6 +11,7 @@ // Prevent warning level 4 warnings for detouring #pragma warning(disable: 4100) +extern HWND hwndClient; extern void displayError(WCHAR* pwszError); extern bool sourceEXE; extern int n_sourceEXE; @@ -32,7 +33,8 @@ class CRawInput static bool initWindow(WCHAR* pwszError); static bool initInput(WCHAR* pwszError); // Identify window of the injected process - static bool clientWindow(); + static BOOL CALLBACK EnumWindowsProc(HWND WindowHandle, LPARAM lParam); + static HWND clientWindow(unsigned long PID); // Find window center of the injected process static bool clientCenter(); // Poll input @@ -53,7 +55,7 @@ class CRawInput private: static HWND hwndInput; - static HWND hwndClient; + static bool TF2unblock; static long hold_x; static long hold_y; static long set_x; diff --git a/versioninfo.h b/versioninfo.h index 6634c6e..a19741e 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.40" -#define RINPUTFVER 1,40 +#define RINPUTVER "v1.41" +#define RINPUTFVER 1,41 #define IDI_ICON 101 #define RINPUTINT "RInput" From f89d9bc40ad779beb1f59ea0ddae9b7fc71d368c Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 10 Dec 2015 05:22:46 -0500 Subject: [PATCH 44/57] some minor typo corrections, deletion of unused inline, etc --- README | 17 ++++++++--------- README.mdown | 9 ++++----- hook.h | 8 +------- main.cpp | 1 + rawinput.cpp | 5 ++--- 5 files changed, 16 insertions(+), 24 deletions(-) diff --git a/README b/README index 2b8ec14..3471b75 100644 --- a/README +++ b/README @@ -2,20 +2,20 @@ RInput Library v1.41 by Vols and Jezuz -------------------------------------- RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. -Requirements: +Requirements -------------------------------------- - OS: Windows XP or later - CPU: Intel Pentium 4 or later; or, AMD Opteron, Athlon 64 or later - Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34 but only needed if you get a pop-up error like "MSVCR90.dll is missing" when trying to run RInput) -Note: +Note -------------------------------------- This is a fork of RInput Library v1.31 as originally authored by abort. x64 architecture support was never added by him, but here are some notes on what would need to be changed to accomplish this, in case someone is interested: - The detouring/hooking mechanism needs to support the x64 architectures. This is mainly due to the jumping mechanism for larger pointers. The jumps need to support the whole memory address span over 32-bit - The injector for RInput needs to support 64-bit injection and support for finding 64-bit processes - This library obviously has to get recompiled to 64-bit (might require some changes to Win32 API calls for the RInput HWND) -Building: +Building -------------------------------------- Compiled with Visual Studio 2008, though you may be able to get it to work with other versions or compilers (Visual C++ 2008 Express Edition is available for free @ https://go.microsoft.com/?linkid=7729279). If you want to start from scratch with your own project file, here's what seem to be the most important compiler and project options: - Pick Dynamic Library (.dll) as the configuration type @@ -23,17 +23,16 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory - Add the Microsoft DirectX SDK (https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) - Compiled with Multi-threaded DLL runtime library for linking -- It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No -- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allow D3D9 to be hooked +- Set Enable C++ Exceptions to Yes (/EHsc), as several of the included standard C++ headers use exception handlers +- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allows D3D9 to be hooked - Set the Entry Point as DllMain - Add shlwapi.lib, dxguid.lib, psapi.lib, and comctl32.lib as an Additional Dependency in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar -How it works: +How it works -------------------------------------- Summary of the process by the original author: - 1. The library gets injected into a process by an injector 2. The injector creates a windows event to communicate with the injected library 3. The injector calls the "entryPoint" function remotely @@ -41,9 +40,9 @@ Summary of the process by the original author: 5. The library raises the event that has been created by the injector 6. The injector gives the user feedback, based on whether the event was raised or not -Credits: +Credits -------------------------------------- - BuSheeZy - helping me understand Visual C++ IDE and Git - qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works -- about - original author +- abort - original author - Dr3am - for the icons \ No newline at end of file diff --git a/README.mdown b/README.mdown index b17117d..c69ecb1 100644 --- a/README.mdown +++ b/README.mdown @@ -19,8 +19,8 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Add the Microsoft SDK (I used v6.0A that came with VS2008/VC++2008) folders as additional Include directory and additional linker library directory - Add the Microsoft DirectX SDK (https://www.microsoft.com/en-us/download/confirmation.aspx?id=6812) folders as additional Include directory and additional linker library directory (the x86 folder) - Compiled with Multi-threaded DLL runtime library for linking -- It may be necessary to set Enable C++ Exceptions to Yes (/EHsc), as it didn't seem to want to compile properly in set to No -- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allow D3D9 to be hooked +- Set Enable C++ Exceptions to Yes (/EHsc), as several of the included standard C++ headers use exception handlers +- Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allows D3D9 to be hooked - Set the Entry Point as DllMain - Add shlwapi.lib, dxguid.lib, psapi.lib, and comctl32.lib as an Additional Dependency in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) @@ -28,7 +28,6 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with ## How it works Summary of the process by the original author: - 1. The library gets injected into a process by an injector 2. The injector creates a windows event to communicate with the injected library 3. The injector calls the "entryPoint" function remotely @@ -36,8 +35,8 @@ Summary of the process by the original author: 5. The library raises the event that has been created by the injector 6. The injector gives the user feedback, based on whether the event was raised or not -## Credits: +## Credits - BuSheeZy - helping me understand Visual C++ IDE and Git - qsxcv - ideas for how to improve the mouse data handling and information about how mouse input works -- about - original author +- abort - original author - Dr3am - for the icons \ No newline at end of file diff --git a/hook.h b/hook.h index 498c45b..9436f69 100644 --- a/hook.h +++ b/hook.h @@ -37,7 +37,7 @@ class HookData { if (bHooked) { - if ((funcIn == func) && (hookFunc != hookFuncIn)) + if (funcIn == func && hookFunc != hookFuncIn) { hookFunc = hookFuncIn; @@ -138,10 +138,4 @@ class HookData } }; -inline FARPROC GetVTable(LPVOID ptr, UINT funcOffset) -{ - UPARAM *vtable = *(UPARAM**)ptr; - return (FARPROC)(*(vtable+funcOffset)); -} - #endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index 7a04fb0..de8315b 100644 --- a/main.cpp +++ b/main.cpp @@ -102,6 +102,7 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) else n_sourceEXE = 4; + // Identify the window that matches the injected process hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); // Start CS:GO and TF2 D3D9 hooking diff --git a/rawinput.cpp b/rawinput.cpp index 0ce3066..4346dd1 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -77,7 +77,6 @@ bool CRawInput::initialize(WCHAR* pwszError) bool CRawInput::initWindow(WCHAR* pwszError) { - // Identify the window that matches the injected process if (!hwndClient) hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); @@ -173,7 +172,7 @@ BOOL CALLBACK CRawInput::EnumWindowsProc(HWND WindowHandle, LPARAM lParam) WindowHandleStructure* data = reinterpret_cast(lParam); GetWindowThreadProcessId(WindowHandle, &PID); - if (data->PID != PID || (GetWindow(WindowHandle, GW_OWNER) || !IsWindowVisible(WindowHandle))) + if (data->PID != PID || GetWindow(WindowHandle, GW_OWNER) || !IsWindowVisible(WindowHandle)) return TRUE; data->WindowHandle = WindowHandle; @@ -270,7 +269,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) ++CRawInput::signal; // Bug fix for Steam overlay in TF2 backpack - if ((consec_EndScene == 6 && CRawInput::consecG == 3) && !CRawInput::alttab) + if (consec_EndScene == 6 && CRawInput::consecG == 3 && !CRawInput::alttab) { if (CRawInput::SCP == 0) CRawInput::SCP = -1; From 10f3b60e96ff1c9a7ae8c359c6b57b313cd7b580 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 10 Dec 2015 23:55:37 -0500 Subject: [PATCH 45/57] added threaded unloading function to properly free RInput DLL when D3D is hooked, removed some unnecessary functions/procedures from the D3D hooking, and added numerous comments throughout to aid others in understanding the code --- README | 2 +- README.mdown | 2 +- d3d9.cpp | 36 +++++++++++++++++++++++++++++------- hook.cpp | 37 ++++++++++++++++--------------------- hook.h | 7 ++++--- main.cpp | 32 ++++++++++++++++---------------- main.h | 2 +- rawinput.cpp | 14 ++++++++++---- rawinput.h | 22 ++++++++++------------ versioninfo.h | 4 ++-- 10 files changed, 90 insertions(+), 68 deletions(-) diff --git a/README b/README index 3471b75..5b3805c 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.41 by Vols and Jezuz +RInput Library v1.42 by Vols and Jezuz -------------------------------------- RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. diff --git a/README.mdown b/README.mdown index c69ecb1..467d934 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.41 by Vols and Jezuz +# RInput Library v1.42 by Vols and Jezuz RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements diff --git a/d3d9.cpp b/d3d9.cpp index 414ebfe..daccce2 100644 --- a/d3d9.cpp +++ b/d3d9.cpp @@ -16,13 +16,25 @@ HookData d3d9EndScene; static LPVOID lpCurrentDevice = NULL; int consec_EndScene = 0; -HMODULE hD3D9Dll = NULL; +static HMODULE hD3D9Dll = NULL; typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) { - EnterCriticalSection(&d3d9EndMutex); + HRESULT hRes = E_FAIL; + + // Make sure CRITICAL_SECTION is not referenced after deletion + if (hUnloadDLLFunc) + return hRes; + + while(!TryEnterCriticalSection(&d3d9EndMutex)) + { + Sleep(16); + + if (hUnloadDLLFunc) + return hRes; + } d3d9EndScene.Unhook(); @@ -36,7 +48,7 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) lpCurrentDevice = device; } - HRESULT hRes = device->EndScene(); + hRes = device->EndScene(); // Consecutive EndScene calls without Get/SetCursorPos pair if (consec_EndScene < 6) @@ -52,11 +64,12 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) typedef IDirect3D9* (WINAPI*D3D9CREATEPROC)(UINT); typedef HRESULT (WINAPI*D3D9CREATEEXPROC)(UINT, IDirect3D9Ex**); -// Figure out the D3D9 device and hook EndScene +// Figure out the D3D9 device address and hook EndScene bool InitD3D9Capture() { bool bSuccess = false; + // Get full path for the D3D9 DLL wchar_t lpD3D9Path[MAX_PATH]; SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, SHGFP_TYPE_CURRENT, lpD3D9Path); size_t size = 11; @@ -65,6 +78,7 @@ bool InitD3D9Capture() wcscat_s(lpD3D9Path, MAX_PATH, wa); delete[] wa; + // Start creation of D3D9Ex device in dummy window if (hD3D9Dll = GetModuleHandleW(lpD3D9Path)) { D3D9CREATEEXPROC d3d9CreateEx = (D3D9CREATEEXPROC)GetProcAddress(hD3D9Dll, "Direct3DCreate9Ex"); @@ -90,10 +104,13 @@ bool InitD3D9Capture() if (SUCCEEDED(hRes = d3d9ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, hwndSender, D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_NOWINDOWCHANGES, &pp, NULL, &deviceEx))) { + // D3D9Ex device made successfully in dummy window bSuccess = true; + // D3D9Ex device address is the D3D9 vTable address UPARAM *vtable = *(UPARAM**)deviceEx; + // EndScene address is a fixed offset in vTable d3d9EndScene.Hook((FARPROC)*(vtable+(168/4)), (FARPROC)D3D9EndScene); deviceEx->Release(); @@ -109,11 +126,16 @@ bool InitD3D9Capture() return bSuccess; } -void CheckD3D9Capture() +// Releases remaining resources and frees the RInput DLL +DWORD WINAPI UnloadDLLFunc(LPVOID lpParameter) { EnterCriticalSection(&d3d9EndMutex); - d3d9EndScene.Rehook(true); + d3d9EndScene.Unhook(); + + DeleteCriticalSection(&d3d9EndMutex); - LeaveCriticalSection(&d3d9EndMutex); + CloseHandle(hUnloadDLLFunc); + FreeLibraryAndExitThread(g_hInstance, 0); + return 1; } \ No newline at end of file diff --git a/hook.cpp b/hook.cpp index d5a4462..edb119a 100644 --- a/hook.cpp +++ b/hook.cpp @@ -12,24 +12,27 @@ HWND hwndSender = NULL; static bool bD3D9Hooked = false; -HANDLE dummyEvent = NULL; +static HANDLE dummyEvent = NULL; CRITICAL_SECTION d3d9EndMutex; -inline bool AttemptToHookSomething() +static inline bool AttemptToHookSomething() { - if (!hwndSender) - return false; - bool bFoundSomethingToHook = false; + // Creates D3D9Ex device for invisible dummy window if (!bD3D9Hooked && InitD3D9Capture()) { + // InitD3D9Capture returned true, so D3D hooking was successful bFoundSomethingToHook = true; - bD3D9Hooked = true; + + // Dummy window no longer need, so its resources can be freed + if (dummyEvent) + CloseHandle(dummyEvent); + + if (hwndSender) + DestroyWindow(hwndSender); } - else - CheckD3D9Capture(); return bFoundSomethingToHook; } @@ -39,6 +42,7 @@ static inline HWND CreateDummyWindow(LPCTSTR lpClass, LPCTSTR lpName) return CreateWindowEx(0, lpClass, lpName, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL); } +// Creates an invisible window and process its messages static DWORD WINAPI DummyWindowThread(LPVOID lpBla) { WNDCLASS wc; @@ -69,6 +73,7 @@ static DWORD WINAPI DummyWindowThread(LPVOID lpBla) return 0; } +// Starting point for D3D hooking DWORD WINAPI CaptureThread(HANDLE hDllMainThread) { bool bSuccess = false; @@ -91,20 +96,10 @@ DWORD WINAPI CaptureThread(HANDLE hDllMainThread) CloseHandle(hWindowThread); + // Loop to attempt D3D hooking until it is successful while(!AttemptToHookSomething()) Sleep(50); - for (size_t n = 0; !bCaptureThreadStop; ++n) - { - // Less frequent loop interations reduce resource usage - if (n % 16 == 0) - AttemptToHookSomething(); - - // Overall interval for checking EndScene hook is still 4000ms - Sleep(250); - } - - DeleteCriticalSection(&d3d9EndMutex); - - return 0; + CloseHandle(hCaptureThread); + return 1; } \ No newline at end of file diff --git a/hook.h b/hook.h index 9436f69..a35ce2b 100644 --- a/hook.h +++ b/hook.h @@ -10,16 +10,17 @@ extern HINSTANCE g_hInstance; extern CRITICAL_SECTION d3d9EndMutex; -extern HANDLE dummyEvent; extern HWND hwndSender; -extern bool bCaptureThreadStop; +extern HANDLE hCaptureThread; +extern HANDLE hUnloadDLLFunc; bool InitD3D9Capture(); DWORD WINAPI CaptureThread(HANDLE hDllMainThread); -void CheckD3D9Capture(); +DWORD WINAPI UnloadDLLFunc(LPVOID lpParameter); typedef unsigned long UPARAM; +// Class to handle all hooking, rehooking, and unhooking class HookData { BYTE data[14]; diff --git a/main.cpp b/main.cpp index de8315b..0e636a8 100644 --- a/main.cpp +++ b/main.cpp @@ -37,7 +37,7 @@ int n_sourceEXE = 0; bool sourceEXE = false; HWND hwndClient = NULL; HANDLE hCaptureThread = NULL; -bool bCaptureThreadStop = false; +HANDLE hUnloadDLLFunc = NULL; int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { @@ -111,32 +111,32 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); if (!(hCaptureThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hDllMainThread, 0, 0))) - { CloseHandle(hDllMainThread); - return 0; - } } break; } case DLL_PROCESS_DETACH: - // Stop CS:GO and TF2 D3D9 hooking - if (n_sourceEXE <= 2) { - bCaptureThreadStop = true; - WaitForSingleObject(hCaptureThread, 300); + // Stop CS:GO and TF2 D3D9 hooking + CRawInput::hookLibrary(false); + CRawInput::unload(); - if (dummyEvent) - CloseHandle(dummyEvent); + if (n_sourceEXE <= 2 && !(hUnloadDLLFunc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)UnloadDLLFunc, NULL, 0, 0))) + { + // Revert to freeing DLL by returning DllMain + hUnloadDLLFunc = hInstance; - if (hwndSender) - DestroyWindow(hwndSender); - } + EnterCriticalSection(&d3d9EndMutex); + DeleteCriticalSection(&d3d9EndMutex); + } + // Free DLL with UnloadDLLFunc, not letting DllMain return + else + WaitForSingleObject(hUnloadDLLFunc, INFINITE); - CRawInput::hookLibrary(false); - CRawInput::unload(); - break; + break; + } } return 1; diff --git a/main.h b/main.h index d42f135..232a79c 100644 --- a/main.h +++ b/main.h @@ -14,7 +14,7 @@ // Raw input class #include "rawinput.h" -// D3D9 hooking class +// D3D hooking class #include "hook.h" extern "C" __declspec(dllexport) void entryPoint(); diff --git a/rawinput.cpp b/rawinput.cpp index 4346dd1..23756db 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -55,7 +55,7 @@ int CRawInput::SCP = 0; bool CRawInput::bSubclass = false; HANDLE CRawInput::hCreateThread = NULL; -// Fixed rare packet discrepancy +// Fixes rare packet discrepancy due to a collion CRITICAL_SECTION rawMouseData; struct WindowHandleStructure @@ -129,7 +129,6 @@ bool CRawInput::initWindow(WCHAR* pwszError) // Unregister the window class UnregisterClass(INPUTWINDOW, NULL); - return true; } @@ -175,6 +174,7 @@ BOOL CALLBACK CRawInput::EnumWindowsProc(HWND WindowHandle, LPARAM lParam) if (data->PID != PID || GetWindow(WindowHandle, GW_OWNER) || !IsWindowVisible(WindowHandle)) return TRUE; + // Found visible, main program window matching current process ID data->WindowHandle = WindowHandle; return FALSE; } @@ -192,12 +192,14 @@ bool CRawInput::clientCenter() if (GetClientRect(hwndClient, &rectClient)) { + // Calculated relative window center long xClient = rectClient.right / 2; long yClient = rectClient.bottom / 2; centerPoint.x = xClient; centerPoint.y = yClient; if (ClientToScreen(hwndClient, ¢erPoint)) + // Translated relative window center to resolution coords return true; } @@ -230,8 +232,10 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA if (!rwInput->header.dwType) { + // Avoid collisions with hGetCursorPos EnterCriticalSection(&rawMouseData); + // Accumulate cursor pos change from raw packets CRawInput::x += rwInput->data.mouse.lLastX; CRawInput::y += rwInput->data.mouse.lLastY; @@ -312,6 +316,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { + // Avoid collisions with accumulation of raw input packets EnterCriticalSection(&rawMouseData); // Split off raw input handling to accumulate independently @@ -395,7 +400,6 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) } CRawInput::SCP = 0; - return 0; } @@ -420,7 +424,7 @@ LRESULT CALLBACK CRawInput::SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, return DefSubclassProc(hWnd, uMsg, wParam, lParam); } -void CRawInput::blockInput() +DWORD WINAPI CRawInput::blockInput(LPVOID lpParameter) { BlockInput(TRUE); @@ -431,7 +435,9 @@ void CRawInput::blockInput() CRawInput::signal = 0; BlockInput(FALSE); + CloseHandle(CRawInput::hCreateThread); + return 1; } bool CRawInput::hookLibrary(bool bInstall) diff --git a/rawinput.h b/rawinput.h index d15e037..371f724 100644 --- a/rawinput.h +++ b/rawinput.h @@ -27,30 +27,28 @@ extern int consec_EndScene; class CRawInput { public: - // Initialize raw input + // Initialize RInput components, register for raw input static bool initialize(WCHAR* pwszError); - // Initialization static bool initWindow(WCHAR* pwszError); static bool initInput(WCHAR* pwszError); - // Identify window of the injected process + // Identify main visible window of the injected process static BOOL CALLBACK EnumWindowsProc(HWND WindowHandle, LPARAM lParam); static HWND clientWindow(unsigned long PID); - // Find window center of the injected process + // Get coords of the injected process' window center static bool clientCenter(); - // Poll input + // Poll mouse input static unsigned int pollInput(); - // Input window proc + // Mouse input window proc static LRESULT __stdcall wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - // Hooked functions handling + // Hooked cursor functions handling static int __stdcall hSetCursorPos(int x, int y); static int __stdcall hGetCursorPos(LPPOINT lpPoint); - // TF2 subclassing + // TF2 subclassing and input blocking when alt-tabbing back in static LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); - // TF2 input blocking when alt-tabbing back in - static void blockInput(); - // Enables or disables the hooking + static DWORD WINAPI blockInput(LPVOID lpParameter); + // Enables or disables the mouse hooking and CRITICAL_SECTION static bool hookLibrary(bool bInstall); - // Unload raw input + // Unload RInput components, stop raw input reads from mouse static void unload(); private: diff --git a/versioninfo.h b/versioninfo.h index a19741e..2888c6e 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,8 +1,8 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.41" -#define RINPUTFVER 1,41 +#define RINPUTVER "v1.42" +#define RINPUTFVER 1,42 #define IDI_ICON 101 #define RINPUTINT "RInput" From d286dc9ea558d76f395178719df392dfdcc51c75 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Fri, 11 Dec 2015 00:04:34 -0500 Subject: [PATCH 46/57] realized that I needed another logic path for DLL_PROCESS_DETACH --- main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.cpp b/main.cpp index 0e636a8..0b3a3c9 100644 --- a/main.cpp +++ b/main.cpp @@ -131,6 +131,8 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) EnterCriticalSection(&d3d9EndMutex); DeleteCriticalSection(&d3d9EndMutex); } + else if (n_sourceEXE > 2) + hUnloadDLLFunc = hInstance; // Free DLL with UnloadDLLFunc, not letting DllMain return else WaitForSingleObject(hUnloadDLLFunc, INFINITE); From 2792dd772d13a7a2fac6c823b47349131652d18a Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 14 Dec 2015 00:28:52 -0500 Subject: [PATCH 47/57] redid some of the linkage and optimization options, and removed some unnecessary functions/code --- README | 2 +- README.mdown | 2 +- RInput.vcproj | 38 ++++++++++++++++++++------------------ d3d9.cpp | 32 ++++++++++++++------------------ hook.cpp | 14 ++++++-------- hook.h | 2 ++ main.cpp | 37 +++++++++++++++++++------------------ main.h | 5 ----- rawinput.cpp | 2 +- rawinput.h | 2 -- 10 files changed, 64 insertions(+), 72 deletions(-) diff --git a/README b/README index 5b3805c..1f49df4 100644 --- a/README +++ b/README @@ -4,8 +4,8 @@ RInput is an alternative to in-game raw input, which some users perceive as havi Requirements -------------------------------------- -- OS: Windows XP or later - CPU: Intel Pentium 4 or later; or, AMD Opteron, Athlon 64 or later +- OS: Windows XP or later (Windows 7 or later for all the bug fixes to work if using DirectX 8 in TF2, i.e. -dxlevel 80 or 81) - Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34 but only needed if you get a pop-up error like "MSVCR90.dll is missing" when trying to run RInput) Note diff --git a/README.mdown b/README.mdown index 467d934..fc41d7d 100644 --- a/README.mdown +++ b/README.mdown @@ -2,8 +2,8 @@ RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements -- OS: Windows XP or later - CPU: Intel Pentium 4 or later; or, AMD Opteron, Athlon 64 or later +- OS: Windows XP or later (Windows 7 or later for all the bug fixes to work if using DirectX 8 in TF2, i.e. -dxlevel 80 or 81) - Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34 but only needed if you get a pop-up error like "MSVCR90.dll is missing" when trying to run RInput) ## Note diff --git a/RInput.vcproj b/RInput.vcproj index 017d080..d9f0c57 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -42,24 +42,25 @@ @@ -14,9 +15,7 @@ HookData d3d9EndScene; -static LPVOID lpCurrentDevice = NULL; -int consec_EndScene = 0; -static HMODULE hD3D9Dll = NULL; +CRITICAL_SECTION d3d9EndMutex; typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); @@ -30,7 +29,7 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) while(!TryEnterCriticalSection(&d3d9EndMutex)) { - Sleep(16); + Sleep(0); if (hUnloadDLLFunc) return hRes; @@ -38,16 +37,6 @@ HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) d3d9EndScene.Unhook(); - if(lpCurrentDevice == NULL) - { - IDirect3D9 *d3d; - - if(SUCCEEDED(device->GetDirect3D(&d3d))) - d3d->Release(); - - lpCurrentDevice = device; - } - hRes = device->EndScene(); // Consecutive EndScene calls without Get/SetCursorPos pair @@ -78,6 +67,8 @@ bool InitD3D9Capture() wcscat_s(lpD3D9Path, MAX_PATH, wa); delete[] wa; + HMODULE hD3D9Dll = NULL; + // Start creation of D3D9Ex device in dummy window if (hD3D9Dll = GetModuleHandleW(lpD3D9Path)) { @@ -110,6 +101,8 @@ bool InitD3D9Capture() // D3D9Ex device address is the D3D9 vTable address UPARAM *vtable = *(UPARAM**)deviceEx; + InitializeCriticalSection(&d3d9EndMutex); + // EndScene address is a fixed offset in vTable d3d9EndScene.Hook((FARPROC)*(vtable+(168/4)), (FARPROC)D3D9EndScene); @@ -129,11 +122,14 @@ bool InitD3D9Capture() // Releases remaining resources and frees the RInput DLL DWORD WINAPI UnloadDLLFunc(LPVOID lpParameter) { - EnterCriticalSection(&d3d9EndMutex); + if (bD3D9Hooked) + { + EnterCriticalSection(&d3d9EndMutex); - d3d9EndScene.Unhook(); + d3d9EndScene.Unhook(); - DeleteCriticalSection(&d3d9EndMutex); + DeleteCriticalSection(&d3d9EndMutex); + } CloseHandle(hUnloadDLLFunc); FreeLibraryAndExitThread(g_hInstance, 0); diff --git a/hook.cpp b/hook.cpp index edb119a..46ff615 100644 --- a/hook.cpp +++ b/hook.cpp @@ -1,6 +1,7 @@ /******************************************************************************** hook.h, hook.cpp, and d3d9.cpp contain the code for fixing the bugs in - the CS:GO buy and escape menus, and are only called for CS:GO. + the CS:GO buy and escape menus, and for fixing the bugs in the TF2 MVM + Upgrade Station menu and some of the bugs in TF2's backpack. Copyright (C) 2012 Hugh Bailey @@ -11,18 +12,17 @@ #include "hook.h" HWND hwndSender = NULL; -static bool bD3D9Hooked = false; +bool bD3D9Hooked = false; static HANDLE dummyEvent = NULL; -CRITICAL_SECTION d3d9EndMutex; -static inline bool AttemptToHookSomething() +inline bool AttemptToHookSomething() { bool bFoundSomethingToHook = false; // Creates D3D9Ex device for invisible dummy window if (!bD3D9Hooked && InitD3D9Capture()) { - // InitD3D9Capture returned true, so D3D hooking was successful + // InitD3D9Capture returned true, so D3D9 hooking was successful bFoundSomethingToHook = true; bD3D9Hooked = true; @@ -37,7 +37,7 @@ static inline bool AttemptToHookSomething() return bFoundSomethingToHook; } -static inline HWND CreateDummyWindow(LPCTSTR lpClass, LPCTSTR lpName) +inline HWND CreateDummyWindow(LPCTSTR lpClass, LPCTSTR lpName) { return CreateWindowEx(0, lpClass, lpName, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL); } @@ -86,8 +86,6 @@ DWORD WINAPI CaptureThread(HANDLE hDllMainThread) dummyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - InitializeCriticalSection(&d3d9EndMutex); - DWORD bla; HANDLE hWindowThread = CreateThread(NULL, 0, DummyWindowThread, NULL, 0, &bla); diff --git a/hook.h b/hook.h index a35ce2b..0997ecf 100644 --- a/hook.h +++ b/hook.h @@ -10,8 +10,10 @@ extern HINSTANCE g_hInstance; extern CRITICAL_SECTION d3d9EndMutex; +extern bool bD3D9Hooked; extern HWND hwndSender; extern HANDLE hCaptureThread; +extern int consec_EndScene; extern HANDLE hUnloadDLLFunc; bool InitD3D9Capture(); diff --git a/main.cpp b/main.cpp index 0b3a3c9..ab6ef53 100644 --- a/main.cpp +++ b/main.cpp @@ -39,6 +39,9 @@ HWND hwndClient = NULL; HANDLE hCaptureThread = NULL; HANDLE hUnloadDLLFunc = NULL; +void displayError(WCHAR* pwszError); +inline bool validateVersion(); + int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { UNREFERENCED_PARAMETER(lpReserved); @@ -172,24 +175,7 @@ extern "C" __declspec(dllexport) void entryPoint() displayError(L"Failed to poll mouse input"); } -// Validate that we are working with at least Windows XP -inline bool validateVersion() -{ - DWORD dwVersion = GetVersion(); - double fCompareVersion = LOBYTE(LOWORD(dwVersion)) + 0.1 * HIBYTE(LOWORD(dwVersion)); - return (dwVersion && fCompareVersion >= 5.1); -} - -void displayError(WCHAR* pwszError) -{ - MessageBoxW(NULL, pwszError, L"Raw Input error!", MB_ICONERROR | MB_OK); - - CRawInput::hookLibrary(false); - - unloadLibrary(); -} - -void unloadLibrary() +static void unloadLibrary() { __asm { @@ -201,4 +187,19 @@ void unloadLibrary() mov eax, FreeLibrary jmp eax } +} + +void displayError(WCHAR* pwszError) +{ + MessageBoxW(NULL, pwszError, L"Raw Input error!", MB_ICONERROR | MB_OK); + CRawInput::hookLibrary(false); + unloadLibrary(); +} + +// Validate that we are working with at least Windows XP +inline bool validateVersion() +{ + DWORD dwVersion = GetVersion(); + double fCompareVersion = LOBYTE(LOWORD(dwVersion)) + 0.1 * HIBYTE(LOWORD(dwVersion)); + return (dwVersion && fCompareVersion >= 5.1); } \ No newline at end of file diff --git a/main.h b/main.h index 232a79c..6f029ef 100644 --- a/main.h +++ b/main.h @@ -19,9 +19,4 @@ extern "C" __declspec(dllexport) void entryPoint(); -void displayError(WCHAR* pwszError); -void unloadLibrary(); - -inline bool validateVersion(); - #endif \ No newline at end of file diff --git a/rawinput.cpp b/rawinput.cpp index 23756db..e49a981 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -55,8 +55,8 @@ int CRawInput::SCP = 0; bool CRawInput::bSubclass = false; HANDLE CRawInput::hCreateThread = NULL; -// Fixes rare packet discrepancy due to a collion CRITICAL_SECTION rawMouseData; +int consec_EndScene = 0; struct WindowHandleStructure { diff --git a/rawinput.h b/rawinput.h index 371f724..857acbc 100644 --- a/rawinput.h +++ b/rawinput.h @@ -12,10 +12,8 @@ #pragma warning(disable: 4100) extern HWND hwndClient; -extern void displayError(WCHAR* pwszError); extern bool sourceEXE; extern int n_sourceEXE; -extern int consec_EndScene; /** * Note from original author (abort): From d715e53d17139d24cb9883fec7b9b9f9aad8163b Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Mon, 14 Dec 2015 00:35:07 -0500 Subject: [PATCH 48/57] corrected minor oversights --- README | 2 +- README.mdown | 2 +- RInput.vcproj | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README b/README index 1f49df4..5b3805c 100644 --- a/README +++ b/README @@ -4,8 +4,8 @@ RInput is an alternative to in-game raw input, which some users perceive as havi Requirements -------------------------------------- +- OS: Windows XP or later - CPU: Intel Pentium 4 or later; or, AMD Opteron, Athlon 64 or later -- OS: Windows XP or later (Windows 7 or later for all the bug fixes to work if using DirectX 8 in TF2, i.e. -dxlevel 80 or 81) - Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34 but only needed if you get a pop-up error like "MSVCR90.dll is missing" when trying to run RInput) Note diff --git a/README.mdown b/README.mdown index fc41d7d..467d934 100644 --- a/README.mdown +++ b/README.mdown @@ -2,8 +2,8 @@ RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements +- OS: Windows XP or later - CPU: Intel Pentium 4 or later; or, AMD Opteron, Athlon 64 or later -- OS: Windows XP or later (Windows 7 or later for all the bug fixes to work if using DirectX 8 in TF2, i.e. -dxlevel 80 or 81) - Microsoft Visual C++ 2008 Redistributable - x86 9.0.21022.218 or later (included in RInput release downloads starting with v1.34 but only needed if you get a pop-up error like "MSVCR90.dll is missing" when trying to run RInput) ## Note diff --git a/RInput.vcproj b/RInput.vcproj index d9f0c57..1608a26 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -48,7 +48,7 @@ EnableIntrinsicFunctions="true" FavorSizeOrSpeed="1" OmitFramePointers="false" - AdditionalIncludeDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include";"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1\Include\um";"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1\Include\shared";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Include"" + AdditionalIncludeDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Include"" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;RINPUT_EXPORTS;" StringPooling="true" MinimalRebuild="false" @@ -83,7 +83,7 @@ AdditionalDependencies="shlwapi.lib dxguid.lib psapi.lib comctl32.lib" Version="1.42-debug" LinkIncremental="1" - AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1\Lib\winv6.3\um\x86";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" + AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" EnableUAC="true" UACExecutionLevel="0" IgnoreDefaultLibraryNames="" From 11a1ba932294f19fffdc2026f7cd4c0f396d007b Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 17 Dec 2015 00:34:05 -0500 Subject: [PATCH 49/57] minor adjustments to optimizations, efficiency, and code style --- README | 2 +- README.mdown | 2 +- RInput.vcproj | 22 ++++----- d3d9.cpp | 7 +-- detours.h | 3 -- hook.cpp | 16 ++----- hook.h | 16 ++++++- main.cpp | 126 +++++++++++++++++++++++--------------------------- rawinput.cpp | 67 ++++++++++++++------------- rawinput.h | 15 +++--- 10 files changed, 134 insertions(+), 142 deletions(-) diff --git a/README b/README index 5b3805c..36c560b 100644 --- a/README +++ b/README @@ -26,7 +26,7 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Set Enable C++ Exceptions to Yes (/EHsc), as several of the included standard C++ headers use exception handlers - Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allows D3D9 to be hooked - Set the Entry Point as DllMain -- Add shlwapi.lib, dxguid.lib, psapi.lib, and comctl32.lib as an Additional Dependency in the Linker options +- Add shlwapi.lib, dxguid.lib, psapi.lib, comctl32.lib, and detours.lib to the Additional Dependency list in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/README.mdown b/README.mdown index 467d934..9c25cb4 100644 --- a/README.mdown +++ b/README.mdown @@ -22,7 +22,7 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Set Enable C++ Exceptions to Yes (/EHsc), as several of the included standard C++ headers use exception handlers - Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allows D3D9 to be hooked - Set the Entry Point as DllMain -- Add shlwapi.lib, dxguid.lib, psapi.lib, and comctl32.lib as an Additional Dependency in the Linker options +- Add shlwapi.lib, dxguid.lib, psapi.lib, comctl32.lib, and detours.lib to the Additional Dependency list in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/RInput.vcproj b/RInput.vcproj index 1608a26..63e0bca 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -42,12 +42,11 @@ #include -HookData d3d9EndScene; - CRITICAL_SECTION d3d9EndMutex; +HookData d3d9EndScene; typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); @@ -125,10 +124,8 @@ DWORD WINAPI UnloadDLLFunc(LPVOID lpParameter) if (bD3D9Hooked) { EnterCriticalSection(&d3d9EndMutex); - - d3d9EndScene.Unhook(); - DeleteCriticalSection(&d3d9EndMutex); + bD3D9Hooked = false; } CloseHandle(hUnloadDLLFunc); diff --git a/detours.h b/detours.h index 347b3f4..db44dc6 100644 --- a/detours.h +++ b/detours.h @@ -8,12 +8,9 @@ // Copyright 1995-2001, Microsoft Corporation // -#pragma once #ifndef _DETOURS_H_ #define _DETOURS_H_ -#pragma comment(lib, "detours.lib") - ////////////////////////////////////////////////////////////////////////////// // #ifndef GUID_DEFINED diff --git a/hook.cpp b/hook.cpp index 46ff615..22e64e1 100644 --- a/hook.cpp +++ b/hook.cpp @@ -74,23 +74,17 @@ static DWORD WINAPI DummyWindowThread(LPVOID lpBla) } // Starting point for D3D hooking -DWORD WINAPI CaptureThread(HANDLE hDllMainThread) +DWORD WINAPI CaptureThread(LPVOID lpParameter) { - bool bSuccess = false; - - if (hDllMainThread) - { - WaitForSingleObject(hDllMainThread, 150); - CloseHandle(hDllMainThread); - } - dummyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - DWORD bla; - HANDLE hWindowThread = CreateThread(NULL, 0, DummyWindowThread, NULL, 0, &bla); + HANDLE hWindowThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DummyWindowThread, NULL, 0, &bla); if (!hWindowThread) + { + CloseHandle(hCaptureThread); return 0; + } CloseHandle(hWindowThread); diff --git a/hook.h b/hook.h index 0997ecf..e98f610 100644 --- a/hook.h +++ b/hook.h @@ -1,8 +1,20 @@ +/******************************************************************************** + hook.h, hook.cpp, and d3d9.cpp contain the code for fixing the bugs in + the CS:GO buy and escape menus, and for fixing the bugs in the TF2 MVM + Upgrade Station menu and some of the bugs in TF2's backpack. + + Copyright (C) 2012 Hugh Bailey + + This new code was adapted from the Open Broadcaster Software source, + obtained from https://github.com/jp9000/OBS +********************************************************************************/ + #ifndef _HOOK_H_ #define _HOOK_H_ -#include #define PSAPI_VERSION 1 + +#include #include #include @@ -17,7 +29,7 @@ extern int consec_EndScene; extern HANDLE hUnloadDLLFunc; bool InitD3D9Capture(); -DWORD WINAPI CaptureThread(HANDLE hDllMainThread); +DWORD WINAPI CaptureThread(LPVOID lpParameter); DWORD WINAPI UnloadDLLFunc(LPVOID lpParameter); typedef unsigned long UPARAM; diff --git a/main.cpp b/main.cpp index ab6ef53..822c56b 100644 --- a/main.cpp +++ b/main.cpp @@ -35,27 +35,19 @@ HINSTANCE g_hInstance = NULL; int n_sourceEXE = 0; bool sourceEXE = false; -HWND hwndClient = NULL; -HANDLE hCaptureThread = NULL; HANDLE hUnloadDLLFunc = NULL; -void displayError(WCHAR* pwszError); -inline bool validateVersion(); - int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { - UNREFERENCED_PARAMETER(lpReserved); - switch(dwReason) { case DLL_PROCESS_ATTACH: + // No need for a threaded entry + if (!DisableThreadLibraryCalls(hInstance)) + return 0; + else { - // No need for a threaded entry - if (!DisableThreadLibraryCalls(hInstance)) - return 0; - g_hInstance = hInstance; - char szEXEPath[MAX_PATH]; // Detect source games for enabling specific bug fixes @@ -104,77 +96,39 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) } else n_sourceEXE = 4; - - // Identify the window that matches the injected process - hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); - - // Start CS:GO and TF2 D3D9 hooking - if (n_sourceEXE <= 2) - { - HANDLE hDllMainThread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId()); - - if (!(hCaptureThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, (LPVOID)hDllMainThread, 0, 0))) - CloseHandle(hDllMainThread); - } - - break; } + break; + case DLL_PROCESS_DETACH: + // Stop CS:GO and TF2 D3D9 hooking + CRawInput::hookLibrary(false); + CRawInput::unload(); + + if (n_sourceEXE <= 2 && !(hUnloadDLLFunc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)UnloadDLLFunc, NULL, 0, 0))) { - // Stop CS:GO and TF2 D3D9 hooking - CRawInput::hookLibrary(false); - CRawInput::unload(); + // Revert to freeing DLL by returning DllMain + hUnloadDLLFunc = hInstance; - if (n_sourceEXE <= 2 && !(hUnloadDLLFunc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)UnloadDLLFunc, NULL, 0, 0))) + if (bD3D9Hooked) { - // Revert to freeing DLL by returning DllMain - hUnloadDLLFunc = hInstance; - EnterCriticalSection(&d3d9EndMutex); DeleteCriticalSection(&d3d9EndMutex); + bD3D9Hooked = false; } - else if (n_sourceEXE > 2) - hUnloadDLLFunc = hInstance; - // Free DLL with UnloadDLLFunc, not letting DllMain return - else - WaitForSingleObject(hUnloadDLLFunc, INFINITE); - - break; } + else if (n_sourceEXE > 2) + hUnloadDLLFunc = hInstance; + else + // Free DLL with UnloadDLLFunc for CS:GO and TF2 + WaitForSingleObject(hUnloadDLLFunc, INFINITE); + + break; } return 1; } -extern "C" __declspec(dllexport) void entryPoint() -{ - HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENTNAME); - - if (!hEvent) - displayError(L"Could not open interprocess communication event."); - - WCHAR pwszError[ERROR_BUFFER_SIZE]; - - if (!validateVersion()) - displayError(L"You must at least have Microsoft Windows XP to use this library."); - - if (!CRawInput::initialize(pwszError)) - displayError(pwszError); - - if (!CRawInput::hookLibrary(true)) - displayError(L"Failed to hook Windows API cursor functions."); - - // Signal the injector that the injection and hooking are successful - if (!SetEvent(hEvent)) - displayError(L"Failed to signal the initialization event."); - - CloseHandle(hEvent); - - if (!CRawInput::pollInput()) - displayError(L"Failed to poll mouse input"); -} - static void unloadLibrary() { __asm @@ -193,6 +147,14 @@ void displayError(WCHAR* pwszError) { MessageBoxW(NULL, pwszError, L"Raw Input error!", MB_ICONERROR | MB_OK); CRawInput::hookLibrary(false); + + if (bD3D9Hooked) + { + EnterCriticalSection(&d3d9EndMutex); + DeleteCriticalSection(&d3d9EndMutex); + bD3D9Hooked = false; + } + unloadLibrary(); } @@ -202,4 +164,32 @@ inline bool validateVersion() DWORD dwVersion = GetVersion(); double fCompareVersion = LOBYTE(LOWORD(dwVersion)) + 0.1 * HIBYTE(LOWORD(dwVersion)); return (dwVersion && fCompareVersion >= 5.1); +} + +extern "C" __declspec(dllexport) void entryPoint() +{ + HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENTNAME); + + if (!hEvent) + displayError(L"Could not open interprocess communication event."); + + WCHAR pwszError[ERROR_BUFFER_SIZE]; + + if (!validateVersion()) + displayError(L"You must at least have Microsoft Windows XP to use this library."); + + if (!CRawInput::initialize(pwszError)) + displayError(pwszError); + + if (!CRawInput::hookLibrary(true)) + displayError(L"Failed to hook Windows API cursor functions."); + + // Signal the injector that the injection and hooking are successful + if (!SetEvent(hEvent)) + displayError(L"Failed to signal the initialization event."); + + CloseHandle(hEvent); + + if (!CRawInput::pollInput()) + displayError(L"Failed to poll mouse input"); } \ No newline at end of file diff --git a/rawinput.cpp b/rawinput.cpp index e49a981..eb89abe 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -34,29 +34,30 @@ #pragma intrinsic(memset) -// Define functions that are to be hooked and detoured -extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); -extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpSetCursorPos(int x, int y), SetCursorPos); - -HWND CRawInput::hwndInput = NULL; +HWND CRawInput::hwndClient = NULL; bool CRawInput::TF2unblock = false; long CRawInput::hold_x = 0; long CRawInput::hold_y = 0; +POINT CRawInput::centerPoint; +HWND CRawInput::hwndInput = NULL; long CRawInput::set_x = 0; long CRawInput::set_y = 0; bool CRawInput::bRegistered = false; -POINT CRawInput::centerPoint; +CRITICAL_SECTION CRawInput::rawMouseData; long CRawInput::x = 0; long CRawInput::y = 0; int CRawInput::signal = 0; +int consec_EndScene = 0; int CRawInput::consecG = 2; bool CRawInput::alttab = false; int CRawInput::SCP = 0; bool CRawInput::bSubclass = false; HANDLE CRawInput::hCreateThread = NULL; +HANDLE hCaptureThread = NULL; -CRITICAL_SECTION rawMouseData; -int consec_EndScene = 0; +// Define functions that are to be hooked and detoured +extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); +extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpSetCursorPos(int x, int y), SetCursorPos); struct WindowHandleStructure { @@ -77,17 +78,17 @@ bool CRawInput::initialize(WCHAR* pwszError) bool CRawInput::initWindow(WCHAR* pwszError) { - if (!hwndClient) - hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); + // Identify the window that matches the injected process + CRawInput::hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); - if (hwndClient) + if (CRawInput::hwndClient) { // TF2 Window must be active for backpack fixes to work - if (n_sourceEXE == 2 && GetForegroundWindow() != hwndClient) + if (n_sourceEXE == 2 && GetForegroundWindow() != CRawInput::hwndClient) { CRawInput::TF2unblock = true; BlockInput(TRUE); - ShowWindow(hwndClient, 1); + ShowWindow(CRawInput::hwndClient, 1); } // Set screen center until SetCursorPos is called @@ -190,7 +191,7 @@ bool CRawInput::clientCenter() { RECT rectClient; - if (GetClientRect(hwndClient, &rectClient)) + if (GetClientRect(CRawInput::hwndClient, &rectClient)) { // Calculated relative window center long xClient = rectClient.right / 2; @@ -198,7 +199,7 @@ bool CRawInput::clientCenter() centerPoint.x = xClient; centerPoint.y = yClient; - if (ClientToScreen(hwndClient, ¢erPoint)) + if (ClientToScreen(CRawInput::hwndClient, ¢erPoint)) // Translated relative window center to resolution coords return true; } @@ -233,13 +234,13 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA if (!rwInput->header.dwType) { // Avoid collisions with hGetCursorPos - EnterCriticalSection(&rawMouseData); + EnterCriticalSection(&CRawInput::rawMouseData); // Accumulate cursor pos change from raw packets CRawInput::x += rwInput->data.mouse.lLastX; CRawInput::y += rwInput->data.mouse.lLastY; - LeaveCriticalSection(&rawMouseData); + LeaveCriticalSection(&CRawInput::rawMouseData); } } @@ -317,7 +318,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) { // Avoid collisions with accumulation of raw input packets - EnterCriticalSection(&rawMouseData); + EnterCriticalSection(&CRawInput::rawMouseData); // Split off raw input handling to accumulate independently CRawInput::set_x += CRawInput::x; @@ -325,7 +326,7 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) CRawInput::x = 0; CRawInput::y = 0; - LeaveCriticalSection(&rawMouseData); + LeaveCriticalSection(&CRawInput::rawMouseData); if (n_sourceEXE == 2) { @@ -334,7 +335,7 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) else if (CRawInput::bSubclass) { // TF2 backpack fix not applied when in actual game - RemoveWindowSubclass(hwndClient, CRawInput::SubclassWndProc, 0); + RemoveWindowSubclass(CRawInput::hwndClient, CRawInput::SubclassWndProc, 0); CRawInput::bSubclass = false; } @@ -357,13 +358,13 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) // Fix for subtle TF2 backpack bug from alt-tabbing if (!CRawInput::bSubclass) { - if (!hwndClient) - hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); + if (!CRawInput::hwndClient) + CRawInput::hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); - if (hwndClient) + if (CRawInput::hwndClient) { // When in TF2 backpack, monitor its window messages - SetWindowSubclass(hwndClient, CRawInput::SubclassWndProc, 0, 1); + SetWindowSubclass(CRawInput::hwndClient, CRawInput::SubclassWndProc, 0, 1); CRawInput::bSubclass = true; } } @@ -377,11 +378,11 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) // Needed to not break backpack in TF2 if (!(CRawInput::SCP != 1 && CRawInput::consecG == 3)) { - if (!hwndClient) - hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); + if (!CRawInput::hwndClient) + CRawInput::hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); // Buy and escape menu bug fixes - if (hwndClient && CRawInput::clientCenter()) + if (CRawInput::hwndClient && CRawInput::clientCenter()) { CRawInput::set_x = CRawInput::centerPoint.x; CRawInput::set_y = CRawInput::centerPoint.y; @@ -428,8 +429,8 @@ DWORD WINAPI CRawInput::blockInput(LPVOID lpParameter) { BlockInput(TRUE); - // Unblock input after 8 SetCursorPos and/or GetCursorPos calls - for (size_t q = 0; CRawInput::signal < 9; ++q) + // Unblock input after 9 SetCursorPos and/or GetCursorPos calls + for (size_t q = 0; CRawInput::signal < 10; ++q) Sleep(16); CRawInput::signal = 0; @@ -447,14 +448,18 @@ bool CRawInput::hookLibrary(bool bInstall) if (!DetourFunctionWithTrampoline((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos) || !DetourFunctionWithTrampoline((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos)) return false; - InitializeCriticalSection(&rawMouseData); + InitializeCriticalSection(&CRawInput::rawMouseData); + + // Start CS:GO and TF2 D3D9 hooking + if (n_sourceEXE <= 2) + hCaptureThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, NULL, 0, 0); } else { DetourRemove((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos); DetourRemove((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos); - DeleteCriticalSection(&rawMouseData); + DeleteCriticalSection(&CRawInput::rawMouseData); } return true; diff --git a/rawinput.h b/rawinput.h index 857acbc..fbb70f1 100644 --- a/rawinput.h +++ b/rawinput.h @@ -1,19 +1,16 @@ #ifndef RAWINPUT_H_ #define RAWINPUT_H_ -#include -#include "detours.h" - #define RAWINPUTHDRSIZE sizeof(RAWINPUTHEADER) #define RAWPTRSIZE 40 #define INPUTWINDOW "RInput" -// Prevent warning level 4 warnings for detouring -#pragma warning(disable: 4100) +#include +#include "detours.h" -extern HWND hwndClient; extern bool sourceEXE; extern int n_sourceEXE; +extern DWORD WINAPI CaptureThread(LPVOID lpParameter); /** * Note from original author (abort): @@ -50,14 +47,16 @@ class CRawInput static void unload(); private: - static HWND hwndInput; + static HWND hwndClient; static bool TF2unblock; static long hold_x; static long hold_y; + static POINT centerPoint; + static HWND hwndInput; static long set_x; static long set_y; static bool bRegistered; - static POINT centerPoint; + static CRITICAL_SECTION rawMouseData; static long x; static long y; static int signal; From 2a078770a4c565514f1047b37fb27f40d83172f7 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Thu, 17 Dec 2015 20:41:51 -0500 Subject: [PATCH 50/57] Completely reworked D3D9 hooking to be a midhook, which has alleviated the occassional null pointer access violations that would cause games to crash immediately when RInput was injected (due to Steam's gameoverlayrenderer.dll also hooking the same functions) --- README | 4 +- README.mdown | 4 +- RInput.vcproj | 14 ++--- d3d9.cpp | 134 ------------------------------------------- d3d9hook.cpp | 84 +++++++++++++++++++++++++++ d3d9hook.h | 11 ++++ hook.cpp | 97 ------------------------------- hook.h | 156 -------------------------------------------------- main.cpp | 34 +---------- main.h | 9 ++- rawinput.cpp | 17 +++--- rawinput.h | 9 +-- 12 files changed, 122 insertions(+), 451 deletions(-) delete mode 100644 d3d9.cpp create mode 100644 d3d9hook.cpp create mode 100644 d3d9hook.h delete mode 100644 hook.cpp delete mode 100644 hook.h diff --git a/README b/README index 36c560b..e7718db 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ RInput Library v1.42 by Vols and Jezuz -------------------------------------- -RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. +RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for x86 (32-bit) games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. Requirements -------------------------------------- @@ -26,7 +26,7 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Set Enable C++ Exceptions to Yes (/EHsc), as several of the included standard C++ headers use exception handlers - Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allows D3D9 to be hooked - Set the Entry Point as DllMain -- Add shlwapi.lib, dxguid.lib, psapi.lib, comctl32.lib, and detours.lib to the Additional Dependency list in the Linker options +- Add shlwapi.lib, dxguid.lib, comctl32.lib, and detours.lib to the Additional Dependency list in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/README.mdown b/README.mdown index 9c25cb4..cb6694a 100644 --- a/README.mdown +++ b/README.mdown @@ -1,5 +1,5 @@ # RInput Library v1.42 by Vols and Jezuz -RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. +RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for x86 (32-bit) games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements - OS: Windows XP or later @@ -22,7 +22,7 @@ Compiled with Visual Studio 2008, though you may be able to get it to work with - Set Enable C++ Exceptions to Yes (/EHsc), as several of the included standard C++ headers use exception handlers - Set Calling Convention __cdecl (/Gd), as it seems to be the only one that performs well and allows D3D9 to be hooked - Set the Entry Point as DllMain -- Add shlwapi.lib, dxguid.lib, psapi.lib, comctl32.lib, and detours.lib to the Additional Dependency list in the Linker options +- Add shlwapi.lib, dxguid.lib, comctl32.lib, and detours.lib to the Additional Dependency list in the Linker options - Use x86 architecture for compiling (i.e., RInput does not support x64 applications) - MS Detours 1.5 header and library were acquired from http://home.comcast.net/~wiccaan/downloads/Detours.rar diff --git a/RInput.vcproj b/RInput.vcproj index 63e0bca..4fd31a0 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -79,7 +79,7 @@ - - - - This new code was adapted from the Open Broadcaster Software source, - obtained from https://github.com/jp9000/OBS -********************************************************************************/ - -#include "hook.h" -#include -#include - -CRITICAL_SECTION d3d9EndMutex; -HookData d3d9EndScene; - -typedef HRESULT(STDMETHODCALLTYPE *D3D9EndScenePROC)(IDirect3DDevice9 *device); - -HRESULT STDMETHODCALLTYPE D3D9EndScene(IDirect3DDevice9 *device) -{ - HRESULT hRes = E_FAIL; - - // Make sure CRITICAL_SECTION is not referenced after deletion - if (hUnloadDLLFunc) - return hRes; - - while(!TryEnterCriticalSection(&d3d9EndMutex)) - { - Sleep(0); - - if (hUnloadDLLFunc) - return hRes; - } - - d3d9EndScene.Unhook(); - - hRes = device->EndScene(); - - // Consecutive EndScene calls without Get/SetCursorPos pair - if (consec_EndScene < 6) - ++consec_EndScene; - - d3d9EndScene.Rehook(); - - LeaveCriticalSection(&d3d9EndMutex); - - return hRes; -} - -typedef IDirect3D9* (WINAPI*D3D9CREATEPROC)(UINT); -typedef HRESULT (WINAPI*D3D9CREATEEXPROC)(UINT, IDirect3D9Ex**); - -// Figure out the D3D9 device address and hook EndScene -bool InitD3D9Capture() -{ - bool bSuccess = false; - - // Get full path for the D3D9 DLL - wchar_t lpD3D9Path[MAX_PATH]; - SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, SHGFP_TYPE_CURRENT, lpD3D9Path); - size_t size = 11; - wchar_t* wa = new wchar_t[size]; - mbstowcs_s(&size, wa, 11, TEXT("\\d3d9.dll"), 11); - wcscat_s(lpD3D9Path, MAX_PATH, wa); - delete[] wa; - - HMODULE hD3D9Dll = NULL; - - // Start creation of D3D9Ex device in dummy window - if (hD3D9Dll = GetModuleHandleW(lpD3D9Path)) - { - D3D9CREATEEXPROC d3d9CreateEx = (D3D9CREATEEXPROC)GetProcAddress(hD3D9Dll, "Direct3DCreate9Ex"); - - if (d3d9CreateEx) - { - HRESULT hRes; - - IDirect3D9Ex *d3d9ex; - - if (SUCCEEDED(hRes = (*d3d9CreateEx)(D3D_SDK_VERSION, &d3d9ex))) - { - D3DPRESENT_PARAMETERS pp; - ZeroMemory(&pp, sizeof(pp)); - pp.Windowed = 1; - pp.SwapEffect = D3DSWAPEFFECT_FLIP; - pp.BackBufferFormat = D3DFMT_A8R8G8B8; - pp.BackBufferCount = 1; - pp.hDeviceWindow = (HWND)hwndSender; - pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - - IDirect3DDevice9Ex *deviceEx; - - if (SUCCEEDED(hRes = d3d9ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, hwndSender, D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_NOWINDOWCHANGES, &pp, NULL, &deviceEx))) - { - // D3D9Ex device made successfully in dummy window - bSuccess = true; - - // D3D9Ex device address is the D3D9 vTable address - UPARAM *vtable = *(UPARAM**)deviceEx; - - InitializeCriticalSection(&d3d9EndMutex); - - // EndScene address is a fixed offset in vTable - d3d9EndScene.Hook((FARPROC)*(vtable+(168/4)), (FARPROC)D3D9EndScene); - - deviceEx->Release(); - - d3d9EndScene.Rehook(); - } - - d3d9ex->Release(); - } - } - } - - return bSuccess; -} - -// Releases remaining resources and frees the RInput DLL -DWORD WINAPI UnloadDLLFunc(LPVOID lpParameter) -{ - if (bD3D9Hooked) - { - EnterCriticalSection(&d3d9EndMutex); - DeleteCriticalSection(&d3d9EndMutex); - bD3D9Hooked = false; - } - - CloseHandle(hUnloadDLLFunc); - FreeLibraryAndExitThread(g_hInstance, 0); - return 1; -} \ No newline at end of file diff --git a/d3d9hook.cpp b/d3d9hook.cpp new file mode 100644 index 0000000..b0dac95 --- /dev/null +++ b/d3d9hook.cpp @@ -0,0 +1,84 @@ +#include "d3d9hook.h" +#include +#include + +DWORD oD3D9EndScene = 0; + +DWORD vtableAdd(int num, DWORD D3D9Base) +{ + PDWORD vtabl; + + for (DWORD g = 0; g < 0x1c3000; ++g) + { + ++D3D9Base; + + // Alternative to signature search with FindPattern + if ((*(WORD*)(D3D9Base + 0x00)) == 0x06C7 && (*(WORD*)(D3D9Base + 0x06)) == 0x8689 && (*(WORD*)(D3D9Base + 0x0C)) == 0x8689) + { + D3D9Base += 2; + break; + } + else if (g >= 0x1c3000) + return 0; + } + + *(DWORD*)&vtabl = *(DWORD*)D3D9Base; + return vtabl[num]; +} + +// Midhook avoids access violations if D3D9 is hooked by another program +void JMPplace(BYTE* inFunc, DWORD deFunc, DWORD len) +{ + DWORD oldPro = 0; + VirtualProtect(inFunc, len, PAGE_EXECUTE_READWRITE, &oldPro); + DWORD relAddress = 0; + relAddress = (deFunc - (DWORD)inFunc) - 5; + *inFunc = 0xE9; + *((DWORD *)(inFunc + 0x1)) = relAddress; + + for (DWORD x = 0x5; x < len; x++) + *(inFunc + x) = 0x90; + + DWORD newPro = 0; + VirtualProtect(inFunc, len, oldPro, &newPro); +} + +__declspec(naked) HRESULT __stdcall D3D9EndScene() +{ + static LPDIRECT3DDEVICE9 pDevice; + + __asm mov edi, edi + __asm push ebp + __asm mov ebp, esp + __asm mov eax, dword ptr ss : [ebp + 0x8] + __asm mov pDevice, eax + __asm pushad + + if (consec_EndScene < 6) + ++consec_EndScene; + + __asm popad + __asm jmp[oD3D9EndScene] +} + +DWORD WINAPI D3D9HookThread(LPVOID lpParameter) +{ + DWORD vtableBase = 0; + HMODULE hD3D9Dll = NULL; + + while (!hD3D9Dll) + { + hD3D9Dll = GetModuleHandleA("d3d9.dll"); + Sleep(150); + } + + if (vtableBase = vtableAdd(42, (DWORD)hD3D9Dll)) + { + // D3D9 device vtable found + oD3D9EndScene = vtableBase + 5; + JMPplace((PBYTE)(oD3D9EndScene - 5), (DWORD)D3D9EndScene, 5); + } + + CloseHandle(hD3D9HookThread); + return 1; +} \ No newline at end of file diff --git a/d3d9hook.h b/d3d9hook.h new file mode 100644 index 0000000..8e9a89d --- /dev/null +++ b/d3d9hook.h @@ -0,0 +1,11 @@ +#ifndef _D3D9HOOK_H_ +#define _D3D9HOOK_H_ + +#include + +extern HANDLE hD3D9HookThread; +extern int consec_EndScene; + +DWORD WINAPI D3D9HookThread(LPVOID lpParameter); + +#endif \ No newline at end of file diff --git a/hook.cpp b/hook.cpp deleted file mode 100644 index 22e64e1..0000000 --- a/hook.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/******************************************************************************** - hook.h, hook.cpp, and d3d9.cpp contain the code for fixing the bugs in - the CS:GO buy and escape menus, and for fixing the bugs in the TF2 MVM - Upgrade Station menu and some of the bugs in TF2's backpack. - - Copyright (C) 2012 Hugh Bailey - - This new code was adapted from the Open Broadcaster Software source, - obtained from https://github.com/jp9000/OBS -********************************************************************************/ - -#include "hook.h" - -HWND hwndSender = NULL; -bool bD3D9Hooked = false; -static HANDLE dummyEvent = NULL; - -inline bool AttemptToHookSomething() -{ - bool bFoundSomethingToHook = false; - - // Creates D3D9Ex device for invisible dummy window - if (!bD3D9Hooked && InitD3D9Capture()) - { - // InitD3D9Capture returned true, so D3D9 hooking was successful - bFoundSomethingToHook = true; - bD3D9Hooked = true; - - // Dummy window no longer need, so its resources can be freed - if (dummyEvent) - CloseHandle(dummyEvent); - - if (hwndSender) - DestroyWindow(hwndSender); - } - - return bFoundSomethingToHook; -} - -inline HWND CreateDummyWindow(LPCTSTR lpClass, LPCTSTR lpName) -{ - return CreateWindowEx(0, lpClass, lpName, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL); -} - -// Creates an invisible window and process its messages -static DWORD WINAPI DummyWindowThread(LPVOID lpBla) -{ - WNDCLASS wc; - ZeroMemory(&wc, sizeof(wc)); - wc.style = CS_OWNDC; - wc.hInstance = g_hInstance; - wc.lpfnWndProc = (WNDPROC)DefWindowProc; - wc.lpszClassName = TEXT("RInputD3DSender"); - - if (RegisterClass(&wc)) - { - hwndSender = CreateDummyWindow(TEXT("RInputD3DSender"), NULL); - - if (!hwndSender) - return 0; - } - else - return 0; - - MSG msg; - - while (GetMessage(&msg, NULL, 0, 0) != 0) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - return 0; -} - -// Starting point for D3D hooking -DWORD WINAPI CaptureThread(LPVOID lpParameter) -{ - dummyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - DWORD bla; - HANDLE hWindowThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DummyWindowThread, NULL, 0, &bla); - - if (!hWindowThread) - { - CloseHandle(hCaptureThread); - return 0; - } - - CloseHandle(hWindowThread); - - // Loop to attempt D3D hooking until it is successful - while(!AttemptToHookSomething()) - Sleep(50); - - CloseHandle(hCaptureThread); - return 1; -} \ No newline at end of file diff --git a/hook.h b/hook.h deleted file mode 100644 index e98f610..0000000 --- a/hook.h +++ /dev/null @@ -1,156 +0,0 @@ -/******************************************************************************** - hook.h, hook.cpp, and d3d9.cpp contain the code for fixing the bugs in - the CS:GO buy and escape menus, and for fixing the bugs in the TF2 MVM - Upgrade Station menu and some of the bugs in TF2's backpack. - - Copyright (C) 2012 Hugh Bailey - - This new code was adapted from the Open Broadcaster Software source, - obtained from https://github.com/jp9000/OBS -********************************************************************************/ - -#ifndef _HOOK_H_ -#define _HOOK_H_ - -#define PSAPI_VERSION 1 - -#include -#include -#include - -#pragma intrinsic(memcpy) - -extern HINSTANCE g_hInstance; -extern CRITICAL_SECTION d3d9EndMutex; -extern bool bD3D9Hooked; -extern HWND hwndSender; -extern HANDLE hCaptureThread; -extern int consec_EndScene; -extern HANDLE hUnloadDLLFunc; - -bool InitD3D9Capture(); -DWORD WINAPI CaptureThread(LPVOID lpParameter); -DWORD WINAPI UnloadDLLFunc(LPVOID lpParameter); - -typedef unsigned long UPARAM; - -// Class to handle all hooking, rehooking, and unhooking -class HookData -{ - BYTE data[14]; - FARPROC func; - FARPROC hookFunc; - bool bHooked; - bool b64bitJump; - bool bAttemptedBounce; - LPVOID bounceAddress; - -public: - inline HookData() : bHooked(false), func(NULL), hookFunc(NULL), b64bitJump(false), bAttemptedBounce(false) {} - - inline bool Hook(FARPROC funcIn, FARPROC hookFuncIn) - { - if (bHooked) - { - if (funcIn == func && hookFunc != hookFuncIn) - { - hookFunc = hookFuncIn; - - Rehook(); - - return true; - } - - Unhook(); - } - - func = funcIn; - hookFunc = hookFuncIn; - - DWORD oldProtect; - - if (!VirtualProtect((LPVOID)func, 14, PAGE_EXECUTE_READWRITE, &oldProtect)) - return false; - - if (*(BYTE *)func == 0xE9 || *(BYTE *)func == 0xE8) - { - CHAR *modName, *ourName; - CHAR szModName[MAX_PATH]; - CHAR szOurName[MAX_PATH]; - - DWORD memAddress; - - MEMORY_BASIC_INFORMATION mem; - - INT_PTR jumpAddress = *(DWORD *)((BYTE *)func + 1) + (DWORD)func; - - if (VirtualQueryEx(GetCurrentProcess(), (LPVOID)jumpAddress, &mem, sizeof(mem)) && mem.State == MEM_COMMIT) - memAddress = (DWORD)mem.AllocationBase; - else - memAddress = jumpAddress; - - if (GetMappedFileNameA(GetCurrentProcess(), (LPVOID)memAddress, szModName, _countof(szModName) - 1)) - modName = szModName; - else if (GetModuleFileNameA((HMODULE)memAddress, szModName, _countof(szModName) - 1)) - modName = szModName; - else - modName = "unknown"; - - if (VirtualQueryEx(GetCurrentProcess(), (LPVOID)func, &mem, sizeof(mem)) && mem.State == MEM_COMMIT) - memAddress = (DWORD)mem.AllocationBase; - else - memAddress = (DWORD)func; - - if (GetMappedFileNameA(GetCurrentProcess(), (LPVOID)memAddress, szOurName, _countof(szOurName) - 1)) - ourName = szOurName; - else if (GetModuleFileNameA((HMODULE)memAddress, szOurName, _countof(szOurName) - 1)) - ourName = szOurName; - else - ourName = "unknown"; - } - - memcpy(data, (const void*)func, 14); - - return true; - } - - inline void Rehook(bool bForce=false) - { - if ((!bForce && bHooked) || !func) - return; - - UPARAM startAddr = UPARAM(func); - UPARAM targetAddr = UPARAM(hookFunc); - - ULONG64 offset, diff; - offset = targetAddr - (startAddr + 5); - - if (startAddr + 5 > targetAddr) - diff = startAddr + 5 - targetAddr; - else - diff = targetAddr - startAddr + 5; - - DWORD oldProtect; - VirtualProtect((LPVOID)func, 5, PAGE_EXECUTE_READWRITE, &oldProtect); - LPBYTE addrData = (LPBYTE)func; - *addrData = 0xE9; - *(DWORD*)(addrData+1) = DWORD(offset); - - bHooked = true; - } - - inline void Unhook() - { - if (!bHooked || !func) - return; - - UINT count = b64bitJump ? 14 : 5; - DWORD oldProtect; - VirtualProtect((LPVOID)func, count, PAGE_EXECUTE_READWRITE, &oldProtect); - memcpy((void*)func, data, count); - - bHooked = false; - } -}; - -#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index 822c56b..29aa950 100644 --- a/main.cpp +++ b/main.cpp @@ -35,7 +35,6 @@ HINSTANCE g_hInstance = NULL; int n_sourceEXE = 0; bool sourceEXE = false; -HANDLE hUnloadDLLFunc = NULL; int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { @@ -84,7 +83,7 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) for (int k = 1; k < 16; ++k) { - // check hl2.exe is TF2 + // Check hl2.exe is TF2 if (testTF2[k] != tf2[k]) n_sourceEXE = 5; } @@ -104,25 +103,6 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) // Stop CS:GO and TF2 D3D9 hooking CRawInput::hookLibrary(false); CRawInput::unload(); - - if (n_sourceEXE <= 2 && !(hUnloadDLLFunc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)UnloadDLLFunc, NULL, 0, 0))) - { - // Revert to freeing DLL by returning DllMain - hUnloadDLLFunc = hInstance; - - if (bD3D9Hooked) - { - EnterCriticalSection(&d3d9EndMutex); - DeleteCriticalSection(&d3d9EndMutex); - bD3D9Hooked = false; - } - } - else if (n_sourceEXE > 2) - hUnloadDLLFunc = hInstance; - else - // Free DLL with UnloadDLLFunc for CS:GO and TF2 - WaitForSingleObject(hUnloadDLLFunc, INFINITE); - break; } @@ -147,14 +127,6 @@ void displayError(WCHAR* pwszError) { MessageBoxW(NULL, pwszError, L"Raw Input error!", MB_ICONERROR | MB_OK); CRawInput::hookLibrary(false); - - if (bD3D9Hooked) - { - EnterCriticalSection(&d3d9EndMutex); - DeleteCriticalSection(&d3d9EndMutex); - bD3D9Hooked = false; - } - unloadLibrary(); } @@ -168,12 +140,12 @@ inline bool validateVersion() extern "C" __declspec(dllexport) void entryPoint() { - HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENTNAME); + HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "RInputEvent32"); if (!hEvent) displayError(L"Could not open interprocess communication event."); - WCHAR pwszError[ERROR_BUFFER_SIZE]; + WCHAR pwszError[256]; if (!validateVersion()) displayError(L"You must at least have Microsoft Windows XP to use this library."); diff --git a/main.h b/main.h index 6f029ef..9d9e81f 100644 --- a/main.h +++ b/main.h @@ -7,15 +7,14 @@ #define _WIN32_WINDOWS 0x0501 #define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN -// Amount of bytes to store an error string -#define ERROR_BUFFER_SIZE 256 -#define EVENTNAME "RInputEvent32" #define KERNEL_LIB L"kernel32.dll" +#include +#include // Raw input class #include "rawinput.h" -// D3D hooking class -#include "hook.h" +// D3D9 mid-hooking +#include "d3d9hook.h" extern "C" __declspec(dllexport) void entryPoint(); diff --git a/rawinput.cpp b/rawinput.cpp index eb89abe..a8ebc54 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -31,6 +31,7 @@ #include "rawinput.h" #include +#include "detours.h" #pragma intrinsic(memset) @@ -53,7 +54,7 @@ bool CRawInput::alttab = false; int CRawInput::SCP = 0; bool CRawInput::bSubclass = false; HANDLE CRawInput::hCreateThread = NULL; -HANDLE hCaptureThread = NULL; +HANDLE hD3D9HookThread = NULL; // Define functions that are to be hooked and detoured extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); @@ -111,7 +112,7 @@ bool CRawInput::initWindow(WCHAR* pwszError) memset(&wcex, 0, sizeof(WNDCLASSEX)); wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpfnWndProc = (WNDPROC)wpInput; - wcex.lpszClassName = INPUTWINDOW; + wcex.lpszClassName = "RInput"; if (!RegisterClassEx(&wcex)) { @@ -120,7 +121,7 @@ bool CRawInput::initWindow(WCHAR* pwszError) } // Create the window to catch WM_INPUT events - CRawInput::hwndInput = CreateWindowEx(NULL, INPUTWINDOW, INPUTWINDOW, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + CRawInput::hwndInput = CreateWindowEx(NULL, "RInput", "RInput", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); if (!CRawInput::hwndInput) { @@ -129,7 +130,7 @@ bool CRawInput::initWindow(WCHAR* pwszError) } // Unregister the window class - UnregisterClass(INPUTWINDOW, NULL); + UnregisterClass("RInput", NULL); return true; } @@ -223,11 +224,11 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA { case WM_INPUT: { - UINT uiSize = RAWPTRSIZE; - static unsigned char lpb[RAWPTRSIZE]; + UINT uiSize = 40; + static unsigned char lpb[40]; RAWINPUT* rwInput; - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &uiSize, RAWINPUTHDRSIZE) != -1) + if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &uiSize, sizeof(RAWINPUTHEADER)) != -1) { rwInput = (RAWINPUT*)lpb; @@ -452,7 +453,7 @@ bool CRawInput::hookLibrary(bool bInstall) // Start CS:GO and TF2 D3D9 hooking if (n_sourceEXE <= 2) - hCaptureThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThread, NULL, 0, 0); + hD3D9HookThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)D3D9HookThread, NULL, 0, 0); } else { diff --git a/rawinput.h b/rawinput.h index fbb70f1..f158b50 100644 --- a/rawinput.h +++ b/rawinput.h @@ -1,16 +1,11 @@ #ifndef RAWINPUT_H_ #define RAWINPUT_H_ -#define RAWINPUTHDRSIZE sizeof(RAWINPUTHEADER) -#define RAWPTRSIZE 40 -#define INPUTWINDOW "RInput" - #include -#include "detours.h" extern bool sourceEXE; extern int n_sourceEXE; -extern DWORD WINAPI CaptureThread(LPVOID lpParameter); +extern DWORD WINAPI D3D9HookThread(LPVOID lpParameter); /** * Note from original author (abort): @@ -41,7 +36,7 @@ class CRawInput // TF2 subclassing and input blocking when alt-tabbing back in static LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); static DWORD WINAPI blockInput(LPVOID lpParameter); - // Enables or disables the mouse hooking and CRITICAL_SECTION + // Enables or disables hooking and mouse critical section static bool hookLibrary(bool bInstall); // Unload RInput components, stop raw input reads from mouse static void unload(); From ad2d0cc701eecf86989cfd67d99cea510b2fa343 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Sat, 19 Dec 2015 10:51:37 -0500 Subject: [PATCH 51/57] made D3D9 device declaraction non-local, as local declaraction would sometimes give black screen or crashes when alt-tabbing back into CS:GO or TF2 in full-screen mode --- d3d9hook.cpp | 39 ++++++++++++++++++++++----------------- main.cpp | 5 +---- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/d3d9hook.cpp b/d3d9hook.cpp index b0dac95..236db02 100644 --- a/d3d9hook.cpp +++ b/d3d9hook.cpp @@ -2,9 +2,10 @@ #include #include -DWORD oD3D9EndScene = 0; +static LPDIRECT3DDEVICE9 pDevice; +static DWORD oD3D9EndScene = 0; -DWORD vtableAdd(int num, DWORD D3D9Base) +PDWORD vtableFind(DWORD D3D9Base) { PDWORD vtabl; @@ -23,7 +24,7 @@ DWORD vtableAdd(int num, DWORD D3D9Base) } *(DWORD*)&vtabl = *(DWORD*)D3D9Base; - return vtabl[num]; + return vtabl; } // Midhook avoids access violations if D3D9 is hooked by another program @@ -45,25 +46,29 @@ void JMPplace(BYTE* inFunc, DWORD deFunc, DWORD len) __declspec(naked) HRESULT __stdcall D3D9EndScene() { - static LPDIRECT3DDEVICE9 pDevice; - - __asm mov edi, edi - __asm push ebp - __asm mov ebp, esp - __asm mov eax, dword ptr ss : [ebp + 0x8] - __asm mov pDevice, eax - __asm pushad + __asm + { + mov edi, edi + push ebp + mov ebp, esp + mov eax, dword ptr ss:[ebp + 0x8] + mov pDevice, eax + pushad + } if (consec_EndScene < 6) ++consec_EndScene; - __asm popad - __asm jmp[oD3D9EndScene] + __asm + { + popad + jmp[oD3D9EndScene] + } } DWORD WINAPI D3D9HookThread(LPVOID lpParameter) { - DWORD vtableBase = 0; + PDWORD vtableBase; HMODULE hD3D9Dll = NULL; while (!hD3D9Dll) @@ -72,11 +77,11 @@ DWORD WINAPI D3D9HookThread(LPVOID lpParameter) Sleep(150); } - if (vtableBase = vtableAdd(42, (DWORD)hD3D9Dll)) + if (vtableBase = vtableFind((DWORD)hD3D9Dll)) { // D3D9 device vtable found - oD3D9EndScene = vtableBase + 5; - JMPplace((PBYTE)(oD3D9EndScene - 5), (DWORD)D3D9EndScene, 5); + oD3D9EndScene = vtableBase[42] + 5; + JMPplace((PBYTE)vtableBase[42], (DWORD)D3D9EndScene, 5); } CloseHandle(hD3D9HookThread); diff --git a/main.cpp b/main.cpp index 29aa950..e7f5792 100644 --- a/main.cpp +++ b/main.cpp @@ -54,12 +54,9 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { char TF2path[sizeof(szEXEPath)]; strcpy_s(TF2path, _countof(TF2path), szEXEPath); - PathStripPathA(szEXEPath); - // Bug fixes now limited to tested source games char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; - bool b_sourceEXE = false; for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) { @@ -109,7 +106,7 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) return 1; } -static void unloadLibrary() +void unloadLibrary() { __asm { From e7fe24938fbeda05cb899c04ef2c6cd8c64c3f91 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Sun, 20 Dec 2015 04:23:27 -0500 Subject: [PATCH 52/57] consolidated header files and added #define's to make program function clearer --- RInput.vcproj | 22 +++++----------------- d3d9hook.cpp | 6 +++--- d3d9hook.h | 11 ----------- detours.h | 6 ++---- icon.ico | Bin 1150 -> 0 bytes main.cpp | 18 +++++++++--------- main.h | 21 --------------------- rawinput.cpp | 29 ++++++++++++++--------------- stdafx.cpp | 5 +++++ rawinput.h => stdafx.h | 33 +++++++++++++++++++-------------- versioninfo.h | 11 ----------- versioninfo.rc | 22 +++++++++------------- 12 files changed, 66 insertions(+), 118 deletions(-) delete mode 100644 d3d9hook.h delete mode 100644 icon.ico delete mode 100644 main.h create mode 100644 stdafx.cpp rename rawinput.h => stdafx.h (76%) delete mode 100644 versioninfo.h diff --git a/RInput.vcproj b/RInput.vcproj index 4fd31a0..5cd7077 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -247,24 +247,12 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > - - - - - - @@ -273,10 +261,6 @@ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" > - - @@ -299,6 +283,10 @@ RelativePath=".\rawinput.cpp" > + + +#include "stdafx.h" #include +#include static LPDIRECT3DDEVICE9 pDevice; static DWORD oD3D9EndScene = 0; @@ -56,7 +56,7 @@ __declspec(naked) HRESULT __stdcall D3D9EndScene() pushad } - if (consec_EndScene < 6) + if (consec_EndScene < MAX_CONSEC_ENDSCENE) ++consec_EndScene; __asm diff --git a/d3d9hook.h b/d3d9hook.h deleted file mode 100644 index 8e9a89d..0000000 --- a/d3d9hook.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _D3D9HOOK_H_ -#define _D3D9HOOK_H_ - -#include - -extern HANDLE hD3D9HookThread; -extern int consec_EndScene; - -DWORD WINAPI D3D9HookThread(LPVOID lpParameter); - -#endif \ No newline at end of file diff --git a/detours.h b/detours.h index db44dc6..aa3863e 100644 --- a/detours.h +++ b/detours.h @@ -8,8 +8,7 @@ // Copyright 1995-2001, Microsoft Corporation // -#ifndef _DETOURS_H_ -#define _DETOURS_H_ +#pragma once ////////////////////////////////////////////////////////////////////////////// // @@ -608,6 +607,5 @@ inline PBYTE DetourGenNop(PBYTE pbCode) #endif DETOURS_INTERAL #endif // __cplusplus -#endif // _DETOURS_H_ // -//////////////////////////////////////////////////////////////// End of File. +//////////////////////////////////////////////////////////////// End of File. \ No newline at end of file diff --git a/icon.ico b/icon.ico deleted file mode 100644 index da4deceaf411f2c5f3422dc060dfcd32f1fa2681..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmbVMNla5w6#WJlCZK{00d?WR1q%}wE+kqZCdLh0mWqG~g|>A1NEtdv9FoEU0vkez zL{tO{v(Qg?jd-YMR9MjWdlIzE#O>#66Mq{-ljhQm_`9JQ9lLPCIFp%?c+&% zC3B5~fOC?u!+>WAa4!IM)^|tf8v?3mmo;RJJ_$i7YWZGGJLf?EQy}!>Cl(Sw+npHt z!mqIQ4G6#9MCS@n$r{EH@?}kZYAc_-qwFHh zb_Z22e_s$AzX3zh85mBV1zX}q&zSOE@0~=)OYT6<&wR<@67`-Bzy*s}#Vo$xWCqW! z(xM>srFXxSugvO0Pn`Q^PX}}BBb=Yy+nxY{m5)f_?8POe7!J=NH}3#)_a9Vxamjh* z3#`Q9Ur**cnXAu1SeS1kRt4bl`Bk6o9sVmDd~I0$!M#7=`EdvO0_UG)4Zh17-YBsA z8ECrNj?BGjf9qtWr=j^;JC=Dq-bJ8pTIIsY-i`CD!8@kUa;H2~FYnyqcOba_71k?V zu(oz9JxXrg<>cP=dEWUsppm^>nBPd. */ -#include "main.h" +#include "stdafx.h" +#include #include -HINSTANCE g_hInstance = NULL; -int n_sourceEXE = 0; -bool sourceEXE = false; +static HINSTANCE g_hInstance = NULL; + +// Expose the entry point function called by RInput.exe +extern "C" __declspec(dllexport) void entryPoint(); int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { @@ -64,9 +66,7 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) if ((std::string)szEXEPath == (std::string)*iSource_exes) { - sourceEXE = true; - - if (n_sourceEXE == 2) + if (n_sourceEXE == TF2) { // Make sure hl2.exe is TF2 PathRemoveFileSpecA(TF2path); @@ -82,7 +82,7 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { // Check hl2.exe is TF2 if (testTF2[k] != tf2[k]) - n_sourceEXE = 5; + n_sourceEXE = NOBUGFIXES; } } @@ -91,7 +91,7 @@ int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) } } else - n_sourceEXE = 4; + n_sourceEXE = NOBUGFIXES; } break; diff --git a/main.h b/main.h deleted file mode 100644 index 9d9e81f..0000000 --- a/main.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _MAIN_H_ -#define _MAIN_H_ - -#define STRICT -// Need at least Windows XP -#define WINVER 0x0501 -#define _WIN32_WINDOWS 0x0501 -#define _WIN32_WINNT 0x0501 -#define WIN32_LEAN_AND_MEAN -#define KERNEL_LIB L"kernel32.dll" - -#include -#include -// Raw input class -#include "rawinput.h" -// D3D9 mid-hooking -#include "d3d9hook.h" - -extern "C" __declspec(dllexport) void entryPoint(); - -#endif \ No newline at end of file diff --git a/rawinput.cpp b/rawinput.cpp index a8ebc54..7b9a5ce 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -29,12 +29,13 @@ along with RInput. If not, see . */ -#include "rawinput.h" -#include +#include "stdafx.h" #include "detours.h" +#include #pragma intrinsic(memset) +// Initialize static variables HWND CRawInput::hwndClient = NULL; bool CRawInput::TF2unblock = false; long CRawInput::hold_x = 0; @@ -48,13 +49,11 @@ CRITICAL_SECTION CRawInput::rawMouseData; long CRawInput::x = 0; long CRawInput::y = 0; int CRawInput::signal = 0; -int consec_EndScene = 0; int CRawInput::consecG = 2; bool CRawInput::alttab = false; int CRawInput::SCP = 0; bool CRawInput::bSubclass = false; HANDLE CRawInput::hCreateThread = NULL; -HANDLE hD3D9HookThread = NULL; // Define functions that are to be hooked and detoured extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); @@ -85,7 +84,7 @@ bool CRawInput::initWindow(WCHAR* pwszError) if (CRawInput::hwndClient) { // TF2 Window must be active for backpack fixes to work - if (n_sourceEXE == 2 && GetForegroundWindow() != CRawInput::hwndClient) + if (n_sourceEXE == TF2 && GetForegroundWindow() != CRawInput::hwndClient) { CRawInput::TF2unblock = true; BlockInput(TRUE); @@ -149,7 +148,7 @@ bool CRawInput::initInput(WCHAR* pwszError) memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); // Flag allows accumulation to continue for TF2 while alt-tabbed - if (n_sourceEXE == 2) + if (n_sourceEXE == TF2) rMouse.dwFlags = RIDEV_INPUTSINK; else rMouse.dwFlags = 0; @@ -267,15 +266,15 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) CRawInput::set_x = (long)x; CRawInput::set_y = (long)y; - if (sourceEXE) + if (n_sourceEXE != NOBUGFIXES) { - if (n_sourceEXE == 2) + if (n_sourceEXE == TF2) { if (CRawInput::signal >= 1) ++CRawInput::signal; // Bug fix for Steam overlay in TF2 backpack - if (consec_EndScene == 6 && CRawInput::consecG == 3 && !CRawInput::alttab) + if (consec_EndScene == MAX_CONSEC_ENDSCENE && CRawInput::consecG == MAX_CONSECG && !CRawInput::alttab) { if (CRawInput::SCP == 0) CRawInput::SCP = -1; @@ -301,7 +300,7 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) } else if (CRawInput::SCP == 2) { - if (n_sourceEXE != 2) + if (n_sourceEXE != TF2) CRawInput::SCP = 0; consec_EndScene = 0; @@ -329,7 +328,7 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) LeaveCriticalSection(&CRawInput::rawMouseData); - if (n_sourceEXE == 2) + if (n_sourceEXE == TF2) { if (CRawInput::signal >= 1) ++CRawInput::signal; @@ -340,11 +339,11 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) CRawInput::bSubclass = false; } - if (CRawInput::consecG < 3) + if (CRawInput::consecG < MAX_CONSECG) ++CRawInput::consecG; // Bug fix for cursor hitting side of screen in TF2 backpack - if (CRawInput::consecG == 3) + if (CRawInput::consecG == MAX_CONSECG) { if (CRawInput::set_x >= CRawInput::hold_x * 2) CRawInput::set_x = CRawInput::hold_x * 2 - 1; @@ -374,10 +373,10 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) if (!CRawInput::alttab) { - if (consec_EndScene == 6) + if (consec_EndScene == MAX_CONSEC_ENDSCENE) { // Needed to not break backpack in TF2 - if (!(CRawInput::SCP != 1 && CRawInput::consecG == 3)) + if (!(CRawInput::SCP != 1 && CRawInput::consecG == MAX_CONSECG)) { if (!CRawInput::hwndClient) CRawInput::hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); diff --git a/stdafx.cpp b/stdafx.cpp new file mode 100644 index 0000000..326dfa2 --- /dev/null +++ b/stdafx.cpp @@ -0,0 +1,5 @@ +#include "stdafx.h" + +int consec_EndScene = 0; +HANDLE hD3D9HookThread = NULL; +int n_sourceEXE = 0; \ No newline at end of file diff --git a/rawinput.h b/stdafx.h similarity index 76% rename from rawinput.h rename to stdafx.h index f158b50..d57f1ed 100644 --- a/rawinput.h +++ b/stdafx.h @@ -1,19 +1,26 @@ -#ifndef RAWINPUT_H_ -#define RAWINPUT_H_ +#pragma once + +#define STRICT +#define WINVER 0x0501 +#define _WIN32_WINDOWS 0x0501 +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN +#define KERNEL_LIB L"kernel32.dll" +#define RINPUTVER "v1.42" +#define RINPUTFVER 1,42 +#define TF2 2 +#define NOBUGFIXES 4 +#define MAX_CONSEC_ENDSCENE 6 +#define MAX_CONSECG 3 #include -extern bool sourceEXE; +extern int consec_EndScene; +extern HANDLE hD3D9HookThread; extern int n_sourceEXE; -extern DWORD WINAPI D3D9HookThread(LPVOID lpParameter); -/** - * Note from original author (abort): - * ---------------------------------- - * Sadly everything has been made static, as Win32 API does not support object oriented callbacks, as it has been written in C. - * To keep the performance as high as possible, I decided not to work with storing the class instance through Win32 API. - * Feel free to rewrite this to something more clean in coding terms :). - */ +DWORD WINAPI D3D9HookThread(LPVOID lpParameter); + class CRawInput { public: @@ -60,6 +67,4 @@ class CRawInput static int SCP; static bool bSubclass; static HANDLE hCreateThread; -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/versioninfo.h b/versioninfo.h deleted file mode 100644 index 2888c6e..0000000 --- a/versioninfo.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _VERSIONINFO_H_ -#define _VERSIONINFO_H_ - -#define RINPUTVER "v1.42" -#define RINPUTFVER 1,42 -#define IDI_ICON 101 - -#define RINPUTINT "RInput" -#define RINPUTDLL "RInput.dll" - -#endif diff --git a/versioninfo.rc b/versioninfo.rc index 7ef9981..7085c1f 100644 --- a/versioninfo.rc +++ b/versioninfo.rc @@ -1,7 +1,6 @@ -#include "versioninfo.h" -#define VS_VERSION_INFO 1 +#include "stdafx.h" -VS_VERSION_INFO VERSIONINFO +1 VERSIONINFO FILEVERSION RINPUTFVER PRODUCTVERSION RINPUTFVER FILEFLAGSMASK 0x17L @@ -10,20 +9,19 @@ VS_VERSION_INFO VERSIONINFO #else FILEFLAGS 0x0L #endif - FILEOS 0x4L - FILETYPE 0x0L + FILEOS 0x40000L + FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN - VALUE "CompanyName", "Digitalise.NET" - VALUE "FileDescription", "Utilize Raw Input in any game/application using Get- and SetCursorPos." + VALUE "FileDescription", "Raw input in any game using Get/SetCursorPos." VALUE "FileVersion", RINPUTVER - VALUE "InternalName", RINPUTINT - VALUE "LegalCopyright", "Copyright (C) 2008" - VALUE "OriginalFilename", RINPUTDLL + VALUE "InternalName", "RInput" + VALUE "LegalCopyright", "Copyright (C) 2015" + VALUE "OriginalFilename", "RInput.dll" VALUE "ProductName", "RInput Dynamic Link Library" VALUE "ProductVersion", RINPUTVER END @@ -32,6 +30,4 @@ BEGIN BEGIN VALUE "Translation", 0x409, 1200 END -END - -IDI_ICON ICON "icon.ico" \ No newline at end of file +END \ No newline at end of file From b0245fa808d1fc51bc4cf6db3861ac7e5024a334 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 29 Dec 2015 12:28:10 -0500 Subject: [PATCH 53/57] minor changes to fix an extremely rare chance of dropping a packet --- README | 2 +- README.mdown | 2 +- RInput.vcproj | 4 ++-- rawinput.cpp | 14 ++++++++------ stdafx.h | 6 +++--- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/README b/README index e7718db..82784ad 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.42 by Vols and Jezuz +RInput Library v1.43 by Vols and Jezuz -------------------------------------- RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for x86 (32-bit) games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. diff --git a/README.mdown b/README.mdown index cb6694a..a18953c 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.42 by Vols and Jezuz +# RInput Library v1.43 by Vols and Jezuz RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for x86 (32-bit) games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements diff --git a/RInput.vcproj b/RInput.vcproj index 5cd7077..2582519 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -80,7 +80,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="shlwapi.lib dxguid.lib comctl32.lib detours.lib" - Version="1.42-debug" + Version="1.43-debug" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" EnableUAC="true" @@ -190,7 +190,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="shlwapi.lib dxguid.lib comctl32.lib detours.lib" - Version="1.42" + Version="1.43" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" GenerateManifest="true" diff --git a/rawinput.cpp b/rawinput.cpp index 7b9a5ce..ecea631 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -233,7 +233,7 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA if (!rwInput->header.dwType) { - // Avoid collisions with hGetCursorPos + // Avoid collisions with hGet/SetCursorPos EnterCriticalSection(&CRawInput::rawMouseData); // Accumulate cursor pos change from raw packets @@ -278,14 +278,11 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) { if (CRawInput::SCP == 0) CRawInput::SCP = -1; - - goto skipGreset; } - - CRawInput::consecG = 0; + else + CRawInput::consecG = 0; } -skipGreset: ++CRawInput::SCP; // Alt-tab bug fix @@ -295,8 +292,13 @@ int __stdcall CRawInput::hSetCursorPos(int x, int y) // Console and buy menu bug fixes if (CRawInput::SCP == 1) { + // Avoid collisions with accumulation of raw input packets + EnterCriticalSection(&CRawInput::rawMouseData); + CRawInput::set_x -= CRawInput::x; CRawInput::set_y -= CRawInput::y; + + LeaveCriticalSection(&CRawInput::rawMouseData); } else if (CRawInput::SCP == 2) { diff --git a/stdafx.h b/stdafx.h index d57f1ed..07416ad 100644 --- a/stdafx.h +++ b/stdafx.h @@ -6,11 +6,11 @@ #define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN #define KERNEL_LIB L"kernel32.dll" -#define RINPUTVER "v1.42" -#define RINPUTFVER 1,42 +#define RINPUTVER "v1.43" +#define RINPUTFVER 1,43 #define TF2 2 #define NOBUGFIXES 4 -#define MAX_CONSEC_ENDSCENE 6 +#define MAX_CONSEC_ENDSCENE 7 #define MAX_CONSECG 3 #include From 9c6b587c05ac89e1774962224d624fa3b6e7f10c Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 5 Jan 2016 13:42:08 -0500 Subject: [PATCH 54/57] Combined code into a single class (or else what was the point of having the RInput object), reworked some of the bug fixes for CS:GO and TF2 that weren't working as intended, and made type casting more efficient and consistent --- RInput.vcproj | 22 +-- d3d9hook.cpp | 89 --------- detours.h | 4 +- main.cpp | 107 +++-------- rawinput.cpp | 500 +++++++++++++++++++++++++++++++------------------ stdafx.cpp | 5 - stdafx.h | 70 ------- versioninfo.rc | 2 +- 8 files changed, 359 insertions(+), 440 deletions(-) delete mode 100644 d3d9hook.cpp delete mode 100644 stdafx.cpp delete mode 100644 stdafx.h diff --git a/RInput.vcproj b/RInput.vcproj index 2582519..e4dd7b5 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -79,7 +79,7 @@ + + + + @@ -271,10 +279,6 @@ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > - - @@ -283,10 +287,6 @@ RelativePath=".\rawinput.cpp" > - - -#include - -static LPDIRECT3DDEVICE9 pDevice; -static DWORD oD3D9EndScene = 0; - -PDWORD vtableFind(DWORD D3D9Base) -{ - PDWORD vtabl; - - for (DWORD g = 0; g < 0x1c3000; ++g) - { - ++D3D9Base; - - // Alternative to signature search with FindPattern - if ((*(WORD*)(D3D9Base + 0x00)) == 0x06C7 && (*(WORD*)(D3D9Base + 0x06)) == 0x8689 && (*(WORD*)(D3D9Base + 0x0C)) == 0x8689) - { - D3D9Base += 2; - break; - } - else if (g >= 0x1c3000) - return 0; - } - - *(DWORD*)&vtabl = *(DWORD*)D3D9Base; - return vtabl; -} - -// Midhook avoids access violations if D3D9 is hooked by another program -void JMPplace(BYTE* inFunc, DWORD deFunc, DWORD len) -{ - DWORD oldPro = 0; - VirtualProtect(inFunc, len, PAGE_EXECUTE_READWRITE, &oldPro); - DWORD relAddress = 0; - relAddress = (deFunc - (DWORD)inFunc) - 5; - *inFunc = 0xE9; - *((DWORD *)(inFunc + 0x1)) = relAddress; - - for (DWORD x = 0x5; x < len; x++) - *(inFunc + x) = 0x90; - - DWORD newPro = 0; - VirtualProtect(inFunc, len, oldPro, &newPro); -} - -__declspec(naked) HRESULT __stdcall D3D9EndScene() -{ - __asm - { - mov edi, edi - push ebp - mov ebp, esp - mov eax, dword ptr ss:[ebp + 0x8] - mov pDevice, eax - pushad - } - - if (consec_EndScene < MAX_CONSEC_ENDSCENE) - ++consec_EndScene; - - __asm - { - popad - jmp[oD3D9EndScene] - } -} - -DWORD WINAPI D3D9HookThread(LPVOID lpParameter) -{ - PDWORD vtableBase; - HMODULE hD3D9Dll = NULL; - - while (!hD3D9Dll) - { - hD3D9Dll = GetModuleHandleA("d3d9.dll"); - Sleep(150); - } - - if (vtableBase = vtableFind((DWORD)hD3D9Dll)) - { - // D3D9 device vtable found - oD3D9EndScene = vtableBase[42] + 5; - JMPplace((PBYTE)vtableBase[42], (DWORD)D3D9EndScene, 5); - } - - CloseHandle(hD3D9HookThread); - return 1; -} \ No newline at end of file diff --git a/detours.h b/detours.h index aa3863e..53b1f64 100644 --- a/detours.h +++ b/detours.h @@ -8,7 +8,8 @@ // Copyright 1995-2001, Microsoft Corporation // -#pragma once +#ifndef _DETOURS_H_ +#define _DETOURS_H_ ////////////////////////////////////////////////////////////////////////////// // @@ -607,5 +608,6 @@ inline PBYTE DetourGenNop(PBYTE pbCode) #endif DETOURS_INTERAL #endif // __cplusplus +#endif // _DETOURS_H_ // //////////////////////////////////////////////////////////////// End of File. \ No newline at end of file diff --git a/main.cpp b/main.cpp index b3548b2..e92c769 100644 --- a/main.cpp +++ b/main.cpp @@ -29,106 +29,34 @@ along with RInput. If not, see . */ -#include "stdafx.h" -#include -#include +#include "main.h" static HINSTANCE g_hInstance = NULL; -// Expose the entry point function called by RInput.exe -extern "C" __declspec(dllexport) void entryPoint(); - -int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: // No need for a threaded entry if (!DisableThreadLibraryCalls(hInstance)) - return 0; - else - { - g_hInstance = hInstance; - char szEXEPath[MAX_PATH]; - - // Detect source games for enabling specific bug fixes - if (GetModuleFileNameA(NULL, szEXEPath, sizeof(szEXEPath))) - { - char TF2path[sizeof(szEXEPath)]; - strcpy_s(TF2path, _countof(TF2path), szEXEPath); - PathStripPathA(szEXEPath); - // Bug fixes now limited to tested source games - char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; - - for (char **iSource_exes = source_exes; *iSource_exes != NULL; ++iSource_exes) - { - ++n_sourceEXE; - - if ((std::string)szEXEPath == (std::string)*iSource_exes) - { - if (n_sourceEXE == TF2) - { - // Make sure hl2.exe is TF2 - PathRemoveFileSpecA(TF2path); - std::string sTF2path = (std::string)TF2path; - char testTF2[16]; - - for (size_t j = 1; j <= 15; ++j) - testTF2[j] = TF2path[sTF2path.size() + j - 15]; - - char tf2[16] = "Team Fortress 2"; - - for (int k = 1; k < 16; ++k) - { - // Check hl2.exe is TF2 - if (testTF2[k] != tf2[k]) - n_sourceEXE = NOBUGFIXES; - } - } - - break; - } - } - } - else - n_sourceEXE = NOBUGFIXES; - } + return FALSE; + g_hInstance = hInstance; break; case DLL_PROCESS_DETACH: - // Stop CS:GO and TF2 D3D9 hooking + // Unhook cursor functions and unload from injected process CRawInput::hookLibrary(false); CRawInput::unload(); break; } - return 1; -} - -void unloadLibrary() -{ - __asm - { - push -2 - push 0 - push g_hInstance - mov eax, TerminateThread - push eax - mov eax, FreeLibrary - jmp eax - } -} - -void displayError(WCHAR* pwszError) -{ - MessageBoxW(NULL, pwszError, L"Raw Input error!", MB_ICONERROR | MB_OK); - CRawInput::hookLibrary(false); - unloadLibrary(); + return TRUE; } // Validate that we are working with at least Windows XP -inline bool validateVersion() +bool validateVersion() { DWORD dwVersion = GetVersion(); double fCompareVersion = LOBYTE(LOWORD(dwVersion)) + 0.1 * HIBYTE(LOWORD(dwVersion)); @@ -161,4 +89,25 @@ extern "C" __declspec(dllexport) void entryPoint() if (!CRawInput::pollInput()) displayError(L"Failed to poll mouse input"); +} + +void unloadLibrary() +{ + __asm + { + push -2 + push 0 + push g_hInstance + mov eax, TerminateThread + push eax + mov eax, FreeLibrary + jmp eax + } +} + +void displayError(WCHAR* pwszError) +{ + MessageBoxW(NULL, pwszError, L"Raw Input error!", MB_ICONERROR | MB_OK); + CRawInput::hookLibrary(FALSE); + unloadLibrary(); } \ No newline at end of file diff --git a/rawinput.cpp b/rawinput.cpp index ecea631..145b2c1 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -29,48 +29,85 @@ along with RInput. If not, see . */ -#include "stdafx.h" -#include "detours.h" -#include +#include "rawinput.h" #pragma intrinsic(memset) -// Initialize static variables +// Define functions that are to be hooked and detoured +extern "C" DETOUR_TRAMPOLINE(BOOL WINAPI TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); +extern "C" DETOUR_TRAMPOLINE(BOOL WINAPI TrmpSetCursorPos(int x, int y), SetCursorPos); + +unsigned char CRawInput::n_sourceEXE = 0; HWND CRawInput::hwndClient = NULL; bool CRawInput::TF2unblock = false; +POINT CRawInput::centerPoint; +long CRawInput::leftBoundary = 0; +long CRawInput::rightBoundary = 0; +long CRawInput::topBoundary = 0; +long CRawInput::bottomBoundary = 0; long CRawInput::hold_x = 0; long CRawInput::hold_y = 0; -POINT CRawInput::centerPoint; +CRITICAL_SECTION CRawInput::rawMouseData; +long CRawInput::x = 0; +long CRawInput::y = 0; HWND CRawInput::hwndInput = NULL; long CRawInput::set_x = 0; long CRawInput::set_y = 0; bool CRawInput::bRegistered = false; -CRITICAL_SECTION CRawInput::rawMouseData; -long CRawInput::x = 0; -long CRawInput::y = 0; -int CRawInput::signal = 0; -int CRawInput::consecG = 2; -bool CRawInput::alttab = false; -int CRawInput::SCP = 0; +unsigned char CRawInput::signal = 0; bool CRawInput::bSubclass = false; HANDLE CRawInput::hCreateThread = NULL; - -// Define functions that are to be hooked and detoured -extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); -extern "C" DETOUR_TRAMPOLINE(int __stdcall TrmpSetCursorPos(int x, int y), SetCursorPos); - -struct WindowHandleStructure -{ - unsigned long PID; - HWND WindowHandle; -}; +unsigned char CRawInput::consecG = 2; +bool CRawInput::alttab = false; +unsigned char CRawInput::consec_EndScene = 0; +char CRawInput::SCP = 0; +HANDLE CRawInput::hD3D9HookThread = NULL; +DWORD CRawInput::oD3D9EndScene = 0; bool CRawInput::initialize(WCHAR* pwszError) { - if (!initWindow(pwszError)) + char szEXEPath[MAX_PATH]; + + // Detect source games for enabling specific bug fixes + if (GetModuleFileNameA(NULL, (LPCH)szEXEPath, (DWORD)sizeof(szEXEPath))) + { + char TF2path[sizeof(szEXEPath)]; + strcpy_s(TF2path, sizeof(TF2path), (const char*)szEXEPath); + PathStripPathA((LPSTR)szEXEPath); + char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; + + // Bug fixes now limited to tested source games + for (++CRawInput::n_sourceEXE; source_exes[CRawInput::n_sourceEXE - 1] != NULL; ++CRawInput::n_sourceEXE) + { + if ((std::string)szEXEPath == (std::string)source_exes[CRawInput::n_sourceEXE - 1]) + { + if (CRawInput::n_sourceEXE == TF2) + { + PathRemoveFileSpecA((LPSTR)TF2path); + char tf2[16] = "Team Fortress 2"; + + // Make sure hl2.exe is TF2 + for (size_t k = 0; k < 15; ++k) + { + if (TF2path[((std::string)TF2path).size() + k - 15] != tf2[k]) + { + ++CRawInput::n_sourceEXE; + break; + } + } + } + + break; + } + } + } + else + CRawInput::n_sourceEXE = NO_BUG_FIXES; + + if (!CRawInput::initWindow(pwszError)) return false; - if (!initInput(pwszError)) + if (!CRawInput::initInput(pwszError)) return false; return true; @@ -79,12 +116,12 @@ bool CRawInput::initialize(WCHAR* pwszError) bool CRawInput::initWindow(WCHAR* pwszError) { // Identify the window that matches the injected process - CRawInput::hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); + EnumWindows(CRawInput::EnumWindowsProc, (LPARAM)GetCurrentProcessId()); if (CRawInput::hwndClient) { // TF2 Window must be active for backpack fixes to work - if (n_sourceEXE == TF2 && GetForegroundWindow() != CRawInput::hwndClient) + if (CRawInput::n_sourceEXE == TF2 && GetForegroundWindow() != CRawInput::hwndClient) { CRawInput::TF2unblock = true; BlockInput(TRUE); @@ -110,7 +147,7 @@ bool CRawInput::initWindow(WCHAR* pwszError) WNDCLASSEX wcex; memset(&wcex, 0, sizeof(WNDCLASSEX)); wcex.cbSize = sizeof(WNDCLASSEX); - wcex.lpfnWndProc = (WNDPROC)wpInput; + wcex.lpfnWndProc = (WNDPROC)CRawInput::wpInput; wcex.lpszClassName = "RInput"; if (!RegisterClassEx(&wcex)) @@ -133,110 +170,66 @@ bool CRawInput::initWindow(WCHAR* pwszError) return true; } -bool CRawInput::initInput(WCHAR* pwszError) -{ - POINT defCor; - - // Raw input accumulators initialized to starting cursor position - if (GetCursorPos(&defCor)) - { - CRawInput::set_x = defCor.x; - CRawInput::set_y = defCor.y; - } - - RAWINPUTDEVICE rMouse; - memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); - - // Flag allows accumulation to continue for TF2 while alt-tabbed - if (n_sourceEXE == TF2) - rMouse.dwFlags = RIDEV_INPUTSINK; - else - rMouse.dwFlags = 0; - - rMouse.hwndTarget = CRawInput::hwndInput; - rMouse.usUsagePage = 0x01; - rMouse.usUsage = 0x02; - - if (!RegisterRawInputDevices(&rMouse, 1, sizeof(RAWINPUTDEVICE))) - { - lstrcpyW(pwszError, L"Failed to register raw input device!"); - return false; - } - - return (bRegistered = true); -} - BOOL CALLBACK CRawInput::EnumWindowsProc(HWND WindowHandle, LPARAM lParam) { unsigned long PID = 0; - WindowHandleStructure* data = reinterpret_cast(lParam); GetWindowThreadProcessId(WindowHandle, &PID); - if (data->PID != PID || GetWindow(WindowHandle, GW_OWNER) || !IsWindowVisible(WindowHandle)) + if (PID != (unsigned long)lParam || GetWindow(WindowHandle, GW_OWNER) || !IsWindowVisible(WindowHandle)) return TRUE; // Found visible, main program window matching current process ID - data->WindowHandle = WindowHandle; + CRawInput::hwndClient = WindowHandle; return FALSE; } -HWND CRawInput::clientWindow(unsigned long PID) -{ - WindowHandleStructure data = {PID, NULL}; - EnumWindows(EnumWindowsProc, reinterpret_cast(&data)); - return data.WindowHandle; -} - bool CRawInput::clientCenter() { RECT rectClient; + // Try to get relative window center if (GetClientRect(CRawInput::hwndClient, &rectClient)) { - // Calculated relative window center - long xClient = rectClient.right / 2; - long yClient = rectClient.bottom / 2; - centerPoint.x = xClient; - centerPoint.y = yClient; - - if (ClientToScreen(CRawInput::hwndClient, ¢erPoint)) - // Translated relative window center to resolution coords + CRawInput::centerPoint.x = (long)((unsigned long)rectClient.right / 2); + CRawInput::centerPoint.y = (long)((unsigned long)rectClient.bottom / 2); + + // Try to translate relative window center to absolute coords + if (ClientToScreen(CRawInput::hwndClient, &CRawInput::centerPoint)) + { + // Get absolute coords of game display monitor for TF2 + if (CRawInput::n_sourceEXE == TF2) + { + CRawInput::leftBoundary = (long)GetSystemMetrics(SM_XVIRTUALSCREEN); + CRawInput::rightBoundary = (long)(GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN)); + CRawInput::topBoundary = (long)GetSystemMetrics(SM_YVIRTUALSCREEN); + CRawInput::bottomBoundary = (long)(GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN)); + } + return true; + } } return false; } -unsigned int CRawInput::pollInput() -{ - MSG msg; - - while (GetMessage(&msg, CRawInput::hwndInput, 0, 0) != 0) - DispatchMessage(&msg); - - return msg.message; -} - -LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +LRESULT CALLBACK CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INPUT: { UINT uiSize = 40; - static unsigned char lpb[40]; - RAWINPUT* rwInput; + static BYTE lpb[40]; if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &uiSize, sizeof(RAWINPUTHEADER)) != -1) { - rwInput = (RAWINPUT*)lpb; + RAWINPUT* rwInput = (RAWINPUT*)lpb; if (!rwInput->header.dwType) { - // Avoid collisions with hGet/SetCursorPos EnterCriticalSection(&CRawInput::rawMouseData); - // Accumulate cursor pos change from raw packets + // Accumulate cursor position change CRawInput::x += rwInput->data.mouse.lLastX; CRawInput::y += rwInput->data.mouse.lLastY; @@ -258,86 +251,83 @@ LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPA return 0; } -int __stdcall CRawInput::hSetCursorPos(int x, int y) +bool CRawInput::initInput(WCHAR* pwszError) { - if (!TrmpSetCursorPos(x, y)) - return 1; - - CRawInput::set_x = (long)x; - CRawInput::set_y = (long)y; + POINT defCor; - if (n_sourceEXE != NOBUGFIXES) + // Raw input accumulators initialized to starting cursor position + if (GetCursorPos(&defCor)) { - if (n_sourceEXE == TF2) - { - if (CRawInput::signal >= 1) - ++CRawInput::signal; + CRawInput::set_x = defCor.x; + CRawInput::set_y = defCor.y; + } - // Bug fix for Steam overlay in TF2 backpack - if (consec_EndScene == MAX_CONSEC_ENDSCENE && CRawInput::consecG == MAX_CONSECG && !CRawInput::alttab) - { - if (CRawInput::SCP == 0) - CRawInput::SCP = -1; - } - else - CRawInput::consecG = 0; - } + RAWINPUTDEVICE rMouse; + memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); - ++CRawInput::SCP; + // Flag allows accumulation to continue for TF2 while alt-tabbed + if (CRawInput::n_sourceEXE == TF2) + rMouse.dwFlags = RIDEV_INPUTSINK; + else + rMouse.dwFlags = 0; - // Alt-tab bug fix - if (CRawInput::set_x == 0 && CRawInput::set_y == 0) - CRawInput::alttab = true; + rMouse.hwndTarget = CRawInput::hwndInput; + rMouse.usUsagePage = 0x01; + rMouse.usUsage = 0x02; - // Console and buy menu bug fixes - if (CRawInput::SCP == 1) - { - // Avoid collisions with accumulation of raw input packets - EnterCriticalSection(&CRawInput::rawMouseData); + if (!RegisterRawInputDevices(&rMouse, 1, (UINT)sizeof(RAWINPUTDEVICE))) + { + lstrcpyW(pwszError, L"Failed to register raw input device!"); + return false; + } - CRawInput::set_x -= CRawInput::x; - CRawInput::set_y -= CRawInput::y; + return (CRawInput::bRegistered = true); +} - LeaveCriticalSection(&CRawInput::rawMouseData); - } - else if (CRawInput::SCP == 2) - { - if (n_sourceEXE != TF2) - CRawInput::SCP = 0; +bool CRawInput::hookLibrary(bool bInstall) +{ + if (bInstall) + { + if (!DetourFunctionWithTrampoline((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos) || !DetourFunctionWithTrampoline((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos)) + return false; - consec_EndScene = 0; + // Avoid collisions with accumulation of raw input packets + InitializeCriticalSection(&CRawInput::rawMouseData); - CRawInput::alttab = false; + // Start CS:GO and TF2 D3D9 hooking + if (CRawInput::n_sourceEXE <= 2) + CRawInput::hD3D9HookThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CRawInput::D3D9HookThread, NULL, 0, 0); + } + else + { + DetourRemove((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos); + DetourRemove((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos); - CRawInput::hold_x = CRawInput::set_x; - CRawInput::hold_y = CRawInput::set_y; - } + DeleteCriticalSection(&CRawInput::rawMouseData); } - return 0; + return true; } -int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) +BOOL WINAPI CRawInput::hGetCursorPos(LPPOINT lpPoint) { - // Avoid collisions with accumulation of raw input packets EnterCriticalSection(&CRawInput::rawMouseData); // Split off raw input handling to accumulate independently CRawInput::set_x += CRawInput::x; CRawInput::set_y += CRawInput::y; - CRawInput::x = 0; - CRawInput::y = 0; + CRawInput::x = CRawInput::y = 0; LeaveCriticalSection(&CRawInput::rawMouseData); - if (n_sourceEXE == TF2) + if (CRawInput::n_sourceEXE == TF2) { if (CRawInput::signal >= 1) ++CRawInput::signal; else if (CRawInput::bSubclass) { // TF2 backpack fix not applied when in actual game - RemoveWindowSubclass(CRawInput::hwndClient, CRawInput::SubclassWndProc, 0); + RemoveWindowSubclass(CRawInput::hwndClient, (SUBCLASSPROC)CRawInput::SubclassWndProc, 0); CRawInput::bSubclass = false; } @@ -347,26 +337,26 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) // Bug fix for cursor hitting side of screen in TF2 backpack if (CRawInput::consecG == MAX_CONSECG) { - if (CRawInput::set_x >= CRawInput::hold_x * 2) - CRawInput::set_x = CRawInput::hold_x * 2 - 1; - else if (CRawInput::set_x < 0) - CRawInput::set_x = 0; + if (CRawInput::set_x < CRawInput::leftBoundary) + CRawInput::set_x = CRawInput::leftBoundary; + else if (CRawInput::set_x >= CRawInput::rightBoundary) + CRawInput::set_x = CRawInput::rightBoundary - 1; - if (CRawInput::set_y >= CRawInput::hold_y * 2) - CRawInput::set_y = CRawInput::hold_y * 2 - 1; - else if (CRawInput::set_y < 0) - CRawInput::set_y = 0; + if (CRawInput::set_y < CRawInput::topBoundary) + CRawInput::set_y = CRawInput::topBoundary; + else if (CRawInput::set_y >= CRawInput::bottomBoundary) + CRawInput::set_y = CRawInput::bottomBoundary - 1; // Fix for subtle TF2 backpack bug from alt-tabbing if (!CRawInput::bSubclass) { if (!CRawInput::hwndClient) - CRawInput::hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); + EnumWindows(CRawInput::EnumWindowsProc, (LPARAM)GetCurrentProcessId()); if (CRawInput::hwndClient) { // When in TF2 backpack, monitor its window messages - SetWindowSubclass(CRawInput::hwndClient, CRawInput::SubclassWndProc, 0, 1); + SetWindowSubclass(CRawInput::hwndClient, (SUBCLASSPROC)CRawInput::SubclassWndProc, 0, 1); CRawInput::bSubclass = true; } } @@ -375,20 +365,23 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) if (!CRawInput::alttab) { - if (consec_EndScene == MAX_CONSEC_ENDSCENE) + // Buy and escape menu bug fixes + if (CRawInput::consec_EndScene == MAX_CONSEC_ENDSCENE) { + if (!CRawInput::hwndClient) + EnumWindows(CRawInput::EnumWindowsProc, (LPARAM)GetCurrentProcessId()); + + if (CRawInput::hwndClient && CRawInput::clientCenter()) + { + CRawInput::hold_x = CRawInput::centerPoint.x; + CRawInput::hold_y = CRawInput::centerPoint.y; + } + // Needed to not break backpack in TF2 if (!(CRawInput::SCP != 1 && CRawInput::consecG == MAX_CONSECG)) { - if (!CRawInput::hwndClient) - CRawInput::hwndClient = CRawInput::clientWindow(GetCurrentProcessId()); - - // Buy and escape menu bug fixes - if (CRawInput::hwndClient && CRawInput::clientCenter()) - { - CRawInput::set_x = CRawInput::centerPoint.x; - CRawInput::set_y = CRawInput::centerPoint.y; - } + CRawInput::set_x = CRawInput::hold_x; + CRawInput::set_y = CRawInput::hold_y; } } @@ -403,7 +396,7 @@ int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) } CRawInput::SCP = 0; - return 0; + return FALSE; } LRESULT CALLBACK CRawInput::SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) @@ -417,7 +410,7 @@ LRESULT CALLBACK CRawInput::SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, break; case WM_NCDESTROY: - RemoveWindowSubclass(hWnd, CRawInput::SubclassWndProc, uIdSubclass); + RemoveWindowSubclass(hWnd, (SUBCLASSPROC)CRawInput::SubclassWndProc, uIdSubclass); break; default: @@ -431,9 +424,9 @@ DWORD WINAPI CRawInput::blockInput(LPVOID lpParameter) { BlockInput(TRUE); - // Unblock input after 9 SetCursorPos and/or GetCursorPos calls - for (size_t q = 0; CRawInput::signal < 10; ++q) - Sleep(16); + // Unblock input after 11 SetCursorPos and/or GetCursorPos calls + while (CRawInput::signal <= 12) + Sleep(30); CRawInput::signal = 0; @@ -443,33 +436,172 @@ DWORD WINAPI CRawInput::blockInput(LPVOID lpParameter) return 1; } -bool CRawInput::hookLibrary(bool bInstall) +BOOL WINAPI CRawInput::hSetCursorPos(int x, int y) { - if (bInstall) + if (!TrmpSetCursorPos(x, y)) + return TRUE; + + CRawInput::set_x = (long)x; + CRawInput::set_y = (long)y; + + if (CRawInput::n_sourceEXE != NO_BUG_FIXES) { - if (!DetourFunctionWithTrampoline((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos) || !DetourFunctionWithTrampoline((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos)) - return false; + if (CRawInput::n_sourceEXE == TF2) + { + if (CRawInput::signal >= 1) + ++CRawInput::signal; - InitializeCriticalSection(&CRawInput::rawMouseData); + // Bug fix for Steam overlay in TF2 backpack + if (CRawInput::consec_EndScene == MAX_CONSEC_ENDSCENE && CRawInput::consecG == MAX_CONSECG && !CRawInput::alttab) + { + if (CRawInput::SCP == 0) + --CRawInput::SCP; + } + else + CRawInput::consecG = 0; + } - // Start CS:GO and TF2 D3D9 hooking - if (n_sourceEXE <= 2) - hD3D9HookThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)D3D9HookThread, NULL, 0, 0); + ++CRawInput::SCP; + + // Alt-tab bug fix + if (CRawInput::set_x == 0 && CRawInput::set_y == 0) + CRawInput::alttab = true; + + // Console and buy menu bug fixes + if (CRawInput::SCP == 1) + { + CRawInput::set_x -= CRawInput::x; + CRawInput::set_y -= CRawInput::y; + } + else if (CRawInput::SCP == 2) + { + if (CRawInput::n_sourceEXE != TF2) + CRawInput::SCP = 0; + + CRawInput::consec_EndScene = 0; + + CRawInput::alttab = false; + + if (CRawInput::hold_x != CRawInput::set_x || CRawInput::hold_y != CRawInput::set_y) + { + if (!CRawInput::hwndClient) + EnumWindows(CRawInput::EnumWindowsProc, (LPARAM)GetCurrentProcessId()); + + if (CRawInput::hwndClient && CRawInput::clientCenter()) + { + CRawInput::hold_x = CRawInput::centerPoint.x; + CRawInput::hold_y = CRawInput::centerPoint.y; + } + else + { + CRawInput::hold_x = CRawInput::set_x; + CRawInput::hold_y = CRawInput::set_y; + } + } + } } - else + + return FALSE; +} + +DWORD WINAPI CRawInput::D3D9HookThread(LPVOID lpParameter) +{ + PDWORD vtableBase; + HMODULE hD3D9Dll = NULL; + + while (!hD3D9Dll) { - DetourRemove((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos); - DetourRemove((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos); + hD3D9Dll = GetModuleHandleA("d3d9.dll"); + Sleep(150); + } + + if (vtableBase = CRawInput::vtableFind((DWORD)hD3D9Dll)) + { + // D3D9 device vtable found + CRawInput::oD3D9EndScene = vtableBase[42] + 5; + CRawInput::JMPplace((PBYTE)vtableBase[42], (DWORD)CRawInput::D3D9EndScene, 5); + } - DeleteCriticalSection(&CRawInput::rawMouseData); + CloseHandle(CRawInput::hD3D9HookThread); + return 1; +} + +PDWORD CRawInput::vtableFind(DWORD D3D9Base) +{ + PDWORD vtabl; + + for (DWORD g = 0; g < 0x1c3000; ++g) + { + ++D3D9Base; + + // Alternative to signature search with FindPattern + if ((*(WORD*)(D3D9Base + 0x00)) == 0x06C7 && (*(WORD*)(D3D9Base + 0x06)) == 0x8689 && (*(WORD*)(D3D9Base + 0x0C)) == 0x8689) + { + D3D9Base += 2; + break; + } + else if (g >= 0x1c3000) + return 0; } - return true; + *(DWORD*)&vtabl = *(DWORD*)D3D9Base; + return vtabl; +} + +// Midhook avoids access violations if D3D9 is hooked by another program +void CRawInput::JMPplace(PBYTE inFunc, DWORD deFunc, DWORD len) +{ + DWORD oldPro = 0; + VirtualProtect((LPVOID)inFunc, (SIZE_T)len, PAGE_EXECUTE_READWRITE, &oldPro); + DWORD relAddress = 0; + relAddress = (deFunc - (DWORD)inFunc) - 5; + *inFunc = 0xE9; + *((DWORD *)(inFunc + 0x1)) = relAddress; + + for (DWORD x = 0x5; x < len; ++x) + *(inFunc + x) = 0x90; + + DWORD newPro = 0; + VirtualProtect((LPVOID)inFunc, (SIZE_T)len, oldPro, &newPro); +} + +__declspec(naked) HRESULT WINAPI CRawInput::D3D9EndScene() +{ + static LPDIRECT3DDEVICE9 pDevice; + + __asm + { + mov edi, edi + push ebp + mov ebp, esp + mov eax, dword ptr ss:[ebp + 0x8] + mov pDevice, eax + pushad + } + + if (pDevice && CRawInput::consec_EndScene < MAX_CONSEC_ENDSCENE) + ++CRawInput::consec_EndScene; + + __asm + { + popad + jmp[CRawInput::oD3D9EndScene] + } +} + +UINT CRawInput::pollInput() +{ + MSG msg; + + while (GetMessage(&msg, CRawInput::hwndInput, 0, 0) != 0) + DispatchMessage(&msg); + + return msg.message; } void CRawInput::unload() { - if (bRegistered && CRawInput::hwndInput) + if (CRawInput::bRegistered && CRawInput::hwndInput) { RAWINPUTDEVICE rMouse; memset(&rMouse, 0, sizeof(RAWINPUTDEVICE)); @@ -477,8 +609,8 @@ void CRawInput::unload() rMouse.hwndTarget = NULL; rMouse.usUsagePage = 0x01; rMouse.usUsage = 0x02; - RegisterRawInputDevices(&rMouse, 1, sizeof(RAWINPUTDEVICE)); + RegisterRawInputDevices(&rMouse, 1, (UINT)sizeof(RAWINPUTDEVICE)); - DestroyWindow(hwndInput); + DestroyWindow(CRawInput::hwndInput); } } \ No newline at end of file diff --git a/stdafx.cpp b/stdafx.cpp deleted file mode 100644 index 326dfa2..0000000 --- a/stdafx.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "stdafx.h" - -int consec_EndScene = 0; -HANDLE hD3D9HookThread = NULL; -int n_sourceEXE = 0; \ No newline at end of file diff --git a/stdafx.h b/stdafx.h deleted file mode 100644 index 07416ad..0000000 --- a/stdafx.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#define STRICT -#define WINVER 0x0501 -#define _WIN32_WINDOWS 0x0501 -#define _WIN32_WINNT 0x0501 -#define WIN32_LEAN_AND_MEAN -#define KERNEL_LIB L"kernel32.dll" -#define RINPUTVER "v1.43" -#define RINPUTFVER 1,43 -#define TF2 2 -#define NOBUGFIXES 4 -#define MAX_CONSEC_ENDSCENE 7 -#define MAX_CONSECG 3 - -#include - -extern int consec_EndScene; -extern HANDLE hD3D9HookThread; -extern int n_sourceEXE; - -DWORD WINAPI D3D9HookThread(LPVOID lpParameter); - -class CRawInput -{ -public: - // Initialize RInput components, register for raw input - static bool initialize(WCHAR* pwszError); - static bool initWindow(WCHAR* pwszError); - static bool initInput(WCHAR* pwszError); - // Identify main visible window of the injected process - static BOOL CALLBACK EnumWindowsProc(HWND WindowHandle, LPARAM lParam); - static HWND clientWindow(unsigned long PID); - // Get coords of the injected process' window center - static bool clientCenter(); - // Poll mouse input - static unsigned int pollInput(); - // Mouse input window proc - static LRESULT __stdcall wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - // Hooked cursor functions handling - static int __stdcall hSetCursorPos(int x, int y); - static int __stdcall hGetCursorPos(LPPOINT lpPoint); - // TF2 subclassing and input blocking when alt-tabbing back in - static LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); - static DWORD WINAPI blockInput(LPVOID lpParameter); - // Enables or disables hooking and mouse critical section - static bool hookLibrary(bool bInstall); - // Unload RInput components, stop raw input reads from mouse - static void unload(); - -private: - static HWND hwndClient; - static bool TF2unblock; - static long hold_x; - static long hold_y; - static POINT centerPoint; - static HWND hwndInput; - static long set_x; - static long set_y; - static bool bRegistered; - static CRITICAL_SECTION rawMouseData; - static long x; - static long y; - static int signal; - static int consecG; - static bool alttab; - static int SCP; - static bool bSubclass; - static HANDLE hCreateThread; -}; \ No newline at end of file diff --git a/versioninfo.rc b/versioninfo.rc index 7085c1f..c2326ba 100644 --- a/versioninfo.rc +++ b/versioninfo.rc @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "versioninfo.h" 1 VERSIONINFO FILEVERSION RINPUTFVER From c8c076015e5938b139bba2af0c82af714d1eb670 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Tue, 5 Jan 2016 13:52:19 -0500 Subject: [PATCH 55/57] Forgot to add new files to commit --- main.h | 17 +++++++++++ rawinput.h | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ versioninfo.h | 7 +++++ 3 files changed, 102 insertions(+) create mode 100644 main.h create mode 100644 rawinput.h create mode 100644 versioninfo.h diff --git a/main.h b/main.h new file mode 100644 index 0000000..e08c73c --- /dev/null +++ b/main.h @@ -0,0 +1,17 @@ +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#define STRICT +#define WINVER 0x0501 +#define _WIN32_WINDOWS 0x0501 +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN +#define KERNEL_LIB L"kernel32.dll" + +#include "rawinput.h" + +extern "C" __declspec(dllexport) void entryPoint(); + +void displayError(WCHAR* pwszError); + +#endif \ No newline at end of file diff --git a/rawinput.h b/rawinput.h new file mode 100644 index 0000000..be64432 --- /dev/null +++ b/rawinput.h @@ -0,0 +1,78 @@ +#ifndef RAWINPUT_H_ +#define RAWINPUT_H_ + +#define TF2 2 +#define NO_BUG_FIXES 4 +#define MAX_CONSEC_ENDSCENE 7 +#define MAX_CONSECG 3 + +#include +#include "detours.h" +#include +#include +#include +#include + +extern void displayError(WCHAR* pwszError); + +class CRawInput +{ +public: + // Initialize RInput components, register for raw input + static bool initialize(WCHAR* pwszError); + static bool initWindow(WCHAR* pwszError); + static bool initInput(WCHAR* pwszError); + // Mouse input window message processing + static LRESULT CALLBACK wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + // Enables or disables hooking and mouse critical section + static bool hookLibrary(bool bInstall); + // Hooked cursor functions handling + static BOOL WINAPI hGetCursorPos(LPPOINT lpPoint); + static BOOL WINAPI hSetCursorPos(int x, int y); + // Poll mouse input + static UINT pollInput(); + // Unload RInput components, stop raw input reads from mouse + static void unload(); + +private: + static unsigned char n_sourceEXE; + static HWND hwndClient; + static bool TF2unblock; + static POINT centerPoint; + static long leftBoundary; + static long rightBoundary; + static long topBoundary; + static long bottomBoundary; + static long hold_x; + static long hold_y; + static CRITICAL_SECTION rawMouseData; + static long x; + static long y; + static HWND hwndInput; + static long set_x; + static long set_y; + static bool bRegistered; + static unsigned char signal; + static bool bSubclass; + static HANDLE hCreateThread; + static unsigned char consecG; + static bool alttab; + static unsigned char consec_EndScene; + static char SCP; + static HANDLE hD3D9HookThread; + static DWORD oD3D9EndScene; + // Identify main visible window of the injected process + static BOOL CALLBACK EnumWindowsProc(HWND WindowHandle, LPARAM lParam); + // Get coords of the injected process' window center + static bool clientCenter(); + // TF2 subclassing and input blocking when alt-tabbing back in + static LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); + static DWORD WINAPI blockInput(LPVOID lpParameter); + // D3D9 hooking for CS:GO and TF2 + static DWORD WINAPI D3D9HookThread(LPVOID lpParameter); + static PDWORD vtableFind(DWORD D3D9Base); + static void JMPplace(BYTE* inFunc, DWORD deFunc, DWORD len); + static HRESULT WINAPI D3D9EndScene(); +}; + +#endif \ No newline at end of file diff --git a/versioninfo.h b/versioninfo.h new file mode 100644 index 0000000..bf1a19f --- /dev/null +++ b/versioninfo.h @@ -0,0 +1,7 @@ +#ifndef _VERSIONINFO_H_ +#define _VERSIONINFO_H_ + +#define RINPUTVER "v1.43" +#define RINPUTFVER 1,43 + +#endif From d36ccf7ab1a6a2e4655ba45098b33e025dcfb557 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Wed, 17 Feb 2016 03:33:24 -0500 Subject: [PATCH 56/57] Minor change to make sure critical section is created first and a minor bug fix where input would stay blocked in TF2 when alt-tabbing into the start of an MVM match --- README | 2 +- README.mdown | 2 +- RInput.vcproj | 4 ++-- main.cpp | 2 +- rawinput.cpp | 15 ++++++++++++--- versioninfo.h | 4 ++-- 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/README b/README index 82784ad..b04ed54 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -RInput Library v1.43 by Vols and Jezuz +RInput Library v1.44 by Vols and Jezuz -------------------------------------- RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for x86 (32-bit) games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. diff --git a/README.mdown b/README.mdown index a18953c..30ad282 100644 --- a/README.mdown +++ b/README.mdown @@ -1,4 +1,4 @@ -# RInput Library v1.43 by Vols and Jezuz +# RInput Library v1.44 by Vols and Jezuz RInput is an alternative to in-game raw input, which some users perceive as having input lag or other shortcomings. It also allows for raw input in games which do not have a native raw input implementation. In either case, RInput will only work for x86 (32-bit) games that control aim using the Win32 API functions GetCursorPos and SetCursorPos. ## Requirements diff --git a/RInput.vcproj b/RInput.vcproj index e4dd7b5..678b91d 100644 --- a/RInput.vcproj +++ b/RInput.vcproj @@ -80,7 +80,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="detours.lib dxguid.lib shlwapi.lib comctl32.lib" - Version="1.43-debug" + Version="1.44-debug" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" EnableUAC="true" @@ -190,7 +190,7 @@ Name="VCLinkerTool" UseUnicodeResponseFiles="false" AdditionalDependencies="detours.lib dxguid.lib shlwapi.lib comctl32.lib" - Version="1.43" + Version="1.44" LinkIncremental="1" AdditionalLibraryDirectories=""C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib";"C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86"" GenerateManifest="true" diff --git a/main.cpp b/main.cpp index e92c769..e4196d6 100644 --- a/main.cpp +++ b/main.cpp @@ -108,6 +108,6 @@ void unloadLibrary() void displayError(WCHAR* pwszError) { MessageBoxW(NULL, pwszError, L"Raw Input error!", MB_ICONERROR | MB_OK); - CRawInput::hookLibrary(FALSE); + CRawInput::hookLibrary(false); unloadLibrary(); } \ No newline at end of file diff --git a/rawinput.cpp b/rawinput.cpp index 145b2c1..f620bf3 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -104,6 +104,9 @@ bool CRawInput::initialize(WCHAR* pwszError) else CRawInput::n_sourceEXE = NO_BUG_FIXES; + // Avoid collisions with accumulation of raw input packets + InitializeCriticalSection(&CRawInput::rawMouseData); + if (!CRawInput::initWindow(pwszError)) return false; @@ -291,9 +294,6 @@ bool CRawInput::hookLibrary(bool bInstall) if (!DetourFunctionWithTrampoline((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos) || !DetourFunctionWithTrampoline((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos)) return false; - // Avoid collisions with accumulation of raw input packets - InitializeCriticalSection(&CRawInput::rawMouseData); - // Start CS:GO and TF2 D3D9 hooking if (CRawInput::n_sourceEXE <= 2) CRawInput::hD3D9HookThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CRawInput::D3D9HookThread, NULL, 0, 0); @@ -424,9 +424,18 @@ DWORD WINAPI CRawInput::blockInput(LPVOID lpParameter) { BlockInput(TRUE); + long SleepTimer = 0; + // Unblock input after 11 SetCursorPos and/or GetCursorPos calls while (CRawInput::signal <= 12) + { + SleepTimer += 30; + + if (SleepTimer >= 2000) + break; + Sleep(30); + } CRawInput::signal = 0; diff --git a/versioninfo.h b/versioninfo.h index bf1a19f..e31bb38 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,7 +1,7 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.43" -#define RINPUTFVER 1,43 +#define RINPUTVER "v1.44" +#define RINPUTFVER 1,44 #endif From 7a5f25dcffd2a1200b4dfd23fc533e3e7d2589f1 Mon Sep 17 00:00:00 2001 From: VolsandJezuz Date: Sat, 20 Feb 2016 20:50:20 -0500 Subject: [PATCH 57/57] Made EndScene hooks work for all versions of Windows from XP to 10 and cleaned up several sections of messy code --- rawinput.cpp | 161 +++++++++++++++++++++++++++------------------------ rawinput.h | 3 +- 2 files changed, 85 insertions(+), 79 deletions(-) diff --git a/rawinput.cpp b/rawinput.cpp index f620bf3..ac6a65c 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -37,7 +37,7 @@ extern "C" DETOUR_TRAMPOLINE(BOOL WINAPI TrmpGetCursorPos(LPPOINT lpPoint), GetCursorPos); extern "C" DETOUR_TRAMPOLINE(BOOL WINAPI TrmpSetCursorPos(int x, int y), SetCursorPos); -unsigned char CRawInput::n_sourceEXE = 0; +unsigned char CRawInput::n_sourceEXE = 1; HWND CRawInput::hwndClient = NULL; bool CRawInput::TF2unblock = false; POINT CRawInput::centerPoint; @@ -66,38 +66,33 @@ DWORD CRawInput::oD3D9EndScene = 0; bool CRawInput::initialize(WCHAR* pwszError) { - char szEXEPath[MAX_PATH]; + wchar_t szEXEPath[MAX_PATH]; // Detect source games for enabling specific bug fixes - if (GetModuleFileNameA(NULL, (LPCH)szEXEPath, (DWORD)sizeof(szEXEPath))) + if (GetModuleFileNameW(NULL, szEXEPath, sizeof(szEXEPath))) { - char TF2path[sizeof(szEXEPath)]; - strcpy_s(TF2path, sizeof(TF2path), (const char*)szEXEPath); - PathStripPathA((LPSTR)szEXEPath); - char *source_exes[] = {"csgo.exe", "hl2.exe", "portal2.exe", NULL}; - + wchar_t TF2path[sizeof(szEXEPath)]; + wcscpy_s(TF2path, sizeof(TF2path), szEXEPath); + PathStripPathW(szEXEPath); + wchar_t *source_exes[] = {L"csgo.exe", L"hl2.exe", L"portal2.exe", NULL}; + // Bug fixes now limited to tested source games - for (++CRawInput::n_sourceEXE; source_exes[CRawInput::n_sourceEXE - 1] != NULL; ++CRawInput::n_sourceEXE) + while (source_exes[CRawInput::n_sourceEXE - 1] != NULL && wcscmp(szEXEPath, source_exes[CRawInput::n_sourceEXE - 1])) + ++CRawInput::n_sourceEXE; + if (CRawInput::n_sourceEXE == TF2) { - if ((std::string)szEXEPath == (std::string)source_exes[CRawInput::n_sourceEXE - 1]) + PathRemoveFileSpecW(TF2path); + wchar_t tf2[16] = L"Team Fortress 2"; + + // Make sure hl2.exe is TF2 + for (size_t k = 0; k < 15; ++k) { - if (CRawInput::n_sourceEXE == TF2) + if (TF2path[((std::wstring)TF2path).size() + k - 15] != tf2[k]) { - PathRemoveFileSpecA((LPSTR)TF2path); - char tf2[16] = "Team Fortress 2"; - - // Make sure hl2.exe is TF2 - for (size_t k = 0; k < 15; ++k) - { - if (TF2path[((std::string)TF2path).size() + k - 15] != tf2[k]) - { - ++CRawInput::n_sourceEXE; - break; - } - } + // Treat other hl2 games like portal and exit loop + ++CRawInput::n_sourceEXE; + k += 15 - k; } - - break; } } } @@ -178,7 +173,7 @@ BOOL CALLBACK CRawInput::EnumWindowsProc(HWND WindowHandle, LPARAM lParam) unsigned long PID = 0; GetWindowThreadProcessId(WindowHandle, &PID); - if (PID != (unsigned long)lParam || GetWindow(WindowHandle, GW_OWNER) || !IsWindowVisible(WindowHandle)) + if (PID != (unsigned long)lParam || (GetWindow(WindowHandle, GW_OWNER) && !IsWindowVisible(WindowHandle))) return TRUE; // Found visible, main program window matching current process ID @@ -230,12 +225,10 @@ LRESULT CALLBACK CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPAR if (!rwInput->header.dwType) { - EnterCriticalSection(&CRawInput::rawMouseData); - // Accumulate cursor position change + EnterCriticalSection(&CRawInput::rawMouseData); CRawInput::x += rwInput->data.mouse.lLastX; CRawInput::y += rwInput->data.mouse.lLastY; - LeaveCriticalSection(&CRawInput::rawMouseData); } } @@ -278,7 +271,7 @@ bool CRawInput::initInput(WCHAR* pwszError) rMouse.usUsagePage = 0x01; rMouse.usUsage = 0x02; - if (!RegisterRawInputDevices(&rMouse, 1, (UINT)sizeof(RAWINPUTDEVICE))) + if (!RegisterRawInputDevices(&rMouse, 1, sizeof(RAWINPUTDEVICE))) { lstrcpyW(pwszError, L"Failed to register raw input device!"); return false; @@ -302,8 +295,10 @@ bool CRawInput::hookLibrary(bool bInstall) { DetourRemove((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos); DetourRemove((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos); - DeleteCriticalSection(&CRawInput::rawMouseData); + + if (CRawInput::hD3D9HookThread) + CloseHandle(CRawInput::hD3D9HookThread); } return true; @@ -311,13 +306,11 @@ bool CRawInput::hookLibrary(bool bInstall) BOOL WINAPI CRawInput::hGetCursorPos(LPPOINT lpPoint) { - EnterCriticalSection(&CRawInput::rawMouseData); - // Split off raw input handling to accumulate independently + EnterCriticalSection(&CRawInput::rawMouseData); CRawInput::set_x += CRawInput::x; CRawInput::set_y += CRawInput::y; CRawInput::x = CRawInput::y = 0; - LeaveCriticalSection(&CRawInput::rawMouseData); if (CRawInput::n_sourceEXE == TF2) @@ -423,24 +416,17 @@ LRESULT CALLBACK CRawInput::SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, DWORD WINAPI CRawInput::blockInput(LPVOID lpParameter) { BlockInput(TRUE); + unsigned char SleepTimer = 0; - long SleepTimer = 0; - - // Unblock input after 11 SetCursorPos and/or GetCursorPos calls - while (CRawInput::signal <= 12) + // Unblock input after 11 SetCursorPos/GetCursorPos or 2 seconds + while (CRawInput::signal <= 12 && SleepTimer < 67) { - SleepTimer += 30; - - if (SleepTimer >= 2000) - break; - + ++SleepTimer; Sleep(30); } CRawInput::signal = 0; - - BlockInput(FALSE); - + BlockInput(FALSE); CloseHandle(CRawInput::hCreateThread); return 1; } @@ -461,11 +447,8 @@ BOOL WINAPI CRawInput::hSetCursorPos(int x, int y) ++CRawInput::signal; // Bug fix for Steam overlay in TF2 backpack - if (CRawInput::consec_EndScene == MAX_CONSEC_ENDSCENE && CRawInput::consecG == MAX_CONSECG && !CRawInput::alttab) - { - if (CRawInput::SCP == 0) - --CRawInput::SCP; - } + if (CRawInput::consec_EndScene == MAX_CONSEC_ENDSCENE && CRawInput::consecG == MAX_CONSECG && !CRawInput::alttab && CRawInput::SCP == 0) + --CRawInput::SCP; else CRawInput::consecG = 0; } @@ -515,7 +498,6 @@ BOOL WINAPI CRawInput::hSetCursorPos(int x, int y) DWORD WINAPI CRawInput::D3D9HookThread(LPVOID lpParameter) { - PDWORD vtableBase; HMODULE hD3D9Dll = NULL; while (!hD3D9Dll) @@ -523,38 +505,64 @@ DWORD WINAPI CRawInput::D3D9HookThread(LPVOID lpParameter) hD3D9Dll = GetModuleHandleA("d3d9.dll"); Sleep(150); } - - if (vtableBase = CRawInput::vtableFind((DWORD)hD3D9Dll)) - { - // D3D9 device vtable found - CRawInput::oD3D9EndScene = vtableBase[42] + 5; - CRawInput::JMPplace((PBYTE)vtableBase[42], (DWORD)CRawInput::D3D9EndScene, 5); - } - CloseHandle(CRawInput::hD3D9HookThread); - return 1; -} + DWORD D3D9Base = (DWORD)hD3D9Dll; + DWORD oldEndScene = 0; + DWORD dwVersion = GetVersion(); + double fCompareVersion = LOBYTE(LOWORD(dwVersion)) + 0.1 * HIBYTE(LOWORD(dwVersion)); -PDWORD CRawInput::vtableFind(DWORD D3D9Base) -{ - PDWORD vtabl; + // Windows versions have different EndScene memory patterns + if (fCompareVersion >= 6.2) + { + // Windows 8, Windows 8.1, and Windows 10 + for (DWORD g = 0; g < 0x128000; ++g) + { + ++D3D9Base; - for (DWORD g = 0; g < 0x1c3000; ++g) + if ((*(WORD*)(D3D9Base + 0x00)) == 0xFF8B && (*(WORD*)(D3D9Base + 0x02)) == 0x8B55 && (*(WORD*)(D3D9Base + 0x04)) == 0xFFEC && (*(WORD*)(D3D9Base + 0x06)) == 0x0875 && (*(WORD*)(D3D9Base + 0x08)) == 0x018B && (*(WORD*)(D3D9Base + 0x0A)) == 0x3E6A && (*(WORD*)(D3D9Base + 0x0C)) == 0x90FF) + { + oldEndScene = D3D9Base; + break; + } + } + } + else if (fCompareVersion >= 6.0) { - ++D3D9Base; + // Windows Vista and Windows 7 + for (DWORD g = 0; g < 0x128000; ++g) + { + ++D3D9Base; - // Alternative to signature search with FindPattern - if ((*(WORD*)(D3D9Base + 0x00)) == 0x06C7 && (*(WORD*)(D3D9Base + 0x06)) == 0x8689 && (*(WORD*)(D3D9Base + 0x0C)) == 0x8689) + if ((*(WORD*)(D3D9Base + 0x00)) == 0xFF8B && (*(WORD*)(D3D9Base + 0x02)) == 0x8B55 && (*(WORD*)(D3D9Base + 0x04)) == 0x8BEC && (*(WORD*)(D3D9Base + 0x06)) == 0x0855 && (*(WORD*)(D3D9Base + 0x08)) == 0x018B && (*(WORD*)(D3D9Base + 0x0A)) == 0x808B && (*(WORD*)(D3D9Base + 0x0C)) == 0x00F4 && (*(WORD*)(D3D9Base + 0x0E)) == 0x0000 && (*(WORD*)(D3D9Base + 0x10)) == 0x6A52 && (*(WORD*)(D3D9Base + 0x12)) == 0xFF3E && (*(WORD*)(D3D9Base + 0x14)) == 0x5DD0 && (*(WORD*)(D3D9Base + 0x16)) == 0x04C2) + { + oldEndScene = D3D9Base; + break; + } + } + } + else if (fCompareVersion >= 5.1) + { + // Windows XP + for (DWORD g = 0; g < 0x128000; ++g) { - D3D9Base += 2; - break; + ++D3D9Base; + + if ((*(WORD*)(D3D9Base + 0x00)) == 0xFF8B && (*(WORD*)(D3D9Base + 0x02)) == 0x8B55 && (*(WORD*)(D3D9Base + 0x04)) == 0x8BEC && (*(WORD*)(D3D9Base + 0x06)) == 0x0855 && (*(WORD*)(D3D9Base + 0x08)) == 0x018B && (*(WORD*)(D3D9Base + 0x0A)) == 0x6A52) + { + oldEndScene = D3D9Base; + break; + } } - else if (g >= 0x1c3000) - return 0; + } + + if (oldEndScene) + { + CRawInput::oD3D9EndScene = oldEndScene + 5; + CRawInput::JMPplace((PBYTE)oldEndScene, (DWORD)CRawInput::D3D9EndScene, 5); } - *(DWORD*)&vtabl = *(DWORD*)D3D9Base; - return vtabl; + CloseHandle(CRawInput::hD3D9HookThread); + return 1; } // Midhook avoids access violations if D3D9 is hooked by another program @@ -618,8 +626,7 @@ void CRawInput::unload() rMouse.hwndTarget = NULL; rMouse.usUsagePage = 0x01; rMouse.usUsage = 0x02; - RegisterRawInputDevices(&rMouse, 1, (UINT)sizeof(RAWINPUTDEVICE)); - + RegisterRawInputDevices(&rMouse, 1, sizeof(RAWINPUTDEVICE)); DestroyWindow(CRawInput::hwndInput); } } \ No newline at end of file diff --git a/rawinput.h b/rawinput.h index be64432..35e4105 100644 --- a/rawinput.h +++ b/rawinput.h @@ -70,8 +70,7 @@ class CRawInput static DWORD WINAPI blockInput(LPVOID lpParameter); // D3D9 hooking for CS:GO and TF2 static DWORD WINAPI D3D9HookThread(LPVOID lpParameter); - static PDWORD vtableFind(DWORD D3D9Base); - static void JMPplace(BYTE* inFunc, DWORD deFunc, DWORD len); + static void JMPplace(PBYTE inFunc, DWORD deFunc, DWORD len); static HRESULT WINAPI D3D9EndScene(); };