From dd6932e27de0d6b14665d717846d2c1b7f0e45a9 Mon Sep 17 00:00:00 2001 From: florianessl Date: Tue, 4 Mar 2025 13:06:59 +0100 Subject: [PATCH 1/4] Ineluki Patch: Implemented support for the undocumented patch options 'setMouseAsReturn' & 'setMouseWheelAsKeys' (Fixes games: Vortex & Rutipa's Quest 9) --- src/game_ineluki.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++-- src/game_ineluki.h | 28 ++++++++++++++++- src/input.cpp | 24 +++++++++++++++ src/input.h | 9 ++++++ src/input_source.cpp | 7 ++++- src/input_source.h | 7 +++++ 6 files changed, 142 insertions(+), 4 deletions(-) diff --git a/src/game_ineluki.cpp b/src/game_ineluki.cpp index 8e5a6d1982..9a549f83e7 100644 --- a/src/game_ineluki.cpp +++ b/src/game_ineluki.cpp @@ -173,6 +173,38 @@ bool Game_Ineluki::Execute(StringView ini_file) { // no-op } else if (cmd.name == "registercheatevent") { cheatlist.emplace_back(Utils::LowerCase(cmd.arg), atoi(cmd.arg2.c_str())); + } else if (cmd.name == "setmouseasreturn") { + if (!mouse_support) { + return true; + } + std::string arg_lower = Utils::LowerCase(cmd.arg); + if (arg_lower == "left") { + mouse_decision_binding = MouseReturnMode::Left; + } else if (arg_lower == "right") { + mouse_decision_binding = MouseReturnMode::Right; + } else if (arg_lower == "both") { + mouse_decision_binding = MouseReturnMode::Both; + } else if (arg_lower == "none") { + mouse_decision_binding = MouseReturnMode::None; + } else { + Output::Warning("Ineluki: Invalid value for setMouseAsReturn"); + mouse_decision_binding = MouseReturnMode::None; + } + } else if (cmd.name == "setmousewheelaskeys") { + if (!mouse_support) { + return true; + } + std::string arg_lower = Utils::LowerCase(cmd.arg); + if (arg_lower == "updown") { + mouse_wheel_binding = MouseWheelMode::UpDown; + } else if (arg_lower == "leftright") { + mouse_wheel_binding = MouseWheelMode::LeftRight; + } else if (arg_lower == "none") { + mouse_wheel_binding = MouseWheelMode::None; + } else { + Output::Warning("Ineluki: Invalid value for setMouseWheelAsKeys"); + mouse_wheel_binding = MouseWheelMode::None; + } } } @@ -263,6 +295,10 @@ bool Game_Ineluki::Parse(StringView ini_file) { } else if (cmd.name == "registercheatevent") { cmd.arg = ini.Get(section, "cheat", std::string()); cmd.arg2 = ini.Get(section, "value", std::string()); + } else if (cmd.name == "setmouseasreturn") { + cmd.arg = ini.Get(section, "value", std::string()); + } else if (cmd.name == "setmousewheelaskeys") { + cmd.arg = ini.Get(section, "value", std::string()); } else { Output::Debug("Ineluki: Unknown command {}", cmd.name); valid = false; @@ -294,10 +330,15 @@ int Game_Ineluki::GetMidiTicks() { } void Game_Ineluki::Update() { - if (!key_support) { - return; + if (key_support) { + UpdateKeys(); } + if (mouse_support) { + UpdateMouse(); + } +} +void Game_Ineluki::UpdateKeys() { for (const auto& key : keylist_down) { if (Input::IsRawKeyTriggered(key.key)) { output_list.push_back(key.value); @@ -332,6 +373,32 @@ void Game_Ineluki::Update() { } } +void Game_Ineluki::UpdateMouse() { + if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_LEFT)) { + if ((mouse_decision_binding == MouseReturnMode::Left || mouse_decision_binding == MouseReturnMode::Both)) { + Input::SimulateButtonPress(Input::DECISION); + } + } else if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_RIGHT)) { + if ((mouse_decision_binding == MouseReturnMode::Right || mouse_decision_binding == MouseReturnMode::Both)) { + Input::SimulateButtonPress(Input::DECISION); + } + } + + if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_SCROLLUP)) { + if (mouse_wheel_binding == MouseWheelMode::UpDown) { + Input::SimulateButtonPress(Input::UP); + } else if (mouse_wheel_binding == MouseWheelMode::LeftRight) { + Input::SimulateButtonPress(Input::LEFT); + } + } else if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_SCROLLDOWN)) { + if (mouse_wheel_binding == MouseWheelMode::UpDown) { + Input::SimulateButtonPress(Input::DOWN); + } else if (mouse_wheel_binding == MouseWheelMode::LeftRight) { + Input::SimulateButtonPress(Input::RIGHT); + } + } +} + void Game_Ineluki::OnScriptFileReady(FileRequestResult* result) { auto it = std::find_if(async_scripts.begin(), async_scripts.end(), [&](const auto& a) { return a.script_name == result->file; diff --git a/src/game_ineluki.h b/src/game_ineluki.h index 72f33c1de4..258ff63362 100644 --- a/src/game_ineluki.h +++ b/src/game_ineluki.h @@ -74,11 +74,21 @@ class Game_Ineluki { int GetMidiTicks(); /** - * Updates the key up/down list. Must be called once per update frame. + * Updates the configured input patches. Must be called once per update frame. */ void Update(); private: + /** + * Updates the key up/down list. + */ + void UpdateKeys(); + + /** + * Handles virtual key bindings for mouse buttons. + */ + void UpdateMouse(); + /** * Parses and caches the script. * @@ -123,6 +133,22 @@ class Game_Ineluki { bool mouse_support = false; int mouse_id_prefix = 0; + enum class MouseReturnMode { + None, + Left, + Right, + Both + }; + + enum class MouseWheelMode { + None, + UpDown, + LeftRight + }; + + MouseReturnMode mouse_decision_binding = MouseReturnMode::None; + MouseWheelMode mouse_wheel_binding = MouseWheelMode::None; + struct Mapping { Input::Keys::InputKey key; const char* name; diff --git a/src/input.cpp b/src/input.cpp index 629c41fece..b18a04104c 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -408,3 +408,27 @@ void Input::ResetMask() { SetMask(source->GetMask()); } +void Input::SimulateButtonPress(Input::InputButton button) { + switch (button) { + case Input::UP: + case Input::DOWN: + case Input::LEFT: + case Input::RIGHT: + { + // Directional movement has its own input handling + // These buttons need to be simulated on a lower level, + // or else those movement actions will be overwritten + auto& cfg = source->GetConfig(); + for (auto& bm : cfg.buttons) { + if (bm.first == button) { + source->SimulateKeyPress(bm.second); + break; + } + } + break; + } + default: + break; + } + UpdateButton(button, true); +} diff --git a/src/input.h b/src/input.h index 4dd980b8b1..52ae2aa521 100644 --- a/src/input.h +++ b/src/input.h @@ -303,6 +303,15 @@ namespace Input { */ Source* GetInputSource(); + /** + * Used to simulate a button press. This is used for + * emulating the behavior of some runtime patches. + * Buttons for directional movement will be delegated + * to the underlying low level input source. + * @param button The input button which should be registered as being 'pressed' + */ + void SimulateButtonPress(Input::InputButton button); + /** Buttons press time (in frames). */ extern std::array press_time; diff --git a/src/input_source.cpp b/src/input_source.cpp index c05b31e3b6..3fb5948a18 100644 --- a/src/input_source.cpp +++ b/src/input_source.cpp @@ -64,9 +64,10 @@ void Input::UiSource::DoUpdate(bool system_only) { } if (!system_only || Input::IsSystemButton(bm.first)) { - pressed_buttons[bm.first] = pressed_buttons[bm.first] || keystates[bm.second]; + pressed_buttons[bm.first] = pressed_buttons[bm.first] || keystates[bm.second] || keystates_virtual[bm.second]; } } + keystates_virtual = {}; Record(); @@ -330,6 +331,10 @@ void Input::Source::AddRecordingData(Input::RecordingData type, StringView data) } } +void Input::Source::SimulateKeyPress(Input::Keys::InputKey key) { + keystates_virtual[key] = true; +} + void Input::LogSource::UpdateSystem() { // input log does not record actions outside of logical frames. } diff --git a/src/input_source.h b/src/input_source.h index c3b91f0dc4..735da6bc2f 100644 --- a/src/input_source.h +++ b/src/input_source.h @@ -139,6 +139,12 @@ namespace Input { const KeyStatus& GetMask() const { return keymask; } KeyStatus& GetMask() { return keymask; } + /** + * Emulate a key being pressed. + * @param key + */ + void SimulateKeyPress(Input::Keys::InputKey key); + protected: void Record(); void UpdateGamepad(); @@ -152,6 +158,7 @@ namespace Input { KeyStatus keystates; KeyStatus keymask; + KeyStatus keystates_virtual; Point mouse_pos; AnalogInput analog_input; From c7e1f34c95d43e68e66d7031298deb2177696d82 Mon Sep 17 00:00:00 2001 From: florianessl Date: Tue, 4 Mar 2025 16:55:03 +0100 Subject: [PATCH 2/4] Added a short comment to the two rare keypatch commands --- src/game_ineluki.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/game_ineluki.cpp b/src/game_ineluki.cpp index 9a549f83e7..560940243f 100644 --- a/src/game_ineluki.cpp +++ b/src/game_ineluki.cpp @@ -174,6 +174,7 @@ bool Game_Ineluki::Execute(StringView ini_file) { } else if (cmd.name == "registercheatevent") { cheatlist.emplace_back(Utils::LowerCase(cmd.arg), atoi(cmd.arg2.c_str())); } else if (cmd.name == "setmouseasreturn") { + // This command is only found in a few uncommon versions of the patch if (!mouse_support) { return true; } @@ -191,6 +192,7 @@ bool Game_Ineluki::Execute(StringView ini_file) { mouse_decision_binding = MouseReturnMode::None; } } else if (cmd.name == "setmousewheelaskeys") { + // This command is only found in a few uncommon versions of the patch if (!mouse_support) { return true; } From 3711fee621369eb88277a38b0062c493217905bb Mon Sep 17 00:00:00 2001 From: florianessl Date: Tue, 4 Mar 2025 18:20:13 +0100 Subject: [PATCH 3/4] Fix build for platforms with no mouse-support --- src/game_ineluki.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/game_ineluki.cpp b/src/game_ineluki.cpp index 560940243f..b7b404d50e 100644 --- a/src/game_ineluki.cpp +++ b/src/game_ineluki.cpp @@ -376,6 +376,7 @@ void Game_Ineluki::UpdateKeys() { } void Game_Ineluki::UpdateMouse() { +#if defined(USE_MOUSE_OR_TOUCH) && defined(SUPPORT_MOUSE_OR_TOUCH) if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_LEFT)) { if ((mouse_decision_binding == MouseReturnMode::Left || mouse_decision_binding == MouseReturnMode::Both)) { Input::SimulateButtonPress(Input::DECISION); @@ -399,6 +400,7 @@ void Game_Ineluki::UpdateMouse() { Input::SimulateButtonPress(Input::RIGHT); } } +#endif } void Game_Ineluki::OnScriptFileReady(FileRequestResult* result) { From c132fb414a7eae2165dccbfd872628fe153f3e6f Mon Sep 17 00:00:00 2001 From: florianessl Date: Tue, 4 Mar 2025 18:50:51 +0100 Subject: [PATCH 4/4] Fix build: Change #define conditions from "MOUSE_OR_TOUCH" to just "MOUSE" --- src/game_ineluki.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_ineluki.cpp b/src/game_ineluki.cpp index b7b404d50e..a99562d92c 100644 --- a/src/game_ineluki.cpp +++ b/src/game_ineluki.cpp @@ -376,7 +376,7 @@ void Game_Ineluki::UpdateKeys() { } void Game_Ineluki::UpdateMouse() { -#if defined(USE_MOUSE_OR_TOUCH) && defined(SUPPORT_MOUSE_OR_TOUCH) +#if defined(USE_MOUSE) && defined(SUPPORT_MOUSE) if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_LEFT)) { if ((mouse_decision_binding == MouseReturnMode::Left || mouse_decision_binding == MouseReturnMode::Both)) { Input::SimulateButtonPress(Input::DECISION);