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 <numeric>
# include <spdlog/spdlog.h>
# include <vector>
2022-10-09 22:28:28 +00:00
# include <initguid.h>
2021-10-20 19:48:19 +00:00
// Device configuration related
# include <cfgmgr32.h>
2022-10-02 00:29:37 +00:00
# ifndef WATCHDOG
2021-10-22 16:07:08 +00:00
# include "Overlay.h"
2022-10-02 00:29:37 +00:00
# endif
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>
2022-10-09 22:28:28 +00:00
# include <cguid.h>
# include <atlbase.h>
2021-10-20 19:48:19 +00:00
2022-09-26 01:39:20 +00:00
# include "UnhookUtil.h"
2022-10-09 22:28:28 +00:00
# pragma comment(lib, "Setupapi.lib")
2021-10-20 19:48:19 +00:00
// {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 )
{
2022-09-25 20:07:25 +00:00
steam_path_ = steam_path ;
2022-10-02 00:29:37 +00:00
# ifndef WATCHDOG
2022-09-25 20:07:25 +00:00
enableOverlayElement ( ) ;
2022-10-02 00:29:37 +00:00
# endif
2022-09-24 17:30:23 +00:00
if ( ! Settings : : devices . hideDevices ) {
spdlog : : info ( " Hiding devices is disabled; Not un-patching valve hooks, not looking for HidHide " ) ;
return ;
}
2022-09-25 20:07:25 +00:00
spdlog : : debug ( " Setting up device hiding... " ) ;
2022-09-25 12:46:44 +00:00
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 ) ;
}
}
2022-10-10 18:51:59 +00:00
if ( Settings : : common . extendedLogging ) {
2022-09-20 18:19:01 +00:00
std : : ranges : : for_each ( whitelist , [ ] ( const auto & exe ) {
spdlog : : trace ( L " Whitelisted executable: {} " , exe ) ;
} ) ;
2022-09-20 18:00:50 +00:00
}
2021-10-20 19:48:19 +00:00
setAppWhiteList ( whitelist ) ;
2021-10-22 16:07:08 +00:00
avail_devices_ = GetHidDeviceList ( ) ;
2022-10-10 18:51:59 +00:00
if ( Settings : : common . extendedLogging ) {
2022-09-23 15:06:13 +00:00
std : : ranges : : for_each ( avail_devices_ , [ ] ( const auto & dev ) {
spdlog : : trace ( L " AvailDevice device: {} " , dev . name ) ;
} ) ;
}
2021-10-22 16:07:08 +00:00
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
2022-09-23 15:03:10 +00:00
if ( ! ( dev . vendor_id = = 0x28de & & ( dev . product_id = = 0x11FF | | dev . product_id = = 0x028E ) ) ) {
2021-10-22 23:29:59 +00:00
if ( ! dev . device_instance_path . empty ( ) ) {
blacklisted_devices_ . push_back ( dev . device_instance_path ) ;
}
2022-09-23 15:02:28 +00:00
if ( ! dev . base_container_device_instance_path . empty ( ) ) {
2021-10-22 23:29:59 +00:00
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... " ) ;
2022-10-10 18:51:59 +00:00
if ( Settings : : common . extendedLogging ) {
2022-09-20 18:19:01 +00:00
std : : ranges : : for_each ( blacklisted_devices_ , [ ] ( const auto & dev ) {
spdlog : : trace ( L " Blacklisted device: {} " , dev ) ;
} ) ;
2022-09-20 18:00:50 +00:00
}
2021-10-23 14:03:18 +00:00
}
2021-10-20 19:48:19 +00:00
closeCtrlDevice ( ) ;
2022-09-25 20:07:25 +00:00
device_hiding_setup_ = true ;
2021-10-20 19:48:19 +00:00
}
void HidHide : : disableHidHide ( )
{
2022-04-08 11:11:20 +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...
2022-09-04 11:35:42 +00:00
if ( const auto setupapidll = GetModuleHandle ( L " setupapi.dll " ) ) {
2022-09-26 01:39:20 +00:00
UnhookUtil : : UnPatchHook ( " SetupDiEnumDeviceInfo " , setupapidll ) ;
UnhookUtil : : UnPatchHook ( " SetupDiGetClassDevsW " , setupapidll ) ;
2021-10-22 23:29:59 +00:00
}
2022-09-04 11:35:42 +00:00
if ( const auto hiddll = GetModuleHandle ( L " hid.dll " ) ) {
2022-09-26 01:39:20 +00:00
for ( const auto & name : UnhookUtil : : UNHOOK_BYTES_ORIGINAL_22000 | std : : views : : keys ) {
2022-09-04 11:35:42 +00:00
if ( name . starts_with ( " Hid " ) ) {
2022-09-26 01:39:20 +00:00
UnhookUtil : : UnPatchHook ( name , hiddll ) ;
2022-09-24 16:18:22 +00:00
}
2021-10-22 23:29:59 +00:00
}
}
}
2022-10-02 00:29:37 +00:00
# ifndef WATCHDOG
2021-10-22 16:07:08 +00:00
void HidHide : : enableOverlayElement ( )
{
2022-09-23 18:51:14 +00:00
Overlay : : AddOverlayElem ( [ this ] ( bool window_has_focus , ImGuiID dockspace_id ) {
ImGui : : SetNextWindowDockID ( dockspace_id , ImGuiCond_FirstUseEver ) ;
2022-09-20 18:02:22 +00:00
if ( ImGui : : Begin ( " Hidden Devices " ) ) {
2022-09-25 20:07:25 +00:00
if ( device_hiding_setup_ ) {
if ( window_has_focus & & ( overlay_elem_clock_ . getElapsedTime ( ) . asSeconds ( ) > OVERLAY_ELEM_REFRESH_INTERVAL_S_ ) ) {
// UnPatchValveHooks();
2022-09-20 18:02:22 +00:00
openCtrlDevice ( ) ;
2022-09-25 20:07:25 +00:00
bool hidehide_state_store = hidhide_active_ ;
2022-10-10 18:51:59 +00:00
if ( Settings : : common . extendedLogging ) {
2022-09-25 20:07:25 +00:00
spdlog : : debug ( " Refreshing HID devices " ) ;
2022-09-20 18:02:22 +00:00
}
2022-09-25 20:07:25 +00:00
if ( hidhide_active_ ) {
setActive ( false ) ;
2022-09-20 18:02:22 +00:00
}
2022-09-25 20:07:25 +00:00
avail_devices_ = GetHidDeviceList ( ) ;
2022-10-10 18:51:59 +00:00
if ( Settings : : common . extendedLogging ) {
2022-09-25 20:07:25 +00:00
std : : ranges : : for_each ( avail_devices_ , [ ] ( const auto & dev ) {
spdlog : : trace ( L " AvailDevice device: {} " , dev . name ) ;
2022-09-20 18:19:01 +00:00
} ) ;
2022-09-20 18:02:22 +00:00
}
2022-09-25 20:07:25 +00:00
blacklisted_devices_ = getBlackListDevices ( ) ;
if ( hidehide_state_store & & Settings : : devices . hideDevices ) {
setActive ( true ) ;
}
closeCtrlDevice ( ) ;
overlay_elem_clock_ . restart ( ) ;
}
ImGui : : BeginChild ( " Inner " , { 0.f , ImGui : : GetItemRectSize ( ) . y - 64 } , true ) ;
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_ ) ;
2022-10-10 18:51:59 +00:00
if ( Settings : : common . extendedLogging ) {
2022-09-25 20:07:25 +00:00
std : : ranges : : for_each ( blacklisted_devices_ , [ ] ( const auto & dev ) {
spdlog : : trace ( L " Blacklisted device: {} " , dev ) ;
} ) ;
}
closeCtrlDevice ( ) ;
}
} ) ;
ImGui : : EndChild ( ) ;
} else {
ImGui : : Text ( " Enable \" Hide Devices \" to see a list of gaming-devices " ) ;
}
if ( ImGui : : Checkbox ( " Hide devices " , & Settings : : devices . hideDevices ) ) {
if ( ! device_hiding_setup_ ) {
hideDevices ( steam_path_ ) ;
}
if ( hidhide_active_ ! = Settings : : devices . hideDevices ) {
openCtrlDevice ( ) ;
setActive ( Settings : : devices . hideDevices ) ;
2022-09-20 18:02:22 +00:00
closeCtrlDevice ( ) ;
}
2021-10-22 16:07:08 +00:00
}
}
ImGui : : End ( ) ;
} ) ;
2021-10-20 19:48:19 +00:00
}
2022-10-02 00:29:37 +00:00
# endif
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 ;
2022-10-10 18:51:59 +00:00
if ( Settings : : common . extendedLogging ) {
2022-09-20 18:00:50 +00:00
spdlog : : debug ( " HidHide State set to {} " , 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 ) ) {
2022-09-04 12:12:18 +00:00
spdlog : : error ( " Couldn't determine required HidHide output buffer size; type: {} " , static_cast < int > ( type ) ) ;
2021-10-20 19:48:19 +00:00
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 " " ) ;
2022-09-24 16:18:22 +00:00
for ( size_t i = 0 ; i < res . name . size ( ) ; + + i ) {
if ( res . name [ i ] = = L ' \0 ' ) {
res . name . resize ( i + 1 ) ;
break ;
}
}
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 ) ) ) ;
}