Skip to content

Commit

Permalink
Saints Row 1 extras (#41)
Browse files Browse the repository at this point in the history
* (SR1)Native mouse controls for map menu, includes scroll wheel.

* tie map cursor speed to zoom & fix typo for =in_map_screen to ==

* Scroll wheel / map_zoom, increase min zoom for map.

* 0.1 map zoom min

* Scroll wheel to Weapon wheel, fix map screen flag.

* Remove check for XThread, should fix mouse stuttering

* Proper degree_x implementation

* [SR2] Remove check for XThread in DoHooks, check XThread in reset_fineaim function to avoid a crash.

* DoHook if input_state.mouse

* Write 1/60 to havok address in case user starts game with input other than winkey.

* RS bindings

* Revert "Proper degree_x implementation"

This reverts commit c4467b7.

* Check if wheel_delta is active in ScrollWheel function

* SR2 bindings, Mouse5 to open wheel, Mouse5 to confirm / 4 to go back in menus.

* Check current thread in FixHavokFrameTime

---------

Co-authored-by: Clippy95 <[email protected]>
  • Loading branch information
2 people authored and marinesciencedude committed Nov 11, 2024
1 parent c189ff2 commit 25aaef8
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 38 deletions.
14 changes: 13 additions & 1 deletion bindings.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ D = LS-Right
CapsLock = Modifier
Ctrl = LS
C = LS
B = RS
LClick = RT
RClick = LT
Mouse5 = B
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -436,6 +441,7 @@ LClick = RT
RClick = RS
F = LB
Q = B
Mouse5 = B
E = Y
R = A
Space = X
Expand Down Expand Up @@ -470,6 +476,8 @@ V = RS
3 = Up
4 = Down
LShift = B
Mouse5 = A
Mouse4 = B
Tab = Back
Enter = Start
Up = RS-Up
Expand All @@ -487,6 +495,7 @@ LClick = RT
RClick = RS
F = LB
Q = B
Mouse5 = B
E = Y
R = A
Space = X
Expand All @@ -513,6 +522,7 @@ LClick = RT
RClick = RS
F = LB
Q = B
Mouse5 = B
E = Y
Space = LT
C = LS-Down
Expand Down Expand Up @@ -546,6 +556,7 @@ RClick = LT
MWheelDown = Left
MWheelUp = Right
Q = B
Mouse5 = B
1 = Left
2 = Right
3 = Up
Expand All @@ -570,6 +581,7 @@ RClick = LT
MWheelDown = Left
MWheelUp = Right
Q = B
Mouse5 = B
Z = LB
C = RB
1 = Left
Expand Down
14 changes: 12 additions & 2 deletions src/xenia/emulator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1716,12 +1716,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<SR1PatchOffsets> 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<uint32_t>*)module->memory()->TranslateVirtual(
Expand All @@ -1741,6 +1744,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) {
Expand Down Expand Up @@ -1810,6 +1815,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<SR2PatchOffsets> supported_builds = {
Expand All @@ -1819,7 +1826,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) {
Expand Down Expand Up @@ -1872,6 +1880,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;
}
}
Expand Down
113 changes: 93 additions & 20 deletions src/xenia/hid/winkey/hookables/SaintsRow1.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 =
Expand All @@ -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<SaintsRow1Game::GameBuild, GameBuildAddrs> 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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<float>* addition_x = kernel_memory()->TranslateVirtual<xe::be<float>*>(
supported_builds[game_build_].x_address);

Expand Down Expand Up @@ -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<float>* havok_frametime =
kernel_memory()->TranslateVirtual<xe::be<float>*>(
supported_builds[game_build_].havok_frametime_address);
Expand Down Expand Up @@ -337,8 +340,77 @@ bool SaintsRow1Game::isPaused() {
return false;
}

void SaintsRow1Game::WeaponWheelScrollWheel(RawInputState& input_state) {
auto* weapon_slot = kernel_memory()->TranslateVirtual<uint8_t*>(
supported_builds[game_build_].weapon_wheel_slot_address);
if (input_state.mouse.wheel_delta) {
int16_t slot = static_cast<int16_t>(*weapon_slot);

// one scroll of the wheel_delta seems to always return 120?
if (!cvars::swap_wheel)
slot += static_cast<int16_t>(input_state.mouse.wheel_delta / 120);
else
slot -= static_cast<int16_t>(input_state.mouse.wheel_delta / 120);

slot = slot % 8;

*weapon_slot = static_cast<uint8_t>(slot);
}
}

bool SaintsRow1Game::inMapScreen() {
auto* pause_screen = kernel_memory()->TranslateVirtual<uint8_t*>(
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<float>* map_x_be = kernel_memory()->TranslateVirtual<xe::be<float>*>(
supported_builds[game_build_].map_x_address);

xe::be<float>* map_y_be = kernel_memory()->TranslateVirtual<xe::be<float>*>(
supported_builds[game_build_].map_x_address + 0x4);

xe::be<float>* map_zoom_be =
kernel_memory()->TranslateVirtual<xe::be<float>*>(
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<uint8_t*>(
wheel_status = kernel_memory()->TranslateVirtual<uint8_t*>(
supported_builds[game_build_].weapon_wheel_address);
auto* menu_status = kernel_memory()->TranslateVirtual<uint8_t*>(
supported_builds[game_build_].menu_status_address);
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/xenia/hid/winkey/hookables/SaintsRow1.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down
17 changes: 9 additions & 8 deletions src/xenia/hid/winkey/hookables/SaintsRow2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint8_t*>(
supported_builds[game_build_].sniper_status_address);

Expand Down Expand Up @@ -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<float>* radian_x = kernel_memory()->TranslateVirtual<xe::be<float>*>(
supported_builds[game_build_].x_address);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<float>* havok_frametime =
kernel_memory()->TranslateVirtual<xe::be<float>*>(
supported_builds[game_build_].havok_frametime_address);
Expand All @@ -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;
}
Expand Down
Loading

0 comments on commit 25aaef8

Please sign in to comment.