|
4 | 4 | #include <winioctl.h> |
5 | 5 | #include <hidsdi.h> |
6 | 6 |
|
| 7 | +#include <queue> |
| 8 | + |
7 | 9 | #include "hook_mgr.hpp" |
8 | 10 | #include "plugin.hpp" |
9 | 11 | #include "game_addrs.hpp" |
@@ -116,3 +118,85 @@ class ImpulseVibration : public Hook |
116 | 118 | static ImpulseVibration instance; |
117 | 119 | }; |
118 | 120 | ImpulseVibration ImpulseVibration::instance; |
| 121 | + |
| 122 | +class ControllerHotPlug : public Hook |
| 123 | +{ |
| 124 | + const static int DInputInit_CallbackPtr_Addr = 0x3E10; |
| 125 | + |
| 126 | +public: |
| 127 | + inline static std::mutex mtx; |
| 128 | + inline static std::unique_ptr<std::thread> DeviceEnumerationThreadHandle; |
| 129 | + inline static std::vector<GUID> KnownDevices; |
| 130 | + inline static std::queue<DIDEVICEINSTANCE> NewDevices; |
| 131 | + |
| 132 | + static BOOL __stdcall DInput_EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext) |
| 133 | + { |
| 134 | + std::lock_guard<std::mutex> lock(mtx); |
| 135 | + if (std::find(KnownDevices.begin(), KnownDevices.end(), pdidInstance->guidInstance) == KnownDevices.end()) |
| 136 | + { |
| 137 | + // GUID not found, add to the vector |
| 138 | + KnownDevices.push_back(pdidInstance->guidInstance); |
| 139 | + |
| 140 | + // Add the new device instance to the queue |
| 141 | + NewDevices.push(*pdidInstance); |
| 142 | + } |
| 143 | + |
| 144 | + return DIENUM_CONTINUE; |
| 145 | + } |
| 146 | + |
| 147 | + static void DeviceEnumerationThread() |
| 148 | + { |
| 149 | + SetThreadDescription(GetCurrentThread(), L"DeviceEnumerationThread"); |
| 150 | + |
| 151 | + while (true) |
| 152 | + { |
| 153 | + if (Game::DirectInput8()) |
| 154 | + Game::DirectInput8()->EnumDevices(DI8DEVCLASS_GAMECTRL, DInput_EnumJoysticksCallback, nullptr, DIEDFL_ATTACHEDONLY); |
| 155 | + |
| 156 | + std::this_thread::sleep_for(std::chrono::seconds(2)); // Poll every 2 seconds |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + std::string_view description() override |
| 161 | + { |
| 162 | + return "ControllerHotPlug"; |
| 163 | + } |
| 164 | + |
| 165 | + bool validate() override |
| 166 | + { |
| 167 | + return Settings::ControllerHotPlug; |
| 168 | + } |
| 169 | + |
| 170 | + bool apply() override |
| 171 | + { |
| 172 | + // Patch game to go through our DInput_EnumJoysticksCallback func, so we can learn GUID of any already connected pads |
| 173 | + Memory::VP::Patch(Module::exe_ptr(DInputInit_CallbackPtr_Addr + 1), DInput_EnumJoysticksCallback); |
| 174 | + |
| 175 | + return true; |
| 176 | + } |
| 177 | + |
| 178 | + static ControllerHotPlug instance; |
| 179 | +}; |
| 180 | +ControllerHotPlug ControllerHotPlug::instance; |
| 181 | + |
| 182 | +void DInput_RegisterNewDevices() |
| 183 | +{ |
| 184 | + if (!ControllerHotPlug::DeviceEnumerationThreadHandle) |
| 185 | + { |
| 186 | + ControllerHotPlug::DeviceEnumerationThreadHandle = std::make_unique<std::thread>(ControllerHotPlug::DeviceEnumerationThread); |
| 187 | + ControllerHotPlug::DeviceEnumerationThreadHandle->detach(); |
| 188 | + } |
| 189 | + |
| 190 | + std::lock_guard<std::mutex> lock(ControllerHotPlug::mtx); |
| 191 | + while (!ControllerHotPlug::NewDevices.empty()) |
| 192 | + { |
| 193 | + DIDEVICEINSTANCE deviceInstance = ControllerHotPlug::NewDevices.front(); |
| 194 | + ControllerHotPlug::NewDevices.pop(); |
| 195 | + |
| 196 | + // Tell game about it |
| 197 | + Game::DInput_EnumJoysticksCallback(&deviceInstance, 0); |
| 198 | + |
| 199 | + // Sets some kind of active-device-number to let it work |
| 200 | + *Module::exe_ptr<int>(0x3398D4) = 0; |
| 201 | + } |
| 202 | +} |
0 commit comments