drs: hook down movement, adjust touch hooks (#898)
When Down Motion button is bound, install hooks into the game that detects down movement via patches. When Disable Touch option is enabled, don't install touch hooks.
This commit is contained in:
@@ -520,6 +520,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
|||||||
games/scotto/io.cpp
|
games/scotto/io.cpp
|
||||||
games/drs/drs.cpp
|
games/drs/drs.cpp
|
||||||
games/drs/io.cpp
|
games/drs/io.cpp
|
||||||
|
games/drs/motion_cam.cpp
|
||||||
games/drs/rgb_cam.cpp
|
games/drs/rgb_cam.cpp
|
||||||
games/we/we.cpp
|
games/we/we.cpp
|
||||||
games/we/io.cpp
|
games/we/io.cpp
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/precise_timer.h"
|
#include "util/precise_timer.h"
|
||||||
#include "util/memutils.h"
|
#include "util/memutils.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "motion_cam.h"
|
||||||
#include "rgb_cam.h"
|
#include "rgb_cam.h"
|
||||||
|
|
||||||
#pragma pack(push)
|
#pragma pack(push)
|
||||||
@@ -276,6 +278,7 @@ namespace games::drs {
|
|||||||
void DRSGame::attach() {
|
void DRSGame::attach() {
|
||||||
Game::attach();
|
Game::attach();
|
||||||
|
|
||||||
|
if (!DISABLE_TOUCH) {
|
||||||
// TouchSDK hooks
|
// TouchSDK hooks
|
||||||
detour::iat("??0TouchSDK@@QEAA@XZ",
|
detour::iat("??0TouchSDK@@QEAA@XZ",
|
||||||
(void *) &TouchSDK_Constructor, avs::game::DLL_INSTANCE);
|
(void *) &TouchSDK_Constructor, avs::game::DLL_INSTANCE);
|
||||||
@@ -312,12 +315,13 @@ namespace games::drs {
|
|||||||
detour::iat("?InitTouch@TouchSDK@@QEAAHPEAU_DeviceInfo@@HP6AXU2@PEBU_TouchPointData@@HHPEBX@ZP6AX1_N3@ZPEAX@Z",
|
detour::iat("?InitTouch@TouchSDK@@QEAAHPEAU_DeviceInfo@@HP6AXU2@PEBU_TouchPointData@@HHPEBX@ZP6AX1_N3@ZPEAX@Z",
|
||||||
(void *) &TouchSDK_InitTouch, avs::game::DLL_INSTANCE);
|
(void *) &TouchSDK_InitTouch, avs::game::DLL_INSTANCE);
|
||||||
|
|
||||||
if (!DISABLE_TOUCH) {
|
|
||||||
start_touch();
|
start_touch();
|
||||||
} else {
|
} else {
|
||||||
log_info("drs", "no native input method detected");
|
log_info("drs", "touch input for dance floor disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init_down_motion_hook();
|
||||||
|
|
||||||
if (RGB_CAMERA_HOOK) {
|
if (RGB_CAMERA_HOOK) {
|
||||||
init_rgb_camera_hook();
|
init_rgb_camera_hook();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ std::vector<Button> &games::drs::get_buttons() {
|
|||||||
"P2 Up",
|
"P2 Up",
|
||||||
"P2 Down",
|
"P2 Down",
|
||||||
"P2 Left",
|
"P2 Left",
|
||||||
"P2 Right"
|
"P2 Right",
|
||||||
|
"Down Motion"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +31,8 @@ std::vector<Button> &games::drs::get_buttons() {
|
|||||||
std::string games::drs::get_buttons_help() {
|
std::string games::drs::get_buttons_help() {
|
||||||
// keep to max 100 characters wide
|
// keep to max 100 characters wide
|
||||||
return
|
return
|
||||||
"Motion camera required for DOWN movement.\n"
|
"Touchscreen supported for dance floor.\n"
|
||||||
"Touchscreen supported for dance floor."
|
"Down Motion applies to both players."
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ namespace games::drs {
|
|||||||
P2_Up,
|
P2_Up,
|
||||||
P2_Down,
|
P2_Down,
|
||||||
P2_Left,
|
P2_Left,
|
||||||
P2_Right
|
P2_Right,
|
||||||
|
DownMotion
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#include "games/drs/motion_cam.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "avs/game.h"
|
||||||
|
#include "util/detour.h"
|
||||||
|
#include "util/logging.h"
|
||||||
|
#include "util/sigscan.h"
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
namespace games::drs {
|
||||||
|
|
||||||
|
// the game reads down movement from a depth camera whose driver is compiled into the game DLL,
|
||||||
|
// so there is no import to replace.
|
||||||
|
//
|
||||||
|
// with no camera the tracked height never changes, so the motion state never leaves "still"
|
||||||
|
// and DOWN notes can never be hit.
|
||||||
|
//
|
||||||
|
// rather than answer CInputManager::IsDown, the button drives the same state pair the camera
|
||||||
|
// would; that leaves the game's own edge test untouched and keeps every note in a frame seeing
|
||||||
|
// one answer.
|
||||||
|
static int32_t *MOTION_STATE = nullptr;
|
||||||
|
static int32_t *MOTION_STATE_PREV = nullptr;
|
||||||
|
static constexpr int32_t MOTION_STATE_STILL = 1;
|
||||||
|
static constexpr int32_t MOTION_STATE_DOWN = 2;
|
||||||
|
|
||||||
|
static void (*UpdateMotion_orig)() = nullptr;
|
||||||
|
static bool DOWN_MOTION_HELD = false;
|
||||||
|
|
||||||
|
static void InputManager_UpdateMotion() {
|
||||||
|
UpdateMotion_orig();
|
||||||
|
|
||||||
|
auto &buttons = get_buttons();
|
||||||
|
const bool held = GameAPI::Buttons::getState(RI_MGR, buttons.at(Buttons::DownMotion));
|
||||||
|
|
||||||
|
// a state of DOWN only counts as movement when the frame before it was something else
|
||||||
|
if (held) {
|
||||||
|
*MOTION_STATE_PREV = DOWN_MOTION_HELD ? MOTION_STATE_DOWN : MOTION_STATE_STILL;
|
||||||
|
*MOTION_STATE = MOTION_STATE_DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
DOWN_MOTION_HELD = held;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_down_motion_hook() {
|
||||||
|
// only hook motion camera if the user has down motion button bound to something
|
||||||
|
auto &buttons = get_buttons();
|
||||||
|
if (!buttons.at(Buttons::DownMotion).isSet()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CInputManager::IsDown - read the state pair out of its two operands.
|
||||||
|
// Identical in game versions 2020121400 and 2022121400 / 2024120300:
|
||||||
|
//
|
||||||
|
// 83 3D xx xx xx xx 02 cmp dword [rip+prev], 2 ; state one frame ago
|
||||||
|
// 74 0C je FALSE ; already down, so no new edge
|
||||||
|
// 83 3D xx xx xx xx 02 cmp dword [rip+cur], 2 ; state this frame
|
||||||
|
// 75 03 jne FALSE ; not down
|
||||||
|
// B0 01 mov al, 1 ; went down on this frame
|
||||||
|
// C3 ret
|
||||||
|
// 32 C0 FALSE: xor al, al
|
||||||
|
// C3 ret
|
||||||
|
auto is_down = reinterpret_cast<uint8_t *>(find_pattern(
|
||||||
|
avs::game::DLL_INSTANCE,
|
||||||
|
"833D0000000002740C833D00000000027503B001C332C0C3",
|
||||||
|
"XX????XXXXX????XXXXXXXXX",
|
||||||
|
0, 0));
|
||||||
|
|
||||||
|
if (is_down == nullptr) {
|
||||||
|
log_warning("drs", "motion sensor state not found, DOWN motion button unavailable");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rip-relative displacements resolve against the end of their own instruction, and each
|
||||||
|
// cmp above is 7 bytes with its displacement 4 bytes in: the first ends at +7 with its
|
||||||
|
// displacement at +2, the second starts at +9 so it ends at +16 with its own at +11.
|
||||||
|
// read signed, since a target sitting below the instruction encodes as negative.
|
||||||
|
MOTION_STATE_PREV = (int32_t *) (is_down + 7 + *(int32_t *) (is_down + 2));
|
||||||
|
MOTION_STATE = (int32_t *) (is_down + 16 + *(int32_t *) (is_down + 11));
|
||||||
|
|
||||||
|
// CInputManager::UpdateMotion is hooked for the sole purpose of reading the Down Motion
|
||||||
|
// button mapping frequently enough so that the rising edge can be detected.
|
||||||
|
//
|
||||||
|
// Matched over the prologue up to the camera read; the wildcards are a float and a callee:
|
||||||
|
//
|
||||||
|
// 48 83 EC 38 sub rsp, 0x38
|
||||||
|
// 0F 29 74 24 20 movaps [rsp+0x20], xmm6
|
||||||
|
// F3 0F 10 35 xx xx xx xx movss xmm6, [rip+height] ; reading as of last frame
|
||||||
|
// E8 xx xx xx xx call GetPlayVideoProcess
|
||||||
|
// 48 8B C8 mov rcx, rax
|
||||||
|
// 48 8B 10 mov rdx, [rax]
|
||||||
|
// FF 52 60 call [rdx+0x60] ; reading off the newest frame
|
||||||
|
auto update = reinterpret_cast<void *>(find_pattern(
|
||||||
|
avs::game::DLL_INSTANCE,
|
||||||
|
"4883EC380F29742420F30F103500000000E800000000488BC8488B10FF5260",
|
||||||
|
"XXXXXXXXXXXXX????X????XXXXXXXXX",
|
||||||
|
0, 0));
|
||||||
|
|
||||||
|
if (update == nullptr ||
|
||||||
|
!detour::trampoline_try(
|
||||||
|
update, (void *) InputManager_UpdateMotion, (void **) &UpdateMotion_orig)) {
|
||||||
|
log_warning("drs", "motion update not hooked, DOWN motion button unavailable");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("drs", "hooked DOWN motion at +{:#x}",
|
||||||
|
(uintptr_t) ((uint8_t *) update - (uint8_t *) avs::game::DLL_INSTANCE));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace games::drs {
|
||||||
|
void init_down_motion_hook();
|
||||||
|
}
|
||||||
@@ -1607,6 +1607,7 @@ typedef enum SPICE_SDK_DANCERUSH_BUTTONS {
|
|||||||
DRS_Button_P2_Down,
|
DRS_Button_P2_Down,
|
||||||
DRS_Button_P2_Left,
|
DRS_Button_P2_Left,
|
||||||
DRS_Button_P2_Right,
|
DRS_Button_P2_Right,
|
||||||
|
DRS_Button_DownMotion,
|
||||||
} SPICE_SDK_DANCERUSH_BUTTONS;
|
} SPICE_SDK_DANCERUSH_BUTTONS;
|
||||||
|
|
||||||
typedef enum SPICE_SDK_DANCERUSH_LIGHTS {
|
typedef enum SPICE_SDK_DANCERUSH_LIGHTS {
|
||||||
|
|||||||
Reference in New Issue
Block a user