2021-10-20 19:48:19 +00:00
/*
2022-01-06 23:38:21 +00:00
Copyright 2021 - 2022 Peter Repukat - FlatspotSoftware
2021-10-20 19:48:19 +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 .
*/
// parts of code adapted from https://github.com/ViGEm/HidHide/blob/HEAD/HidHideCLI/src/HID.cpp
// // (c) Eric Korff de Gidts
// SPDX-License-Identifier: MIT
# include "HidHide.h"
# include <iostream>
# include <numeric>
# include <spdlog/spdlog.h>
# include <vector>
// Device configuration related
# include <cfgmgr32.h>
# include <initguid.h>
//
2021-10-22 16:07:08 +00:00
# include "Overlay.h"
2021-10-23 14:03:18 +00:00
# include "Settings.h"
2021-10-22 16:07:08 +00:00
2021-10-20 19:48:19 +00:00
# include <devguid.h>
# include <devpkey.h>
# include <regex>
// {D61CA365-5AF4-4486-998B-9DB4734C6CA3}add the XUSB class GUID as it is missing in the public interfaces
DEFINE_GUID ( GUID_DEVCLASS_XUSBCLASS , 0xD61CA365 , 0x5AF4 , 0x4486 , 0x99 , 0x8B , 0x9D , 0xB4 , 0x73 , 0x4C , 0x6C , 0xA3 ) ;
// {EC87F1E3-C13B-4100-B5F7-8B84D54260CB} add the XUSB interface class GUID as it is missing in the public interfaces
DEFINE_GUID ( GUID_DEVINTERFACE_XUSB , 0xEC87F1E3 , 0xC13B , 0x4100 , 0xB5 , 0xF7 , 0x8B , 0x84 , 0xD5 , 0x42 , 0x60 , 0xCB ) ;
// {00000000-0000-0000-FFFF-FFFFFFFFFFFF} the system container id
DEFINE_GUID ( GUID_CONTAINER_ID_SYSTEM , 0x00000000 , 0x0000 , 0x0000 , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ) ;
2021-10-22 23:29:59 +00:00
HidHide : : HidHide ( ) { } ;
2021-10-20 19:48:19 +00:00
void HidHide : : openCtrlDevice ( )
{
hidhide_handle = CreateFile (
L " \\ \\ . \\ HidHide " ,
GENERIC_READ ,
( FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ) ,
NULL ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_NORMAL ,
NULL ) ;
}
void HidHide : : closeCtrlDevice ( )
{
if ( hidhide_handle = = nullptr ) {
return ;
}
CloseHandle ( hidhide_handle ) ;
hidhide_handle = nullptr ;
}
void HidHide : : hideDevices ( const std : : filesystem : : path & steam_path )
{
2021-10-22 23:29:59 +00:00
UnPatchValveHooks ( ) ;
2021-10-20 19:48:19 +00:00
openCtrlDevice ( ) ;
auto active = getActive ( ) ;
if ( active ) {
// disable hidhide so we can see devices ourselves
setActive ( false ) ;
}
auto whitelist = getAppWhiteList ( ) ;
// has anyone more than 4 keys to open overlay?!
std : : wsmatch m ;
const auto steam_path_string = steam_path . wstring ( ) ;
if ( ! std : : regex_search ( steam_path_string , m , std : : wregex ( L " (.:)( \\ /| \\ \\ ) " ) ) | | m . size ( ) < 3 ) {
spdlog : : warn ( " Couldn't detect steam drive letter; Device hiding may not function " ) ;
return ;
}
const auto dos_device = DosDeviceForVolume ( m [ 1 ] ) ;
if ( dos_device . empty ( ) ) {
spdlog : : warn ( " Couldn't detect steam drive letter DOS Path; Device hiding may not function " ) ;
return ;
}
for ( const auto & exe : whitelist_executeables_ ) {
auto path = std : : regex_replace ( steam_path_string , std : : wregex ( L " (.:)( \\ /| \\ \\ ) " ) , dos_device + L " \\ " ) ;
path = std : : regex_replace ( path , std : : wregex ( L " \\ / " ) , L " \\ " ) + L " \\ " + std : : wstring { exe } ;
if ( std : : ranges : : none_of ( whitelist , [ & path ] ( auto ep ) { // make copy!
2021-10-22 16:07:08 +00:00
auto p = path ; // non-const(!) copy of path
2021-10-20 19:48:19 +00:00
std : : ranges : : transform ( path , p . begin ( ) , tolower ) ;
std : : ranges : : transform ( ep , ep . begin ( ) , tolower ) ;
return p = = ep ;
} ) ) {
whitelist . push_back ( path ) ;
}
}
setAppWhiteList ( whitelist ) ;
2021-10-22 16:07:08 +00:00
avail_devices_ = GetHidDeviceList ( ) ;
blacklisted_devices_ = getBlackListDevices ( ) ;
2021-10-20 19:48:19 +00:00
2021-10-22 16:07:08 +00:00
for ( const auto & dev : avail_devices_ ) {
if ( std : : ranges : : none_of ( blacklisted_devices_ , [ & dev ] ( const auto & blackdev ) {
2021-10-20 19:48:19 +00:00
return blackdev = = dev . device_instance_path | | blackdev = = dev . base_container_device_instance_path ;
2021-10-22 16:07:08 +00:00
} ) ) {
2021-10-22 23:29:59 +00:00
// Valve emulated gamepad PID/VID; mirrord by ViGEm
if ( ! ( dev . vendor_id = = 0x28de & & dev . product_id = = 0x11FF ) ) {
if ( ! dev . device_instance_path . empty ( ) ) {
blacklisted_devices_ . push_back ( dev . device_instance_path ) ;
}
if ( ! dev . device_instance_path . empty ( ) ) {
blacklisted_devices_ . push_back ( dev . base_container_device_instance_path ) ;
2021-10-22 23:44:31 +00:00
}
2021-10-20 19:48:19 +00:00
}
}
}
2021-10-23 14:03:18 +00:00
if ( Settings : : devices . hideDevices ) {
// TODO: MAXBE: remove all vigem controllers added by GlosSI
setBlacklistDevices ( blacklisted_devices_ ) ;
setActive ( true ) ;
spdlog : : info ( " Hid Gaming Devices; Enabling Overlay element... " ) ;
enableOverlayElement ( ) ;
}
2021-10-20 19:48:19 +00:00
closeCtrlDevice ( ) ;
}
void HidHide : : disableHidHide ( )
{
2021-10-23 14:03:18 +00:00
openCtrlDevice ( ) ;
if ( getActive ( ) ) {
setActive ( false ) ;
spdlog : : info ( " Un-hid Gaming Devices " ) ;
}
closeCtrlDevice ( ) ;
2021-10-22 16:07:08 +00:00
}
2021-10-22 23:29:59 +00:00
void HidHide : : UnPatchValveHooks ( )
{
spdlog : : debug ( " Unpatching Valve HID hooks... " ) ;
// need to load addresses that way.. Otherwise we land before some jumps...
auto setupapidll = GetModuleHandle ( L " setupapi.dll " ) ;
if ( setupapidll ) {
BYTE * addr = reinterpret_cast < BYTE * > ( GetProcAddress ( setupapidll , " SetupDiEnumDeviceInfo " ) ) ;
if ( addr ) {
UnPatchHook ( addr , SETUP_DI_ENUM_DEV_INFO_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched SetupDiEnumDeviceInfo " ) ;
}
else {
spdlog : : error ( " failed to unpatch SetupDiEnumDeviceInfo " ) ;
}
2021-11-04 21:15:54 +00:00
//addr = reinterpret_cast<BYTE*>(GetProcAddress(setupapidll, "SetupDiGetClassDevsW"));
//if (addr) {
// UnPatchHook(addr, SETUP_DI_GETCLASSDEVSW_ORIG_BYTES);
// spdlog::trace("Unpatched SetupDiGetClassDevsW");
//}
//else {
// spdlog::error("failed to unpatch SetupDiGetClassDevsW");
//}
2021-10-22 23:29:59 +00:00
}
auto hiddll = GetModuleHandle ( L " hid.dll " ) ;
if ( hiddll ) {
BYTE * addr = reinterpret_cast < BYTE * > ( GetProcAddress ( hiddll , " HidD_GetPreparsedData " ) ) ;
if ( addr ) {
UnPatchHook ( addr , HID_GETPREPARSED_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched HidD_GetPreparsedData " ) ;
}
else {
spdlog : : error ( " failed to unpatch HidD_GetPreparsedData " ) ;
}
addr = reinterpret_cast < BYTE * > ( GetProcAddress ( hiddll , " HidP_GetCaps " ) ) ;
if ( addr ) {
UnPatchHook ( addr , HID_GETCAPS_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched HidP_GetCaps " ) ;
}
else {
spdlog : : error ( " failed to unpatch HidP_GetCaps " ) ;
}
addr = reinterpret_cast < BYTE * > ( GetProcAddress ( hiddll , " HidD_GetAttributes " ) ) ;
if ( addr ) {
UnPatchHook ( addr , HID_GETATTRS_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched HidD_GetAttributes " ) ;
}
else {
spdlog : : error ( " failed to unpatch HidD_GetAttributes " ) ;
}
addr = reinterpret_cast < BYTE * > ( GetProcAddress ( hiddll , " HidD_GetProductString " ) ) ;
if ( addr ) {
UnPatchHook ( addr , HID_GETPRODSTR_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched HidD_GetProductString " ) ;
}
else {
spdlog : : error ( " failed to unpatch HidD_GetProductString " ) ;
}
addr = reinterpret_cast < BYTE * > ( GetProcAddress ( hiddll , " HidP_GetUsages " ) ) ;
if ( addr ) {
UnPatchHook ( addr , HID_GETUSAGE_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched HidP_GetUsages " ) ;
}
else {
spdlog : : error ( " failed to unpatch HidP_GetUsages " ) ;
}
addr = reinterpret_cast < BYTE * > ( GetProcAddress ( hiddll , " HidP_GetData " ) ) ;
if ( addr ) {
UnPatchHook ( addr , HID_GETDATA_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched HidP_GetData " ) ;
}
else {
spdlog : : error ( " failed to unpatch HidP_GetData " ) ;
}
addr = reinterpret_cast < BYTE * > ( GetProcAddress ( hiddll , " HidP_GetValueCaps " ) ) ;
if ( addr ) {
UnPatchHook ( addr , HID_GETVALUECAPS_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched HidP_GetValueCaps " ) ;
}
else {
spdlog : : error ( " failed to unpatch HidP_GetValueCaps " ) ;
}
addr = reinterpret_cast < BYTE * > ( GetProcAddress ( hiddll , " HidP_GetUsageValue " ) ) ;
if ( addr ) {
UnPatchHook ( addr , HID_GETUSAGE_VAL_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched HidP_GetUsageValue " ) ;
}
else {
spdlog : : error ( " failed to unpatch HidP_GetUsageValue " ) ;
}
addr = reinterpret_cast < BYTE * > ( GetProcAddress ( hiddll , " HidP_GetButtonCaps " ) ) ;
if ( addr ) {
UnPatchHook ( addr , HID_GETBTNCAPS_VAL_ORIG_BYTES ) ;
spdlog : : trace ( " Unpatched HidP_GetButtonCaps " ) ;
}
else {
spdlog : : error ( " failed to unpatch HidP_GetButtonCaps " ) ;
}
}
}
void HidHide : : UnPatchHook ( BYTE * address , const std : : string & bytes )
{
DWORD dw_old_protect , dw_bkup ;
const auto len = bytes . size ( ) ;
VirtualProtect ( address , len , PAGE_EXECUTE_READWRITE , & dw_old_protect ) ; //Change permissions of memory..
for ( DWORD i = 0 ; i < len ; i + + ) //unpatch Valve's hook
{
* ( address + i ) = bytes [ i ] ;
}
VirtualProtect ( address , len , dw_old_protect , & dw_bkup ) ; //Revert permission change...
}
2021-10-22 16:07:08 +00:00
void HidHide : : enableOverlayElement ( )
{
Overlay : : AddOverlayElem ( [ this ] ( ) {
if ( overlay_elem_clock_ . getElapsedTime ( ) . asSeconds ( ) > OVERLAY_ELEM_REFRESH_INTERVAL_S_ ) {
openCtrlDevice ( ) ;
bool hidehide_state_store = hidhide_active_ ;
if ( hidhide_active_ ) {
setActive ( false ) ;
}
avail_devices_ = GetHidDeviceList ( ) ;
blacklisted_devices_ = getBlackListDevices ( ) ;
if ( hidehide_state_store ) {
setActive ( true ) ;
}
closeCtrlDevice ( ) ;
overlay_elem_clock_ . restart ( ) ;
}
2021-10-30 01:08:47 +00:00
ImGui : : SetNextWindowPos ( { 650 , 100 } , ImGuiCond_FirstUseEver ) ;
ImGui : : SetNextWindowSizeConstraints ( { 400 , 270 } , { 1000 , 1000 } ) ;
2021-10-22 16:07:08 +00:00
ImGui : : Begin ( " Hidden Devices " ) ;
2021-10-28 13:20:20 +00:00
ImGui : : BeginChild ( " Inner " , { 0.f , ImGui : : GetItemRectSize ( ) . y - 64 } , true ) ;
2021-10-22 16:07:08 +00:00
std : : ranges : : for_each ( avail_devices_ , [ this ] ( const auto & device ) {
std : : string label = ( std : : string ( device . name . begin ( ) , std : : ranges : : find ( device . name , L ' \0 ' ) ) + " ## " + std : : string ( device . device_instance_path . begin ( ) , device . device_instance_path . end ( ) ) ) ;
const auto findDeviceFn = [ & device ] ( const auto & blackdev ) {
return device . device_instance_path = = blackdev | | device . base_container_device_instance_path = = blackdev ;
} ;
bool hidden = std : : ranges : : find_if ( blacklisted_devices_ , findDeviceFn ) ! = blacklisted_devices_ . end ( ) ;
if ( ImGui : : Checkbox ( label . data ( ) , & hidden ) ) {
openCtrlDevice ( ) ;
if ( hidden ) {
if ( std : : ranges : : none_of ( blacklisted_devices_ , findDeviceFn ) ) {
if ( ! device . device_instance_path . empty ( ) ) {
blacklisted_devices_ . push_back ( device . device_instance_path ) ;
}
if ( ! device . device_instance_path . empty ( ) ) {
blacklisted_devices_ . push_back ( device . base_container_device_instance_path ) ;
}
}
}
else {
blacklisted_devices_ . erase ( std : : ranges : : remove_if ( blacklisted_devices_ , findDeviceFn ) . begin ( ) ,
blacklisted_devices_ . end ( ) ) ;
}
setBlacklistDevices ( blacklisted_devices_ ) ;
closeCtrlDevice ( ) ;
}
} ) ;
ImGui : : EndChild ( ) ;
if ( ImGui : : Checkbox ( " Devices Hidden " , & hidhide_active_ ) ) {
openCtrlDevice ( ) ;
setActive ( hidhide_active_ ) ;
closeCtrlDevice ( ) ;
}
ImGui : : End ( ) ;
} ) ;
2021-10-20 19:48:19 +00:00
}
std : : wstring HidHide : : DosDeviceForVolume ( const std : : wstring & volume )
{
std : : vector < WCHAR > buffer ( UNICODE_STRING_MAX_CHARS ) ;
QueryDosDeviceW ( volume . c_str ( ) , buffer . data ( ) , static_cast < DWORD > ( buffer . size ( ) ) ) ;
return { buffer . data ( ) } ;
}
std : : vector < std : : wstring > HidHide : : getAppWhiteList ( ) const
{
DWORD bytes_needed = getRequiredOutputBufferSize ( IOCTL_TYPE : : GET_WHITELIST ) ;
if ( bytes_needed = = 0 ) {
return std : : vector < std : : wstring > { } ;
}
std : : vector < WCHAR > buffer ( bytes_needed ) ;
if ( ! DeviceIoControl (
hidhide_handle , static_cast < DWORD > ( IOCTL_TYPE : : GET_WHITELIST ) , nullptr , 0 , buffer . data ( ) , static_cast < DWORD > ( buffer . size ( ) * sizeof ( WCHAR ) ) , & bytes_needed , nullptr ) ) {
spdlog : : error ( " Couldn't retrieve HidHide Whitelist " ) ;
return std : : vector < std : : wstring > { } ;
}
return BufferToStringVec ( buffer ) ;
}
std : : vector < std : : wstring > HidHide : : getBlackListDevices ( ) const
{
DWORD bytes_needed = getRequiredOutputBufferSize ( IOCTL_TYPE : : GET_BLACKLIST ) ;
if ( bytes_needed = = 0 ) {
return std : : vector < std : : wstring > { } ;
}
std : : vector < WCHAR > buffer ( bytes_needed ) ;
if ( ! DeviceIoControl (
hidhide_handle , static_cast < DWORD > ( IOCTL_TYPE : : GET_BLACKLIST ) , nullptr , 0 , buffer . data ( ) , static_cast < DWORD > ( buffer . size ( ) * sizeof ( WCHAR ) ) , & bytes_needed , nullptr ) ) {
spdlog : : error ( " Couldn't retrieve HidHide Blacklist " ) ;
return std : : vector < std : : wstring > { } ;
}
return BufferToStringVec ( buffer ) ;
}
2021-10-22 16:07:08 +00:00
bool HidHide : : getActive ( )
2021-10-20 19:48:19 +00:00
{
DWORD bytes_needed ;
BOOLEAN res ;
if ( ! DeviceIoControl (
hidhide_handle , static_cast < DWORD > ( IOCTL_TYPE : : GET_ACTIVE ) , nullptr , 0 , & res , sizeof ( BOOLEAN ) , & bytes_needed , nullptr ) ) {
spdlog : : error ( " Couldn't retrieve HidHide State " ) ;
return false ;
}
2021-10-22 16:07:08 +00:00
hidhide_active_ = res ;
2021-10-20 19:48:19 +00:00
return res ;
}
void HidHide : : setAppWhiteList ( const std : : vector < std : : wstring > & whitelist ) const
{
DWORD bytes_needed ;
auto buffer = StringListToMultiString ( whitelist ) ;
if ( ! DeviceIoControl (
hidhide_handle , static_cast < DWORD > ( IOCTL_TYPE : : SET_WHITELIST ) , buffer . data ( ) , static_cast < DWORD > ( buffer . size ( ) * sizeof ( WCHAR ) ) , nullptr , 0 , & bytes_needed , nullptr ) ) {
spdlog : : error ( " Couldn't set HidHide WhiteList " ) ;
}
}
void HidHide : : setBlacklistDevices ( const std : : vector < std : : wstring > & blacklist ) const
{
DWORD bytes_needed ;
auto buffer = StringListToMultiString ( blacklist ) ;
if ( ! DeviceIoControl (
hidhide_handle , static_cast < DWORD > ( IOCTL_TYPE : : SET_BLACKLIST ) , buffer . data ( ) , static_cast < DWORD > ( buffer . size ( ) * sizeof ( WCHAR ) ) , nullptr , 0 , & bytes_needed , nullptr ) ) {
spdlog : : error ( " Couldn't set HidHide BlackList " ) ;
}
}
2021-10-22 16:07:08 +00:00
void HidHide : : setActive ( bool active )
2021-10-20 19:48:19 +00:00
{
DWORD bytes_needed ;
if ( ! DeviceIoControl (
hidhide_handle , static_cast < DWORD > ( IOCTL_TYPE : : SET_ACTIVE ) , & active , sizeof ( BOOLEAN ) , nullptr , 0 , & bytes_needed , nullptr ) ) {
spdlog : : error ( " Couldn't set HidHide State " ) ;
2021-10-22 16:07:08 +00:00
return ;
2021-10-20 19:48:19 +00:00
}
2021-10-22 16:07:08 +00:00
hidhide_active_ = active ;
2021-10-20 19:48:19 +00:00
}
DWORD HidHide : : getRequiredOutputBufferSize ( IOCTL_TYPE type ) const
{
DWORD bytes_needed ;
if ( ! DeviceIoControl ( hidhide_handle , static_cast < DWORD > ( type ) , nullptr , 0 , nullptr , 0 , & bytes_needed , nullptr ) ) {
spdlog : : error ( " Couldn't determine required HidHide output buffer size; type: {} " , type ) ;
return 0 ;
}
return bytes_needed ;
}
std : : vector < std : : wstring > HidHide : : BufferToStringVec ( const auto & buffer )
{
std : : vector < std : : wstring > res ;
if ( buffer [ 0 ] ! = L ' \0 ' ) {
res . emplace_back ( ) ;
for ( const auto & ch : buffer ) {
if ( ch = = L ' \0 ' ) {
if ( ( res . end ( ) - 1 ) - > length ( ) = = 0 ) {
res . erase ( res . end ( ) - 1 ) ;
break ;
}
res . emplace_back ( ) ;
continue ;
}
( res . end ( ) - 1 ) - > push_back ( ch ) ;
}
}
return res ;
}
std : : vector < WCHAR > HidHide : : StringListToMultiString ( const std : : vector < std : : wstring > & stringlist )
{
auto res = std : : accumulate ( stringlist . begin ( ) , stringlist . end ( ) , std : : vector < WCHAR > { } , [ ] ( auto acc , const auto & curr ) {
acc . insert ( acc . end ( ) , curr . begin ( ) , curr . end ( ) ) ;
acc . push_back ( L ' \0 ' ) ;
return acc ;
} ) ;
res . push_back ( L ' \0 ' ) ;
return res ;
}
std : : vector < HidHide : : SmallHidInfo > HidHide : : GetHidDeviceList ( )
{
std : : wstring hid_class_guid_string ;
hid_class_guid_string . resize ( 39 ) ;
if ( ! StringFromGUID2 ( GUID_DEVCLASS_HIDCLASS , hid_class_guid_string . data ( ) , static_cast < int > ( hid_class_guid_string . size ( ) ) ) ) {
spdlog : : error ( " couldn't convert GUID to string " ) ;
}
ULONG needed { } ;
if ( const auto result = CM_Get_Device_ID_List_SizeW ( & needed , hid_class_guid_string . c_str ( ) , CM_GETIDLIST_FILTER_CLASS ) ;
( CR_SUCCESS ! = result ) ) {
spdlog : : error ( " Couldn't get device id list size; code: {} " , result ) ;
}
std : : vector < WCHAR > buffer ( needed ) ;
if ( const auto result = CM_Get_Device_ID_ListW ( hid_class_guid_string . c_str ( ) , buffer . data ( ) , needed , CM_GETIDLIST_FILTER_CLASS ) ;
( CR_SUCCESS ! = result ) ) {
spdlog : : error ( " Couldn't get device id list; code: {} " , result ) ;
}
auto device_instance_paths = BufferToStringVec ( buffer ) ;
2021-10-22 23:29:59 +00:00
2021-10-20 19:48:19 +00:00
device_instance_paths . erase (
std : : ranges : : remove_if (
device_instance_paths ,
[ ] ( const auto & dev ) { return ! DevicePresent ( dev ) ; } )
. begin ( ) ,
device_instance_paths . end ( ) ) ;
GUID hid_device_interface_guid { } ;
HidD_GetHidGuid ( & hid_device_interface_guid ) ;
std : : vector < SmallHidInfo > res ;
for ( auto & instance_path : device_instance_paths ) {
auto symlink = SymbolicLink ( hid_device_interface_guid , instance_path ) ;
if ( ! symlink . empty ( ) ) {
res . push_back ( GetDeviceInfo ( instance_path , symlink ) ) ;
}
}
res . erase (
std : : ranges : : remove_if (
res ,
[ ] ( const auto & dev ) { return ! dev . gaming_device ; } )
. begin ( ) ,
res . end ( ) ) ;
return res ;
}
HidHide : : SmallHidInfo HidHide : : GetDeviceInfo ( const DeviceInstancePath & instance_path , const std : : filesystem : : path & symlink )
{
SmallHidInfo res ;
res . device_instance_path = instance_path ;
// Open a handle to communicate with the HID device
const CloseHandlePtr device_object (
CreateFileW (
symlink . c_str ( ) ,
GENERIC_READ ,
( FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ) ,
nullptr ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_NORMAL ,
nullptr ) ,
& CloseHandle ) ;
if ( INVALID_HANDLE_VALUE = = device_object . get ( ) ) {
const auto err = GetLastError ( ) ;
switch ( err ) {
case ERROR_ACCESS_DENIED :
// The device is opened exclusively and in use hence we can't interact with it
__fallthrough ;
case ERROR_SHARING_VIOLATION :
// The device is (most-likely) cloaked by Hid Hide itself while its client application isn't on the white-list
__fallthrough ;
case ERROR_FILE_NOT_FOUND :
// The device is currently not present hence we can't query its details
return res ;
default :
spdlog : : error ( L " Couldn't open device {}; code: {} " , instance_path , err ) ;
return res ;
}
}
PHIDP_PREPARSED_DATA pre_parsed_data ;
if ( ! HidD_GetPreparsedData ( device_object . get ( ) , & pre_parsed_data ) ) {
spdlog : : error ( L " Couldn't get PreParsed data; Device: {} " , instance_path ) ;
return { } ;
}
const HidD_FreePreparsedDataPtr free_preparsed_data_ptr ( pre_parsed_data , & HidD_FreePreparsedData ) ;
HIDP_CAPS capabilities ;
if ( HIDP_STATUS_SUCCESS ! = HidP_GetCaps ( pre_parsed_data , & capabilities ) ) {
spdlog : : error ( L " Could get Hid capabilities; Device: {} " , instance_path ) ;
return { } ;
}
HIDD_ATTRIBUTES attributes ;
if ( ! HidD_GetAttributes ( device_object . get ( ) , & attributes ) ) {
spdlog : : error ( L " Could get Hid attributes; Device: {} " , instance_path ) ;
return { } ;
}
std : : wstring buffer ;
buffer . resize ( 127 * sizeof WCHAR ) ;
res . name = ( HidD_GetProductString ( device_object . get ( ) , buffer . data ( ) , static_cast < ULONG > ( sizeof ( WCHAR ) * buffer . size ( ) ) )
? buffer
: L " " ) ;
2021-10-22 16:07:08 +00:00
// Valve emulated gamepad PID/VID; mirrord by ViGEm
2021-10-30 00:06:23 +00:00
if ( attributes . VendorID = = 0x28de /* && attributes.ProductID == 0x11FF*/ ) {
2021-10-22 16:07:08 +00:00
res . name = std : : wstring ( L " ViGEm Emulated: " ) + res . name ;
}
2021-10-20 19:48:19 +00:00
res . base_container_device_instance_path = BaseContainerDeviceInstancePath ( instance_path ) ;
res . gaming_device = IsGamingDevice ( attributes , capabilities ) ;
2021-10-22 23:29:59 +00:00
res . vendor_id = attributes . VendorID ;
res . product_id = attributes . ProductID ;
2021-10-20 19:48:19 +00:00
return res ;
}
bool HidHide : : DevicePresent ( const DeviceInstancePath & dev )
{
DEVINST dev_inst { } ;
if (
const auto result = CM_Locate_DevNodeW ( & dev_inst , const_cast < DEVINSTID_W > ( dev . c_str ( ) ) , CM_LOCATE_DEVNODE_NORMAL ) ;
( CR_NO_SUCH_DEVNODE = = result ) | | ( CR_SUCCESS = = result ) ) {
return ( CR_SUCCESS = = result ) ;
}
spdlog : : error ( L " Couldn't determine if device \" {} \" is present " , dev ) ;
return false ;
}
std : : filesystem : : path HidHide : : SymbolicLink ( GUID const & interface_guid , DeviceInstancePath const & instance_path )
{
// Ask the device for the device interface
// Note that this call will succeed, whether or not the interface is present, but the iterator will have no entries, when the device interface isn't supported
const SetupDiDestroyDeviceInfoListPtr handle ( SetupDiGetClassDevsW ( & interface_guid , instance_path . c_str ( ) , nullptr , DIGCF_DEVICEINTERFACE ) , & SetupDiDestroyDeviceInfoList ) ;
if ( INVALID_HANDLE_VALUE = = handle . get ( ) ) {
spdlog : : error ( L " Device Handle invalid; device: {} " , instance_path ) ;
return { } ;
}
// Is the interface supported ?
SP_DEVICE_INTERFACE_DATA device_interface_data ;
device_interface_data . cbSize = sizeof ( device_interface_data ) ;
if ( ! SetupDiEnumDeviceInterfaces ( handle . get ( ) , nullptr , & interface_guid , 0 , & device_interface_data ) ) {
if ( ERROR_NO_MORE_ITEMS ! = GetLastError ( ) ) {
spdlog : : error ( L " Couldn't get Device interfaces; device: {} " , instance_path ) ;
return { } ;
}
return { } ;
}
// Determine the buffer length needed
DWORD needed { } ;
if ( ! SetupDiGetDeviceInterfaceDetailW ( handle . get ( ) , & device_interface_data , nullptr , 0 , & needed , nullptr ) & & ERROR_INSUFFICIENT_BUFFER ! = GetLastError ( ) ) {
spdlog : : error ( L " Couldn't get Device interface details; device: {} " , instance_path ) ;
return { } ;
}
std : : vector < BYTE > buffer ( needed ) ;
// Acquire the detailed data containing the symbolic link (aka. device path)
auto & [ cbSize , DevicePath ] { * reinterpret_cast < PSP_DEVICE_INTERFACE_DETAIL_DATA_W > ( buffer . data ( ) ) } ;
cbSize = sizeof ( SP_DEVICE_INTERFACE_DETAIL_DATA_W ) ;
if ( ! SetupDiGetDeviceInterfaceDetailW ( handle . get ( ) , & device_interface_data , reinterpret_cast < PSP_DEVICE_INTERFACE_DETAIL_DATA_W > ( buffer . data ( ) ) , static_cast < DWORD > ( buffer . size ( ) ) , nullptr , nullptr ) ) {
spdlog : : error ( L " Couldn't get Device interface details; device: {} " , instance_path ) ;
return { } ;
}
return { std : : wstring ( DevicePath ) } ;
}
HidHide : : DeviceInstancePath HidHide : : BaseContainerDeviceInstancePath ( DeviceInstancePath const & device_instance_path )
{
const GUID base_container_id ( BaseContainerId ( device_instance_path ) ) ;
if ( ( GUID_NULL = = base_container_id ) | | ( GUID_CONTAINER_ID_SYSTEM = = base_container_id ) )
return ( std : : wstring { } ) ;
for ( auto it { device_instance_path } ; ; ) {
if ( const auto device_instance_path_parent = DeviceInstancePathParent ( it ) ; ( base_container_id = = BaseContainerId ( device_instance_path_parent ) ) )
it = device_instance_path_parent ;
else
return ( it ) ;
}
}
GUID HidHide : : BaseContainerId ( DeviceInstancePath const & device_instance_path )
{
// Bail out when the device instance path is empty
if ( device_instance_path . empty ( ) )
return ( GUID_NULL ) ;
DEVINST devInst { } ;
DEVPROPTYPE devPropType { } ;
GUID buffer { } ;
ULONG needed { sizeof ( buffer ) } ;
if ( const auto result = CM_Locate_DevNodeW ( & devInst , const_cast < DEVINSTID_W > ( device_instance_path . c_str ( ) ) , CM_LOCATE_DEVNODE_PHANTOM ) ; ( CR_SUCCESS ! = result ) ) {
spdlog : : error ( L " Couldn't locate device DevNode; Device {}; Code: {} " , device_instance_path , result ) ;
return { } ;
}
if ( const auto result = CM_Get_DevNode_PropertyW ( devInst , & DEVPKEY_Device_ContainerId , & devPropType , reinterpret_cast < PBYTE > ( & buffer ) , & needed , 0 ) ; ( CR_SUCCESS ! = result ) ) {
// Bail out when the container id property isn't present
if ( CR_NO_SUCH_VALUE = = result ) {
return ( GUID_NULL ) ;
}
spdlog : : error ( L " Couldn't locate device DevNode Property; Device {}; Code: {} " , device_instance_path , result ) ;
return { } ;
}
if ( DEVPROP_TYPE_GUID ! = devPropType ) {
spdlog : : error ( L " Device Prop is not GUID; Device {} " , device_instance_path ) ;
return { } ;
}
return ( buffer ) ;
}
HidHide : : DeviceInstancePath HidHide : : DeviceInstancePathParent ( DeviceInstancePath const & device_instance_path )
{
DEVINST dev_inst { } ;
DEVPROPTYPE dev_prop_type { } ;
DEVINST dev_inst_parent { } ;
std : : wstring res ;
res . resize ( UNICODE_STRING_MAX_CHARS ) ;
ULONG needed { static_cast < ULONG > ( res . size ( ) ) } ;
if ( const auto result = CM_Locate_DevNodeW ( & dev_inst , const_cast < DEVINSTID_W > ( device_instance_path . c_str ( ) ) , CM_LOCATE_DEVNODE_PHANTOM ) ; ( CR_SUCCESS ! = result ) ) {
spdlog : : error ( L " Couldn't locate device DevNode; Device {}; Code: {} " , device_instance_path , result ) ;
return { } ;
}
if ( const auto result = CM_Get_Parent ( & dev_inst_parent , dev_inst , 0 ) ; ( CR_SUCCESS ! = result ) ) {
spdlog : : error ( L " Couldn't get device Parent; Device {}; Code: {} " , device_instance_path , result ) ;
return { } ;
}
if ( const auto result = CM_Get_DevNode_PropertyW ( dev_inst_parent , & DEVPKEY_Device_InstanceId , & dev_prop_type , reinterpret_cast < PBYTE > ( res . data ( ) ) , & needed , 0 ) ; ( CR_SUCCESS ! = result ) ) {
spdlog : : error ( L " Couldn't locate device DevNode Property; Device {}; Code: {} " , device_instance_path , result ) ;
return { } ;
}
if ( DEVPROP_TYPE_STRING ! = dev_prop_type ) {
spdlog : : error ( L " Device Prop is not STRING; Device {} " , device_instance_path ) ;
return { } ;
}
return res ;
}
bool HidHide : : IsGamingDevice ( const HIDD_ATTRIBUTES & attributes , const HIDP_CAPS & capabilities )
{
return (
// 0x28DE 0x1142 = Valve Corporation Steam Controller
// keep them for now
/* ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1142)) || */
( 0x05 = = capabilities . UsagePage ) | | ( 0x01 = = capabilities . UsagePage ) & & ( ( 0x04 = = capabilities . Usage ) | | ( 0x05 = = capabilities . Usage ) ) ) ;
}