2016-11-19 01:24:49 +00:00
/*
2019-10-19 18:25:18 +00:00
Copyright 2018 - 2019 Peter Repukat - FlatspotSoftware
2016-11-19 01:24:49 +00:00
Licensed under the Apache License , Version 2.0 ( the " License " ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an " AS IS " BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
*/
# include "VirtualControllerThread.h"
2018-03-27 21:01:36 +00:00
2018-03-27 22:19:59 +00:00
2018-03-27 21:01:36 +00:00
# include "../common/loguru.hpp"
2016-11-19 01:24:49 +00:00
2018-03-27 20:15:31 +00:00
VirtualControllerThread : : VirtualControllerThread ( const int delay )
2016-11-19 01:24:49 +00:00
{
2018-03-10 22:02:39 +00:00
driver_ = vigem_alloc ( ) ;
2017-09-29 17:52:20 +00:00
2018-03-10 22:02:39 +00:00
if ( ! VIGEM_SUCCESS ( vigem_connect ( driver_ ) ) )
2016-11-19 01:24:49 +00:00
{
2018-03-27 21:01:36 +00:00
LOG_F ( ERROR , " initializing ViGem! " ) ;
2017-01-25 16:13:48 +00:00
MessageBoxW ( NULL , L " Error initializing ViGem! " , L " GloSC-SteamTarget " , MB_OK ) ;
2018-03-10 22:07:25 +00:00
b_should_run_ = false ;
2016-11-19 01:24:49 +00:00
}
2018-03-27 20:19:53 +00:00
for ( auto & target : vt_x360_ )
2016-11-19 01:24:49 +00:00
{
2018-03-27 20:19:53 +00:00
target = vigem_target_x360_alloc ( ) ;
2016-11-19 01:24:49 +00:00
}
2017-09-29 19:45:06 +00:00
2018-03-10 22:02:39 +00:00
seven_ = IsWindows7OrGreater ( ) ! = IsWindows8OrGreater ( ) ;
2018-03-27 20:15:31 +00:00
delay_ = delay ;
2016-11-19 01:24:49 +00:00
}
VirtualControllerThread : : ~ VirtualControllerThread ( )
{
2018-03-10 22:02:39 +00:00
if ( controller_thread_ . joinable ( ) )
controller_thread_ . join ( ) ;
vigem_disconnect ( driver_ ) ;
2016-11-19 01:24:49 +00:00
}
void VirtualControllerThread : : run ( )
{
2018-03-10 22:07:25 +00:00
b_should_run_ = true ;
2018-03-10 22:02:39 +00:00
controller_thread_ = std : : thread ( & VirtualControllerThread : : controllerLoop , this ) ;
2016-11-19 01:24:49 +00:00
}
void VirtualControllerThread : : stop ( )
{
2018-03-10 22:07:25 +00:00
b_should_run_ = false ;
2018-03-27 20:19:53 +00:00
for ( auto & target : vt_x360_ )
2016-11-19 01:24:49 +00:00
{
2018-03-27 20:19:53 +00:00
vigem_target_remove ( driver_ , target ) ;
2016-11-19 01:24:49 +00:00
}
}
2018-03-10 22:02:39 +00:00
bool VirtualControllerThread : : isRunning ( ) const
2016-11-19 01:24:49 +00:00
{
2018-03-10 22:07:25 +00:00
return b_should_run_ ;
2016-11-19 01:24:49 +00:00
}
void VirtualControllerThread : : controllerLoop ( )
{
2017-02-15 20:49:45 +00:00
sf : : Clock waitForHookTimer ;
2018-03-10 22:07:25 +00:00
while ( b_should_run_ )
2016-11-19 01:24:49 +00:00
{
2018-03-10 22:02:39 +00:00
sf_clock_ . restart ( ) ;
2016-11-19 01:24:49 +00:00
2017-02-15 13:34:01 +00:00
// We have to retrieve the XInputGetState function by loading it via GetProcAdress
2018-03-10 22:02:39 +00:00
// otherwise we get calls to a jumptable, jumping to the real function
2017-02-15 13:34:01 +00:00
// We can't have this if we wan't to dynamically unpatch and repatch Valve's XInput hook
// Also wait a second, jut to be sure Steam has done it's hooking thing...
2018-03-10 22:02:39 +00:00
if ( x_get_state_ = = nullptr & & waitForHookTimer . getElapsedTime ( ) . asSeconds ( ) > 1 )
2016-11-19 01:24:49 +00:00
{
2017-02-15 03:07:27 +00:00
HMODULE xinputmod = nullptr ;
2016-11-19 01:24:49 +00:00
2018-03-10 22:02:39 +00:00
const HANDLE hProcess = GetCurrentProcess ( ) ;
2017-02-15 03:07:27 +00:00
HMODULE hMods [ 1024 ] ;
DWORD cbNeeded ;
EnumProcessModules ( hProcess , hMods , sizeof ( hMods ) , & cbNeeded ) ;
for ( int i = 0 ; i < ( cbNeeded / sizeof ( HMODULE ) ) ; i + + )
{
TCHAR szModName [ MAX_PATH ] ;
2016-11-19 01:24:49 +00:00
2017-02-15 03:07:27 +00:00
if ( GetModuleBaseName ( hProcess , hMods [ i ] , szModName ,
sizeof ( szModName ) / sizeof ( TCHAR ) ) )
2016-11-19 01:24:49 +00:00
{
2017-02-15 03:07:27 +00:00
std : : wstring name ( & szModName [ 0 ] ) ;
auto & f = std : : use_facet < std : : ctype < wchar_t > > ( std : : locale ( ) ) ;
f . tolower ( & name [ 0 ] , & name [ 0 ] + name . size ( ) ) ;
if ( name . find ( std : : wstring ( L " xinput " ) ) ! = std : : wstring : : npos )
{
xinputmod = hMods [ i ] ;
break ;
}
2016-11-19 01:24:49 +00:00
}
}
2018-03-10 22:02:39 +00:00
const XInputGetState_t realXgstate = reinterpret_cast < XInputGetState_t > ( GetProcAddress ( xinputmod , " XInputGetState " ) ) ;
2016-11-19 01:24:49 +00:00
2018-03-11 02:38:36 +00:00
//std::cout << "realXgstate: " << std::hex << realXgstate << "\n";
2017-02-15 03:07:27 +00:00
for ( int i = 0 ; i < 5 ; i + + )
2016-11-19 01:24:49 +00:00
{
2018-03-10 22:02:39 +00:00
valve_hook_bytes_ [ i ] = * reinterpret_cast < uint8_t * > ( reinterpret_cast < uint64_t > ( * realXgstate ) + i ) ;
2017-02-15 03:07:27 +00:00
}
2016-11-19 01:24:49 +00:00
2018-03-10 22:02:39 +00:00
x_get_state_ = realXgstate ;
controller_count_ = 1 ;
2017-02-15 03:07:27 +00:00
}
2016-11-19 01:24:49 +00:00
2018-03-10 22:02:39 +00:00
if ( x_get_state_ ! = nullptr )
2017-02-15 03:07:27 +00:00
{
2017-02-15 13:34:01 +00:00
for ( int i = 0 ; i < XUSER_MAX_COUNT ; i + + )
2016-11-19 01:24:49 +00:00
{
2017-02-15 03:07:27 +00:00
////////
2018-03-27 20:23:28 +00:00
//Call the hooked, as well as the 'real' XInputGetState function to determine if a controller is real of 'fake' (from Steam)
2017-02-15 13:34:01 +00:00
XINPUT_STATE state = { 0 } ;
2018-03-10 22:02:39 +00:00
const DWORD result = XInputGetStateWrapper ( i , & state ) ;
2017-02-15 13:34:01 +00:00
XINPUT_STATE state2 = { 0 } ;
2018-03-10 22:02:39 +00:00
const DWORD result2 = callRealXinputGetState ( i , & state2 ) ;
2017-02-15 03:07:27 +00:00
if ( result = = ERROR_SUCCESS )
2016-11-19 01:24:49 +00:00
{
2018-03-27 20:23:28 +00:00
if ( ( result2 ! = ERROR_SUCCESS ) = = seven_ ) //for whatever reason, the second call also returns true on win7, false (as it should(?)) otherwise.
2017-02-15 03:07:27 +00:00
{
2017-02-15 13:34:01 +00:00
// By using VID and PID of Valve's SteamController, Steam doesn't give us ANOTHER "fake" XInput device
// Leading to endless pain and suffering.
// Or really, leading to pluggin in one virtual controller after another and mirroring inputs
// Also annoying the shit out of the user when they open the overlay as steam prompts to setup new XInput devices
// Also avoiding any fake inputs from Valve's default controllerprofile
// -> Leading to endless pain and suffering
2018-03-10 22:02:39 +00:00
vigem_target_set_vid ( vt_x360_ [ i ] , 0x28de ) ; //Valve SteamController VID
vigem_target_set_pid ( vt_x360_ [ i ] , 0x1102 ) ; //Valve SteamController PID
2017-02-15 13:34:01 +00:00
2018-03-10 22:02:39 +00:00
const int vigem_res = vigem_target_add ( driver_ , vt_x360_ [ i ] ) ;
2017-02-15 13:34:01 +00:00
if ( vigem_res = = VIGEM_ERROR_TARGET_UNINITIALIZED )
{
2018-03-10 22:02:39 +00:00
vt_x360_ [ i ] = vigem_target_x360_alloc ( ) ;
2017-02-15 13:34:01 +00:00
}
if ( vigem_res = = VIGEM_ERROR_NONE )
{
2018-03-27 21:01:36 +00:00
LOG_F ( INFO , " Plugged in controller %d " , vigem_target_get_index ( vt_x360_ [ i ] ) ) ;
2018-03-10 22:02:39 +00:00
vigem_target_x360_register_notification ( driver_ , vt_x360_ [ i ] ,
2019-10-19 18:25:54 +00:00
reinterpret_cast < PFN_VIGEM_X360_NOTIFICATION > ( & VirtualControllerThread : :
2018-03-10 22:02:39 +00:00
controllerCallback ) ) ;
2017-02-15 13:34:01 +00:00
}
2017-02-15 03:07:27 +00:00
}
2016-11-19 01:24:49 +00:00
2018-03-10 22:02:39 +00:00
if ( vt_x360_ [ i ] ! = nullptr )
vigem_target_x360_update ( driver_ , vt_x360_ [ i ] , * reinterpret_cast < XUSB_REPORT * > ( & state . Gamepad ) ) ;
2017-02-15 03:07:27 +00:00
}
else
{
2018-03-10 22:02:39 +00:00
if ( VIGEM_SUCCESS ( vigem_target_remove ( driver_ , vt_x360_ [ i ] ) ) )
2017-02-15 03:07:27 +00:00
{
2018-03-27 21:01:36 +00:00
LOG_F ( INFO , " Unplugged controller %d " , vigem_target_get_index ( vt_x360_ [ i ] ) ) ;
2016-11-19 01:24:49 +00:00
}
}
}
}
2018-03-10 22:02:39 +00:00
tick_time_ = sf_clock_ . getElapsedTime ( ) . asMicroseconds ( ) ;
2018-03-27 20:15:31 +00:00
if ( tick_time_ < delay_ )
2017-01-25 16:14:23 +00:00
{
2018-03-27 20:15:31 +00:00
std : : this_thread : : sleep_for ( std : : chrono : : microseconds ( delay_ - tick_time_ ) ) ;
2017-01-25 16:14:23 +00:00
}
2017-02-15 03:07:27 +00:00
2016-11-19 01:24:49 +00:00
}
}
2017-09-29 17:52:20 +00:00
void VirtualControllerThread : : controllerCallback ( PVIGEM_CLIENT client , PVIGEM_TARGET Target , UCHAR LargeMotor , UCHAR SmallMotor , UCHAR LedNumber )
2016-11-19 01:24:49 +00:00
{
XINPUT_VIBRATION vibration ;
ZeroMemory ( & vibration , sizeof ( XINPUT_VIBRATION ) ) ;
2016-11-19 01:29:21 +00:00
vibration . wLeftMotorSpeed = LargeMotor * 0xff ; //Controllers only use 1 byte, XInput-API uses two, ViGEm also only uses one, like the hardware does, so we have to multiply
2017-02-15 03:07:27 +00:00
vibration . wRightMotorSpeed = SmallMotor * 0xff ; //Yeah yeah I do know about bitshifting and the multiplication not being 100% correct...
2016-11-19 01:24:49 +00:00
2017-09-29 17:52:20 +00:00
XInputSetState ( vigem_target_get_index ( Target ) - 1 , & vibration ) ;
2017-02-15 03:07:27 +00:00
}
DWORD VirtualControllerThread : : XInputGetStateWrapper ( DWORD dwUserIndex , XINPUT_STATE * pState )
{
return XInputGetState ( dwUserIndex , pState ) ;
}
DWORD VirtualControllerThread : : callRealXinputGetState ( DWORD dwUserIndex , XINPUT_STATE * pState )
{
DWORD dwOldProtect , dwBkup ;
2018-03-10 22:07:25 +00:00
auto * Address = reinterpret_cast < BYTE * > ( x_get_state_ ) ;
2018-03-10 22:02:39 +00:00
VirtualProtect ( Address , op_patch_lenght , PAGE_EXECUTE_READWRITE , & dwOldProtect ) ; //Change permissions of memory..
for ( DWORD i = 0 ; i < op_patch_lenght ; i + + ) //unpatch Valve's hook
2016-11-19 01:24:49 +00:00
{
2018-03-18 13:37:53 +00:00
* ( Address + i ) = real_bytes [ i ] ;
2017-02-15 03:07:27 +00:00
}
2018-03-10 22:07:25 +00:00
const DWORD ret = x_get_state_ ( dwUserIndex , pState ) ; //Cal REAL XInputGetState...
2017-02-15 03:07:27 +00:00
2018-03-10 22:02:39 +00:00
for ( int i = 0 ; i < op_patch_lenght ; i + + ) //repatch Valve's hook
2017-02-15 03:07:27 +00:00
{
2018-03-10 22:02:39 +00:00
* ( Address + i ) = valve_hook_bytes_ [ i ] ;
2016-11-19 01:24:49 +00:00
}
2018-03-10 22:02:39 +00:00
VirtualProtect ( Address , op_patch_lenght , dwOldProtect , & dwBkup ) ; //Revert permission change...
2017-02-15 03:07:27 +00:00
return ret ;
2016-11-19 01:24:49 +00:00
}
2017-02-15 03:07:27 +00:00