diff --git a/bindings.ini b/bindings.ini index d6dc8d4357..d76797643d 100644 --- a/bindings.ini +++ b/bindings.ini @@ -30,6 +30,7 @@ D = LS-Right CapsLock = Modifier Ctrl = LS C = LS +B = RS LClick = RT RClick = LT Mouse5 = B @@ -53,6 +54,8 @@ Up = RS-Up Down = RS-Down Left = RS-Left Right = RS-Right +B = RS +H = RS [545107D1 Vehicle - Saints Row 1] W = A @@ -61,9 +64,11 @@ A = LS-Left D = LS-Right Q = LB E = RB -CapsLock = Modifier +CapsLock = RS Ctrl = LS C = LS +B = RS +H = RS LClick = RT RClick = LT Mouse5 = B @@ -436,6 +441,7 @@ LClick = RT RClick = RS F = LB Q = B +Mouse5 = B E = Y R = A Space = X @@ -470,6 +476,8 @@ V = RS 3 = Up 4 = Down LShift = B +Mouse5 = A +Mouse4 = B Tab = Back Enter = Start Up = RS-Up @@ -487,6 +495,7 @@ LClick = RT RClick = RS F = LB Q = B +Mouse5 = B E = Y R = A Space = X @@ -513,6 +522,7 @@ LClick = RT RClick = RS F = LB Q = B +Mouse5 = B E = Y Space = LT C = LS-Down @@ -546,6 +556,7 @@ RClick = LT MWheelDown = Left MWheelUp = Right Q = B +Mouse5 = B 1 = Left 2 = Right 3 = Up @@ -570,6 +581,7 @@ RClick = LT MWheelDown = Left MWheelUp = Right Q = B +Mouse5 = B Z = LB C = RB 1 = Left diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 31cefe226d..12e9eac18f 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -1700,12 +1700,15 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, uint32_t min_float_addr_lis1; uint32_t write_max_value1; // making it same as on-foot causes the camera // to clip. + uint32_t havok_value_address; + uint32_t fps_1over60_value; }; std::vector supported_builds = { // TU1 Release build {0x82050304, 0x7361696E, 0x60000000, 0x8249db00, 0x8249dd28, 0x8249dd50, 0x82079cbc, 0x82195324, 0x8225BD8C, 0x8211D604, 0x82772D90, 0x8211FC8C, - 0x82772DB0, 0xC108C88C, 0xC00BC88C, 0xC0C8B850, 0x8208C88C}, + 0x82772DB0, 0xC108C88C, 0xC00BC88C, 0xC0C8B850, 0x8208C88C, 0x835F2684, + 0x3c888889}, }; for (auto& build : supported_builds) { auto* test_addr = (xe::be*)module->memory()->TranslateVirtual( @@ -1725,6 +1728,8 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, build.havok_write_frametime_address2) { patch_addr(build.havok_write_frametime_address1, build.beNOP); patch_addr(build.havok_write_frametime_address2, build.beNOP); + // in case user boots game with inputs other than winkey. + patch_addr(build.havok_value_address, build.fps_1over60_value); } if (cvars::sr_better_drive_cam && build.vehicle_rotationXWrite_addr_start) { @@ -1794,6 +1799,8 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, uint32_t Vehicle_RotationXWrite_addr2; // Handbrake. uint32_t aim_assist_xbtl; // File declares aim_assist values. uint32_t havok_write_frametime_address1; + uint32_t havok_value_address; + uint32_t fps_1over60_value; }; std::vector supported_builds = { @@ -1803,7 +1810,8 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, 0x8247832c, 0x821a4b84, 0x824e6a68, 0x824e7f50, 0x824e6b8c, 0x82478934, 0x824e6b2c, 0x82478330, 0x82478094, 0x821a4b88, 0x82B7A5AC, 0x82B7A5A8, 0x82B77C04, 0x82B77C08, 0x82B77C0C, 0x82B77C08, 0x82B77C10, 0x821A4D20, - 0x821A4D18, 0x821a1f74, 0x821A2A2C, 0x820A61C0, 0x8221CEAC}, + 0x821A4D18, 0x821a1f74, 0x821A2A2C, 0x820A61C0, 0x8221CEAC, 0x837DB620, + 0x3c888889}, }; for (auto& build : supported_builds) { @@ -1856,6 +1864,8 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, } if (cvars::sr_havok_fix_frametime && build.havok_write_frametime_address1) patch_addr(build.havok_write_frametime_address1, build.beNOP); + // in case user boots game with inputs other than winkey. + patch_addr(build.havok_value_address, build.fps_1over60_value); break; } } diff --git a/src/xenia/hid/winkey/hookables/SaintsRow1.cc b/src/xenia/hid/winkey/hookables/SaintsRow1.cc index 430914f9c4..0cbbf25d7d 100644 --- a/src/xenia/hid/winkey/hookables/SaintsRow1.cc +++ b/src/xenia/hid/winkey/hookables/SaintsRow1.cc @@ -27,9 +27,11 @@ DECLARE_double(sensitivity); DECLARE_bool(invert_y); DECLARE_bool(invert_x); DECLARE_double(right_stick_hold_time_workaround); -DECLARE_bool(sr_havok_fix_frametime) +DECLARE_bool(sr_havok_fix_frametime); +DECLARE_bool(swap_wheel); +DECLARE_double(menu_sensitivity); - const uint32_t kTitleIdSaintsRow1 = 0x545107D1; +const uint32_t kTitleIdSaintsRow1 = 0x545107D1; namespace xe { namespace hid { @@ -38,8 +40,13 @@ struct GameBuildAddrs { const char* title_version; uint32_t x_address; uint32_t y_address; + uint32_t fineaim_y_address; + uint32_t map_x_address; + uint32_t map_zoom_address; + uint32_t pause_screen_section_address; uint32_t vehicle_address; uint32_t weapon_wheel_address; + uint32_t weapon_wheel_slot_address; uint32_t menu_status_address; uint32_t havok_frametime_address; uint32_t current_frametime_address; // x_axis_addition = @@ -50,16 +57,16 @@ struct GameBuildAddrs { uint32_t isfirstperson_address; // Unused game camera mode ; toggleable with // a console command mostly usable with // Tervel's sr1fineaim plugin. - uint32_t fineaim_y_address; + uint32_t slow_pan_horizontal_multiplier_address; }; std::map supported_builds{ {SaintsRow1Game::GameBuild::Unknown, {" ", NULL, NULL}}, {SaintsRow1Game::GameBuild::SaintsRow1_TU1, - {"1.0.1", 0x827f9af8, 0x827F9B00, 0x82932407, 0x8283CA7B, 0x835F27A3, - 0x835F2684, 0x827CA69C, 0x827F9AD8, 0x827F9B58, 0x827F99C7, 0x827F9BA4, - 0x827F956C}}}; + {"1.0.1", 0x827f9af8, 0x827F9B00, 0x827F9BA4, 0x835F2B80, 0x827CF9CC, + 0x835F279B, 0x82932407, 0x8283CA7B, 0x835F2883, 0x835F27A3, 0x835F2684, + 0x827CA69C, 0x827F9AD8, 0x827F9B58, 0x827F99C7, 0x827F956C}}}; SaintsRow1Game::~SaintsRow1Game() = default; @@ -149,7 +156,7 @@ bool SaintsRow1Game::DoHooks(uint32_t user_index, RawInputState& input_state, now - last_movement_time_y_) .count(); - if (!(inFirstPerson() && isTervelPlugin())) { + if (!(inFirstPerson() && isTervelPlugin()) && !inMapScreen()) { // Declare static variables for last deltas static int last_x_delta = 0; static int last_y_delta = 0; @@ -188,22 +195,15 @@ bool SaintsRow1Game::DoHooks(uint32_t user_index, RawInputState& input_state, out_state->gamepad.thumb_ry = SHRT_MAX; } } - - // Return true if either X or Y delta is non-zero or if within the hold time - if (input_state.mouse.x_delta == 0 && input_state.mouse.y_delta == 0 && - elapsed_x >= hold_time && elapsed_y >= hold_time) { - return false; - } } - // Stop mouse this late here to allow RS in menus and frametime fix to apply. - if (isPaused()) return false; - XThread* current_thread = XThread::GetCurrentThread(); - - if (!current_thread) { + if ((!input_state.mouse.x_delta && !input_state.mouse.y_delta && + !input_state.mouse.wheel_delta)) return false; - } + if (inMapScreen()) MapCursor(input_state); + + if (isPaused()) return false; xe::be* addition_x = kernel_memory()->TranslateVirtual*>( supported_builds[game_build_].x_address); @@ -275,10 +275,13 @@ bool SaintsRow1Game::DoHooks(uint32_t user_index, RawInputState& input_state, degree_y += delta_y; *fine_aim_y = DegreetoRadians(degree_y); } + if (*wheel_status == 1) WeaponWheelScrollWheel(input_state); return true; } void SaintsRow1Game::FixHavokFrameTime(float frametime) { + XThread* current_thread = XThread::GetCurrentThread(); + if (!current_thread) return; xe::be* havok_frametime = kernel_memory()->TranslateVirtual*>( supported_builds[game_build_].havok_frametime_address); @@ -337,8 +340,77 @@ bool SaintsRow1Game::isPaused() { return false; } +void SaintsRow1Game::WeaponWheelScrollWheel(RawInputState& input_state) { + auto* weapon_slot = kernel_memory()->TranslateVirtual( + supported_builds[game_build_].weapon_wheel_slot_address); + if (input_state.mouse.wheel_delta) { + int16_t slot = static_cast(*weapon_slot); + + // one scroll of the wheel_delta seems to always return 120? + if (!cvars::swap_wheel) + slot += static_cast(input_state.mouse.wheel_delta / 120); + else + slot -= static_cast(input_state.mouse.wheel_delta / 120); + + slot = slot % 8; + + *weapon_slot = static_cast(slot); + } +} + +bool SaintsRow1Game::inMapScreen() { + auto* pause_screen = kernel_memory()->TranslateVirtual( + supported_builds[game_build_].pause_screen_section_address); + + if (*pause_screen == 26 && isPaused()) + return true; + else + return false; +} + +void SaintsRow1Game::MapCursor(RawInputState& input_state) { + xe::be* map_x_be = kernel_memory()->TranslateVirtual*>( + supported_builds[game_build_].map_x_address); + + xe::be* map_y_be = kernel_memory()->TranslateVirtual*>( + supported_builds[game_build_].map_x_address + 0x4); + + xe::be* map_zoom_be = + kernel_memory()->TranslateVirtual*>( + supported_builds[game_build_].map_zoom_address); + + float map_x = *map_x_be; + + float map_y = *map_y_be; + + float map_zoom = *map_zoom_be; + + // 3.75 * 0.2 = 0.75 when zoomed out the farthest game allows. + map_x -= (input_state.mouse.x_delta / (3.75f * map_zoom)) * + (float)cvars::menu_sensitivity; + + map_y -= (input_state.mouse.y_delta / (3.75f * map_zoom)) * + (float)cvars::menu_sensitivity; + + if (!cvars::swap_wheel) + map_zoom += (input_state.mouse.wheel_delta / (1000.f / map_zoom)); + else + map_zoom -= (input_state.mouse.wheel_delta / (1000.f / map_zoom)); + map_x = std::clamp(map_x, -1677.760498f, 1677.760498f); + map_y = std::clamp(map_y, -2245.578369f, 2245.578369f); + + // game default clamping is between 0.2 and 1, the game does allow to write + // outside of those, so I set the minimum a bit lower as that feels more + // natural with a mouse? + map_zoom = std::clamp(map_zoom, 0.1f, 2.5f); + + *map_x_be = map_x; + *map_y_be = map_y; + *map_zoom_be = map_zoom; +} + std::string SaintsRow1Game::ChooseBinds() { - auto* wheel_status = kernel_memory()->TranslateVirtual( + wheel_status = kernel_memory()->TranslateVirtual( supported_builds[game_build_].weapon_wheel_address); auto* menu_status = kernel_memory()->TranslateVirtual( supported_builds[game_build_].menu_status_address); @@ -373,6 +445,7 @@ bool SaintsRow1Game::ModifierKeyHandler(uint32_t user_index, out_state->gamepad.thumb_lx = (int16_t)(distance * cosf(angle)); out_state->gamepad.thumb_ly = (int16_t)(distance * sinf(angle)); } + // Return true to signal that we've handled the modifier, so default modifier // won't be used return true; diff --git a/src/xenia/hid/winkey/hookables/SaintsRow1.h b/src/xenia/hid/winkey/hookables/SaintsRow1.h index 900d71380c..9e0ed36e41 100644 --- a/src/xenia/hid/winkey/hookables/SaintsRow1.h +++ b/src/xenia/hid/winkey/hookables/SaintsRow1.h @@ -34,6 +34,9 @@ class SaintsRow1Game : public HookableGame { bool isTervelPlugin(); bool inFirstPerson(); bool isPaused(); + void WeaponWheelScrollWheel(RawInputState& input_state); + bool inMapScreen(); + void MapCursor(RawInputState& input_state); std::string ChooseBinds(); bool ModifierKeyHandler(uint32_t user_index, RawInputState& input_state, X_INPUT_STATE* out_state); @@ -45,6 +48,7 @@ class SaintsRow1Game : public HookableGame { std::chrono::steady_clock::time_point last_movement_time_x_; std::chrono::steady_clock::time_point last_movement_time_y_; uint8_t tervelplugin_status; + uint8_t* wheel_status; }; } // namespace winkey diff --git a/src/xenia/hid/winkey/hookables/SaintsRow2.cc b/src/xenia/hid/winkey/hookables/SaintsRow2.cc index 92328495a5..7499f95a75 100644 --- a/src/xenia/hid/winkey/hookables/SaintsRow2.cc +++ b/src/xenia/hid/winkey/hookables/SaintsRow2.cc @@ -150,12 +150,6 @@ bool SaintsRow2Game::DoHooks(uint32_t user_index, RawInputState& input_state, } } - XThread* current_thread = XThread::GetCurrentThread(); - - if (!current_thread) { - return false; - } - auto* sniper_status = kernel_memory()->TranslateVirtual( supported_builds[game_build_].sniper_status_address); @@ -185,7 +179,9 @@ bool SaintsRow2Game::DoHooks(uint32_t user_index, RawInputState& input_state, } } } - + if ((!input_state.mouse.x_delta && !input_state.mouse.y_delta && + !input_state.mouse.wheel_delta)) + return false; xe::be* radian_x = kernel_memory()->TranslateVirtual*>( supported_builds[game_build_].x_address); @@ -226,7 +222,6 @@ bool SaintsRow2Game::DoHooks(uint32_t user_index, RawInputState& input_state, degree_y -= (input_state.mouse.y_delta / divisor) * (float)cvars::sensitivity; } - *radian_y = DegreetoRadians(degree_y); } return true; @@ -297,6 +292,8 @@ bool SaintsRow2Game::ModifierKeyHandler(uint32_t user_index, } void SaintsRow2Game::FixHavokFrameTime() { + XThread* current_thread = XThread::GetCurrentThread(); + if (!current_thread) return; xe::be* havok_frametime = kernel_memory()->TranslateVirtual*>( supported_builds[game_build_].havok_frametime_address); @@ -320,6 +317,10 @@ uint64_t SaintsRow2Game::reset_fineaim(uint32_t function_address, uint32_t a3) { XThread* current_thread = XThread::GetCurrentThread(); + if (!current_thread) { + return 0; + } + if (function_address == NULL && player_ptr == NULL) { return 0; } diff --git a/src/xenia/hid/winkey/hookables/goldeneye.cc b/src/xenia/hid/winkey/hookables/goldeneye.cc index 10af1bc7fc..396bdd66c9 100644 --- a/src/xenia/hid/winkey/hookables/goldeneye.cc +++ b/src/xenia/hid/winkey/hookables/goldeneye.cc @@ -21,6 +21,7 @@ DECLARE_double(sensitivity); DECLARE_bool(invert_y); DECLARE_bool(invert_x); DECLARE_bool(disable_autoaim); +DECLARE_double(menu_sensitivity); DEFINE_double( ge_aim_turn_distance, 0.4f, @@ -28,9 +29,6 @@ DEFINE_double( "turning the camera [range 0 - 1]", "MouseHook"); -DEFINE_double(ge_menu_sensitivity, 0.5f, - "(GoldenEye) Mouse sensitivity when in menus", "MouseHook"); - DEFINE_bool(ge_gun_sway, true, "(GoldenEye/Perfect Dark) Enable gun sway as camera is turned", "MouseHook"); @@ -159,9 +157,9 @@ bool GoldeneyeGame::DoHooks(uint32_t user_index, RawInputState& input_state, float menuY = *menuY_ptr; menuX += (((float)input_state.mouse.x_delta) / 5.f) * - (float)cvars::ge_menu_sensitivity; + (float)cvars::menu_sensitivity; menuY += (((float)input_state.mouse.y_delta) / 5.f) * - (float)cvars::ge_menu_sensitivity; + (float)cvars::menu_sensitivity; *menuX_ptr = menuX; *menuY_ptr = menuY; diff --git a/src/xenia/hid/winkey/winkey_input_driver.cc b/src/xenia/hid/winkey/winkey_input_driver.cc index 7f33efc3d0..5790e0145c 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.cc +++ b/src/xenia/hid/winkey/winkey_input_driver.cc @@ -35,7 +35,8 @@ DEFINE_bool(invert_y, false, "Invert mouse Y axis", "MouseHook"); DEFINE_bool(invert_x, false, "Invert mouse X axis", "MouseHook"); DEFINE_bool(swap_wheel, false, "Swaps binds for wheel, so wheel up will go to next weapon & down " - "will go to prev", + "will go to prev, also changes the weapon wheel & map zoom " + "direction in Saints Row 1.", "MouseHook"); DEFINE_double(sensitivity, 1, "Mouse sensitivity", "MouseHook"); DEFINE_double( @@ -43,10 +44,14 @@ DEFINE_double( "Mouse scale when FOV is lowered (Currently for COD, DR, RDR & UE3 Games)", "MouseHook"); +DEFINE_double(menu_sensitivity, 0.5f, + "(GoldenEye/Saints Row 1) Mouse sensitivity when in menus", + "MouseHook"); + DEFINE_double(right_stick_hold_time_workaround, 33, "For games that move the right stick alongside the mouse, this " "declares how long to hold in that direction when mouse movement " - "is detected. (Currently Saints Row 2 & Gears Of Wars series)", + "is detected. (Currently Saints Row 1&2 & Gears Of Wars series)", "MouseHook"); DEFINE_int32(