diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..863e3a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +RInput.ncb +RInput.sln +RInput.suo +RInput.vcproj.* +Release/ +versioninfo.aps +.gitconfig +.gitignore diff --git a/README b/README index 0a6d10e..b04ed54 100644 --- a/README +++ b/README @@ -1,24 +1,38 @@ -RInput Library v1.31 ----------------------------------- -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.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. -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. +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) -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. +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: ----------------------------------- -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 - -How it works: ----------------------------------- -As most of the developers are not into Win32 API these days, I'll just sum up how the process works: +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 +- 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 +- 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, 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 +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 @@ -26,7 +40,9 @@ 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 -Credits: ----------------------------------- -Dr3am - for the icons -Microsoft - for preventing me from writing my own detour/hooking stub \ No newline at end of file +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 +- abort - original author +- Dr3am - for the icons \ No newline at end of file diff --git a/README.mdown b/README.mdown index 3d52141..30ad282 100644 --- a/README.mdown +++ b/README.mdown @@ -1,20 +1,33 @@ -# RInput Library v1.31 -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.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. -## 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. +## 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) -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. +## 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 -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 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 +- 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 +- 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, 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 ## 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 3. The injector calls the "entryPoint" function remotely @@ -22,13 +35,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 +## 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 +- abort - original author +- Dr3am - for the icons \ No newline at end of file diff --git a/RInput.vcproj b/RInput.vcproj new file mode 100644 index 0000000..678b91d --- /dev/null +++ b/RInput.vcproj @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/detours.h b/detours.h new file mode 100644 index 0000000..53b1f64 --- /dev/null +++ b/detours.h @@ -0,0 +1,613 @@ +////////////////////////////////////////////////////////////////////////////// +// +// File: detours.h +// Module: detours.lib +// +// Detours for binary functions. Version 1.5 (Build 46) +// +// Copyright 1995-2001, Microsoft Corporation +// + +#ifndef _DETOURS_H_ +#define _DETOURS_H_ + +////////////////////////////////////////////////////////////////////////////// +// +#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. \ No newline at end of file diff --git a/detours.lib b/detours.lib new file mode 100644 index 0000000..56c6a8d Binary files /dev/null and b/detours.lib differ diff --git a/icon.ico b/icon.ico deleted file mode 100644 index da4dece..0000000 Binary files a/icon.ico and /dev/null differ diff --git a/main.cpp b/main.cpp index f0a91db..e4196d6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,65 +1,76 @@ /* - 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) 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" -int __stdcall DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) -{ - UNREFERENCED_PARAMETER(lpReserved); +static HINSTANCE g_hInstance = NULL; +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; - g_hInstance = hInstance; - -#ifdef _DEBUG - OutputDebugString("Loaded RInput"); -#endif + if (!DisableThreadLibraryCalls(hInstance)) + return FALSE; + g_hInstance = hInstance; break; + case DLL_PROCESS_DETACH: - CRawInput::hookLibrary(false); - CRawInput::unload(); -#ifdef _DEBUG - OutputDebugString("Unloaded RInput"); -#endif + // Unhook cursor functions and unload from injected process + CRawInput::hookLibrary(false); + CRawInput::unload(); break; } - return 1; + return TRUE; +} + +// Validate that we are working with at least Windows XP +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() { -#ifdef _DEBUG - OutputDebugString("entryPoint called"); -#endif + HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "RInputEvent32"); - HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENTNAME); 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."); @@ -78,30 +89,12 @@ 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 Windows XP or higher (required for raw input) -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() { - __asm { + __asm + { push -2 push 0 push g_hInstance @@ -110,4 +103,11 @@ 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(); } \ No newline at end of file diff --git a/main.h b/main.h index 3d2a617..e08c73c 100644 --- a/main.h +++ b/main.h @@ -2,25 +2,16 @@ #define _MAIN_H_ #define STRICT +#define WINVER 0x0501 +#define _WIN32_WINDOWS 0x0501 +#define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN - -#define WINVER 0x0501 // XP atleast -#define _WIN32_WINNT 0x0501 // XP atleast - -#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" -HINSTANCE g_hInstance = NULL; - -// only handle the hooking / dll functions here extern "C" __declspec(dllexport) void entryPoint(); -inline bool validateVersion(); -void unloadLibrary(); + void displayError(WCHAR* pwszError); #endif \ No newline at end of file diff --git a/rawinput.cpp b/rawinput.cpp index 8fbad74..ac6a65c 100644 --- a/rawinput.cpp +++ b/rawinput.cpp @@ -1,90 +1,272 @@ /* - 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) 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" -// 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); - -// Initialize static variables -bool CRawInput::bRegistered = false; -HWND CRawInput::hwndInput = NULL; +#pragma intrinsic(memset) + +// 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 = 1; +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; +CRITICAL_SECTION CRawInput::rawMouseData; long CRawInput::x = 0; long CRawInput::y = 0; - -bool CRawInput::initialize(WCHAR* pwszError) +HWND CRawInput::hwndInput = NULL; +long CRawInput::set_x = 0; +long CRawInput::set_y = 0; +bool CRawInput::bRegistered = false; +unsigned char CRawInput::signal = 0; +bool CRawInput::bSubclass = false; +HANDLE CRawInput::hCreateThread = NULL; +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)) + wchar_t szEXEPath[MAX_PATH]; + + // Detect source games for enabling specific bug fixes + if (GetModuleFileNameW(NULL, szEXEPath, sizeof(szEXEPath))) + { + 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 + while (source_exes[CRawInput::n_sourceEXE - 1] != NULL && wcscmp(szEXEPath, source_exes[CRawInput::n_sourceEXE - 1])) + ++CRawInput::n_sourceEXE; + if (CRawInput::n_sourceEXE == TF2) + { + 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 (TF2path[((std::wstring)TF2path).size() + k - 15] != tf2[k]) + { + // Treat other hl2 games like portal and exit loop + ++CRawInput::n_sourceEXE; + k += 15 - k; + } + } + } + } + else + CRawInput::n_sourceEXE = NO_BUG_FIXES; + + // Avoid collisions with accumulation of raw input packets + InitializeCriticalSection(&CRawInput::rawMouseData); + + if (!CRawInput::initWindow(pwszError)) return false; - if (!initInput(pwszError)) + if (!CRawInput::initInput(pwszError)) return false; return true; } -bool CRawInput::initWindow(WCHAR* pwszError) +bool CRawInput::initWindow(WCHAR* pwszError) { + // Identify the window that matches the injected process + EnumWindows(CRawInput::EnumWindowsProc, (LPARAM)GetCurrentProcessId()); + + if (CRawInput::hwndClient) + { + // TF2 Window must be active for backpack fixes to work + if (CRawInput::n_sourceEXE == TF2 && GetForegroundWindow() != CRawInput::hwndClient) + { + CRawInput::TF2unblock = true; + BlockInput(TRUE); + ShowWindow(CRawInput::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)); - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.lpfnWndProc = (WNDPROC)wpInput; - wcex.lpszClassName = INPUTWINDOW; + wcex.lpfnWndProc = (WNDPROC)CRawInput::wpInput; + wcex.lpszClassName = "RInput"; if (!RegisterClassEx(&wcex)) { 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); + CRawInput::hwndInput = CreateWindowEx(NULL, "RInput", "RInput", 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); - + UnregisterClass("RInput", NULL); return true; } -bool CRawInput::initInput(WCHAR* pwszError) +BOOL CALLBACK CRawInput::EnumWindowsProc(HWND WindowHandle, LPARAM lParam) { - // Set default coordinates - CRawInput::x = CRawInput::y = 0; + unsigned long PID = 0; + GetWindowThreadProcessId(WindowHandle, &PID); + + if (PID != (unsigned long)lParam || (GetWindow(WindowHandle, GW_OWNER) && !IsWindowVisible(WindowHandle))) + return TRUE; + + // Found visible, main program window matching current process ID + CRawInput::hwndClient = WindowHandle; + return FALSE; +} + +bool CRawInput::clientCenter() +{ + RECT rectClient; + + // Try to get relative window center + if (GetClientRect(CRawInput::hwndClient, &rectClient)) + { + 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; +} + +LRESULT CALLBACK CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INPUT: + { + UINT uiSize = 40; + static BYTE lpb[40]; + + if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &uiSize, sizeof(RAWINPUTHEADER)) != -1) + { + RAWINPUT* rwInput = (RAWINPUT*)lpb; + + if (!rwInput->header.dwType) + { + // Accumulate cursor position change + EnterCriticalSection(&CRawInput::rawMouseData); + CRawInput::x += rwInput->data.mouse.lLastX; + CRawInput::y += rwInput->data.mouse.lLastY; + LeaveCriticalSection(&CRawInput::rawMouseData); + } + } + + break; + } + + case WM_DESTROY: + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return 0; +} + +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)); - rMouse.dwFlags = 0; + // Flag allows accumulation to continue for TF2 while alt-tabbed + if (CRawInput::n_sourceEXE == TF2) + rMouse.dwFlags = RIDEV_INPUTSINK; + else + rMouse.dwFlags = 0; + rMouse.hwndTarget = CRawInput::hwndInput; rMouse.usUsagePage = 0x01; rMouse.usUsage = 0x02; @@ -95,126 +277,356 @@ bool CRawInput::initInput(WCHAR* pwszError) return false; } - return (bRegistered = true); + return (CRawInput::bRegistered = true); } -unsigned int CRawInput::pollInput() +bool CRawInput::hookLibrary(bool bInstall) { - MSG msg; + if (bInstall) + { + if (!DetourFunctionWithTrampoline((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos) || !DetourFunctionWithTrampoline((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos)) + return false; - while (GetMessage(&msg, CRawInput::hwndInput, 0, 0) != 0) - DispatchMessage(&msg); + // 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); + DeleteCriticalSection(&CRawInput::rawMouseData); - return msg.message; + if (CRawInput::hD3D9HookThread) + CloseHandle(CRawInput::hD3D9HookThread); + } + + return true; } -LRESULT __stdcall CRawInput::wpInput(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +BOOL WINAPI CRawInput::hGetCursorPos(LPPOINT lpPoint) { - switch (message) + // 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) { - case WM_INPUT:{ -#ifdef _DEBUG - OutputDebugString("WM_INPUT event"); -#endif - UINT uiSize = RAWPTRSIZE; - static unsigned char lpb[RAWPTRSIZE]; - RAWINPUT* rwInput; + if (CRawInput::signal >= 1) + ++CRawInput::signal; + else if (CRawInput::bSubclass) + { + // TF2 backpack fix not applied when in actual game + RemoveWindowSubclass(CRawInput::hwndClient, (SUBCLASSPROC)CRawInput::SubclassWndProc, 0); + CRawInput::bSubclass = false; + } - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &uiSize, RAWINPUTHDRSIZE) != -1) + if (CRawInput::consecG < MAX_CONSECG) + ++CRawInput::consecG; + + // Bug fix for cursor hitting side of screen in TF2 backpack + if (CRawInput::consecG == MAX_CONSECG) + { + 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::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) { - rwInput = (RAWINPUT*)lpb; + if (!CRawInput::hwndClient) + EnumWindows(CRawInput::EnumWindowsProc, (LPARAM)GetCurrentProcessId()); - if (!rwInput->header.dwType) + if (CRawInput::hwndClient) { - CRawInput::x += rwInput->data.mouse.lLastX; - CRawInput::y += rwInput->data.mouse.lLastY; + // When in TF2 backpack, monitor its window messages + SetWindowSubclass(CRawInput::hwndClient, (SUBCLASSPROC)CRawInput::SubclassWndProc, 0, 1); + CRawInput::bSubclass = true; } } - break; } - case WM_DESTROY: - PostQuitMessage(0); - break; - default: return DefWindowProc(hWnd, message, wParam, lParam); } - return 0; + if (!CRawInput::alttab) + { + // 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)) + { + CRawInput::set_x = CRawInput::hold_x; + CRawInput::set_y = CRawInput::hold_y; + } + } + + 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; + } + + CRawInput::SCP = 0; + return FALSE; } -int __stdcall CRawInput::hSetCursorPos(int x, int y) +LRESULT CALLBACK CRawInput::SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { - if (!TrmpSetCursorPos(x, y)) return 1; + switch (uMsg) + { + case WM_SETFOCUS: + // Bug fix during alt-tab back into TF2 backpack + CRawInput::signal = 1; + CRawInput::hCreateThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CRawInput::blockInput, NULL, 0, 0); + break; - CRawInput::x = (long)x; - CRawInput::y = (long)y; + case WM_NCDESTROY: + RemoveWindowSubclass(hWnd, (SUBCLASSPROC)CRawInput::SubclassWndProc, uIdSubclass); + break; -#ifdef _DEBUG - OutputDebugString("Set coordinates"); -#endif + default: + break; + } - return 0; + return DefSubclassProc(hWnd, uMsg, wParam, lParam); } -int __stdcall CRawInput::hGetCursorPos(LPPOINT lpPoint) +DWORD WINAPI CRawInput::blockInput(LPVOID lpParameter) { - lpPoint->x = CRawInput::x; - lpPoint->y = CRawInput::y; + BlockInput(TRUE); + unsigned char SleepTimer = 0; -#ifdef _DEBUG - OutputDebugString("Returned coordinates"); -#endif + // Unblock input after 11 SetCursorPos/GetCursorPos or 2 seconds + while (CRawInput::signal <= 12 && SleepTimer < 67) + { + ++SleepTimer; + Sleep(30); + } - return 0; + CRawInput::signal = 0; + BlockInput(FALSE); + CloseHandle(CRawInput::hCreateThread); + 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; -#ifdef _DEBUG - else - OutputDebugString("Hooked GetCursorPos and SetCursorPos"); -#endif + if (CRawInput::n_sourceEXE == TF2) + { + if (CRawInput::signal >= 1) + ++CRawInput::signal; + + // Bug fix for Steam overlay in TF2 backpack + if (CRawInput::consec_EndScene == MAX_CONSEC_ENDSCENE && CRawInput::consecG == MAX_CONSECG && !CRawInput::alttab && CRawInput::SCP == 0) + --CRawInput::SCP; + else + CRawInput::consecG = 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) +{ + HMODULE hD3D9Dll = NULL; + + while (!hD3D9Dll) + { + hD3D9Dll = GetModuleHandleA("d3d9.dll"); + Sleep(150); + } + + DWORD D3D9Base = (DWORD)hD3D9Dll; + DWORD oldEndScene = 0; + DWORD dwVersion = GetVersion(); + double fCompareVersion = LOBYTE(LOWORD(dwVersion)) + 0.1 * HIBYTE(LOWORD(dwVersion)); + + // 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; + + 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) { - if (DetourRemove((PBYTE)TrmpGetCursorPos, (PBYTE)CRawInput::hGetCursorPos)) + // Windows Vista and Windows 7 + for (DWORD g = 0; g < 0x128000; ++g) { -#ifdef _DEBUG - OutputDebugString("Removed GetCursorPos hook"); -#endif + ++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)) == 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; + } } - if (DetourRemove((PBYTE)TrmpSetCursorPos, (PBYTE)CRawInput::hSetCursorPos)) + } + else if (fCompareVersion >= 5.1) + { + // Windows XP + for (DWORD g = 0; g < 0x128000; ++g) { -#ifdef _DEBUG - OutputDebugString("Removed SetCursorPos hook"); -#endif + ++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; + } } } + + if (oldEndScene) + { + CRawInput::oD3D9EndScene = oldEndScene + 5; + CRawInput::JMPplace((PBYTE)oldEndScene, (DWORD)CRawInput::D3D9EndScene, 5); + } - return true; + CloseHandle(CRawInput::hD3D9HookThread); + return 1; +} + +// 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)); - 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(CRawInput::hwndInput); } } \ No newline at end of file diff --git a/rawinput.h b/rawinput.h index ac66f02..35e4105 100644 --- a/rawinput.h +++ b/rawinput.h @@ -1,54 +1,77 @@ #ifndef RAWINPUT_H_ #define RAWINPUT_H_ -#include -#include // Required for function hooking +#define TF2 2 +#define NO_BUG_FIXES 4 +#define MAX_CONSEC_ENDSCENE 7 +#define MAX_CONSECG 3 -#define RAWINPUTHDRSIZE sizeof(RAWINPUTHEADER) -#define RAWPTRSIZE 40 -#define INPUTWINDOW "RInput" +#include +#include "detours.h" +#include +#include +#include +#include -// Prevent warning level 4 warnings for detouring -#pragma warning(disable: 4100) +extern void displayError(WCHAR* pwszError); -/** - * 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 :). - */ -class CRawInput { +class CRawInput +{ public: - // Initialize raw input + // Initialize RInput components, register for raw input static bool initialize(WCHAR* pwszError); - - // Enable/Disable 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 + // 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 void JMPplace(PBYTE inFunc, DWORD deFunc, DWORD len); + static HRESULT WINAPI D3D9EndScene(); }; -extern void displayError(WCHAR* pwszError); - #endif \ No newline at end of file diff --git a/versioninfo.h b/versioninfo.h index fc888b3..e31bb38 100644 --- a/versioninfo.h +++ b/versioninfo.h @@ -1,11 +1,7 @@ #ifndef _VERSIONINFO_H_ #define _VERSIONINFO_H_ -#define RINPUTVER "v1.31 sequential edition" -#define RINPUTFVER 1,31 -#define IDI_ICON 101 - -#define RINPUTINT "RInput" -#define RINPUTDLL "RInput.dll" +#define RINPUTVER "v1.44" +#define RINPUTFVER 1,44 #endif diff --git a/versioninfo.rc b/versioninfo.rc index 7ef9981..c2326ba 100644 --- a/versioninfo.rc +++ b/versioninfo.rc @@ -1,7 +1,6 @@ #include "versioninfo.h" -#define VS_VERSION_INFO 1 -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