diff --git a/src/spice2x/CMakeLists.txt b/src/spice2x/CMakeLists.txt index d2713f1..d726cf1 100644 --- a/src/spice2x/CMakeLists.txt +++ b/src/spice2x/CMakeLists.txt @@ -441,6 +441,7 @@ set(SOURCE_FILES ${SOURCE_FILES} games/scotto/io.cpp games/drs/drs.cpp games/drs/io.cpp + games/drs/rgb_cam.cpp games/we/we.cpp games/we/io.cpp games/we/touchpanel.cpp diff --git a/src/spice2x/games/drs/drs.cpp b/src/spice2x/games/drs/drs.cpp index 91df3a0..585e2b1 100644 --- a/src/spice2x/games/drs/drs.cpp +++ b/src/spice2x/games/drs/drs.cpp @@ -8,6 +8,7 @@ #include "util/detour.h" #include "util/logging.h" #include "util/memutils.h" +#include "rgb_cam.h" #pragma pack(push) @@ -150,6 +151,7 @@ namespace games::drs { char DRS_TAPELED[DRS_TAPELED_COLS_TOTAL][3] {}; bool DISABLE_TOUCH = false; bool TRANSPOSE_TOUCH = false; + bool RGB_CAMERA_HOOK = false; std::vector TOUCH_EVENTS; @@ -313,6 +315,10 @@ namespace games::drs { } else { log_info("drs", "no native input method detected"); } + + if (RGB_CAMERA_HOOK) { + init_rgb_camera_hook(); + } } void DRSGame::detach() { diff --git a/src/spice2x/games/drs/drs.h b/src/spice2x/games/drs/drs.h index ec2f202..42fead3 100644 --- a/src/spice2x/games/drs/drs.h +++ b/src/spice2x/games/drs/drs.h @@ -31,6 +31,7 @@ namespace games::drs { extern std::vector TOUCH_EVENTS; extern bool DISABLE_TOUCH; extern bool TRANSPOSE_TOUCH; + extern bool RGB_CAMERA_HOOK; void fire_touches(drs_touch_t *events, size_t event_count); void start_touch(); diff --git a/src/spice2x/games/drs/rgb_cam.cpp b/src/spice2x/games/drs/rgb_cam.cpp new file mode 100644 index 0000000..2117ae5 --- /dev/null +++ b/src/spice2x/games/drs/rgb_cam.cpp @@ -0,0 +1,170 @@ +#include "games/drs/rgb_cam.h" + +// set version to Windows 7 to enable Media Foundation functions +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0601 + +// turn on C-style COM objects +#define CINTERFACE + +#include +#include +#include + +#include "avs/game.h" +#include "cfg/configurator.h" +#include "hooks/cfgmgr32hook.h" +#include "util/detour.h" +#include "util/memutils.h" +#include "util/logging.h" +#include "util/utils.h" + +static VTBL_TYPE(IMFActivate, GetAllocatedString) GetAllocatedString_orig = nullptr; +static decltype(MFEnumDeviceSources) *MFEnumDeviceSources_orig = nullptr; + +namespace games::drs { + + /* + * Camera related stuff + */ + static std::wstring CAMERA0_ID; + + static HRESULT WINAPI GetAllocatedString_hook(IMFActivate* This, REFGUID guidKey, LPWSTR *ppwszValue, + UINT32 *pcchLength) + { + // call the original + HRESULT result = GetAllocatedString_orig(This, guidKey, ppwszValue, pcchLength); + + // log the cam name + log_misc("drs::rgbcam", "obtained {}", ws2s(*ppwszValue)); + + // try first camera + wchar_t *pwc = nullptr; + if (CAMERA0_ID.length() == 23) { + pwc = wcsstr(*ppwszValue, CAMERA0_ID.c_str()); + } + + // check if camera could be identified + if (pwc) { + + // fake the USB IDs + pwc[4] = L'2'; + pwc[5] = L'8'; + pwc[6] = L'8'; + pwc[7] = L'c'; + + pwc[13] = L'0'; + pwc[14] = L'0'; + pwc[15] = L'0'; + pwc[16] = L'2'; + + pwc[21] = L'0'; + pwc[22] = L'0'; + + log_misc("drs::rgbcam", "replaced {}", ws2s(*ppwszValue)); + } + + // return original result + return result; + } + + static void hook_camera(std::wstring camera_id, std::string camera_instance) { + // don't hook if camera 0 is already hooked + if (CAMERA0_ID.length() > 0) { + return; + } + + // save the camera ID + CAMERA0_ID = camera_id; + + // cfgmgr hook + CFGMGR32_HOOK_SETTING camera_setting; + camera_setting.device_instance = 0xDEADBEEF; + camera_setting.parent_instance = ~camera_setting.device_instance; + camera_setting.device_id = "USB\\VEN_8086&DEV_8C2D"; + camera_setting.device_node_id = "USB\\VID_288C&PID_0002&MI_00\\?&????????&?&????"; + if (camera_instance.length() == 17) { + for (int i = 0; i < 17; i++) { + camera_setting.device_node_id[28 + i] = camera_instance[i]; + } + } + cfgmgr32hook_add(camera_setting); + + log_info("drs::rgbcam", "hooked camera @ {}", ws2s(camera_id)); + } + + static void hook_camera_vtable(IMFActivate *camera) { + + // hook allocated string method for camera identification + memutils::VProtectGuard camera_guard(camera->lpVtbl); + camera->lpVtbl->GetAllocatedString = GetAllocatedString_hook; + } + + static HRESULT WINAPI MFEnumDeviceSources_hook(IMFAttributes *pAttributes, IMFActivate ***pppSourceActivate, + UINT32 *pcSourceActivate) { + + // call original function + HRESULT result_orig = MFEnumDeviceSources_orig(pAttributes, pppSourceActivate, pcSourceActivate); + + // check for capture devices + if (FAILED(result_orig) || !*pcSourceActivate) { + return result_orig; + } + + // get camera + IMFActivate *camera = **pppSourceActivate; + + // save original method for later use + if (GetAllocatedString_orig == nullptr) { + GetAllocatedString_orig = camera->lpVtbl->GetAllocatedString; + } + + // hook allocated string method for camera identification + hook_camera_vtable(camera); + + // get camera link + LPWSTR camera_link_lpwstr; + UINT32 camera_link_length; + if (SUCCEEDED(GetAllocatedString_orig( + camera, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, + &camera_link_lpwstr, + &camera_link_length))) { + + // cut name to make ID + std::wstring camera_link_ws = std::wstring(camera_link_lpwstr); + std::wstring camera_id = camera_link_ws.substr(8, 23); + + log_info("drs::rgbcam", "found video capture device: {}", ws2s(camera_link_ws)); + + // only support cameras that start with \\?\usb + if (!string_begins_with(camera_link_ws, L"\\\\?\\usb")) { + return result_orig; + } + + // get camera instance + std::string camera_link = ws2s(camera_link_ws); + std::string camera_instance = camera_link.substr(32, 17); + + // hook the camera + hook_camera(camera_id, camera_instance); + } else { + log_warning("drs::rgbcam", "failed to open camera"); + } + + // return result + return result_orig; + } + + void init_rgb_camera_hook() { + log_info("drs::rgbcam", "initializing camera hook"); + + cfgmgr32hook_init(avs::game::DLL_INSTANCE); + + // camera media framework hook + MFEnumDeviceSources_orig = detour::iat_try( + "MFEnumDeviceSources", MFEnumDeviceSources_hook, avs::game::DLL_INSTANCE); + } +} diff --git a/src/spice2x/games/drs/rgb_cam.h b/src/spice2x/games/drs/rgb_cam.h new file mode 100644 index 0000000..361ba0b --- /dev/null +++ b/src/spice2x/games/drs/rgb_cam.h @@ -0,0 +1,5 @@ +#pragma once + +namespace games::drs { + void init_rgb_camera_hook(); +} \ No newline at end of file diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 3598773..a5d592c 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -1118,6 +1118,9 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::spice2x_DRSTransposeTouch].value_bool()) { games::drs::TRANSPOSE_TOUCH = true; } + if (options[launcher::Options::DRSRGBCameraHook].value_bool()) { + games::drs::RGB_CAMERA_HOOK = true; + } if (options[launcher::Options::spice2x_AutoCard].is_active()) { const auto text = options[launcher::Options::spice2x_AutoCard].value_text(); if (text == "p1") { diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index 813e1b4..0677711 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -2219,6 +2219,17 @@ static const std::vector OPTION_DEFINITIONS = { .game_name = "DANCERUSH", .category = "Game Options", }, + { + // DRSRGBCameraHook + .title = "DRS RGB Camera Hook", + .name = "drsrgbcamhook", + .desc = "Hook into the RGB camera detection and attempt to use your own non-official webcam. " + "Not every webcam will be compatible with this; YMMV.\n\n" + "Note: this is NOT for the motion camera - you still need a real RealSense camera for that!", + .type = OptionType::Bool, + .game_name = "DANCERUSH", + .category = "Game Options", + }, { // spice2x_IIDXNativeTouch .title = "IIDX Native Touch Handling", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index f0c59c8..bbe1ba5 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -236,6 +236,7 @@ namespace launcher { spice2x_IIDXWindowedTDJ, spice2x_DRSDisableTouch, spice2x_DRSTransposeTouch, + DRSRGBCameraHook, spice2x_IIDXNativeTouch, spice2x_IIDXNoSub, spice2x_IIDXEmulateSubscreenKeypadTouch,