From e6c4bf52f0eb2c9c78e983ffbc667891463d3253 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 27 Jun 2021 14:02:38 -0500 Subject: [PATCH] input_common/tas: Add swap controller --- src/common/fs/path_util.h | 2 +- src/input_common/main.h | 20 +++--- src/input_common/tas/tas_input.cpp | 63 ++++++++++++++++--- src/input_common/tas/tas_input.h | 9 ++- src/yuzu/configuration/config.cpp | 5 ++ .../configure_input_player_widget.cpp | 32 +++++----- src/yuzu/configuration/configure_tas.ui | 3 - src/yuzu/main.cpp | 2 +- 8 files changed, 98 insertions(+), 38 deletions(-) diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index 52e4670e27..0a9e3a1455 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h @@ -23,7 +23,7 @@ enum class YuzuPath { ScreenshotsDir, // Where yuzu screenshots are stored. SDMCDir, // Where the emulated SDMC is stored. ShaderDir, // Where shaders are stored. - TASDir, // Where the current script file is stored. + TASDir, // Where TAS scripts are stored. }; /** diff --git a/src/input_common/main.h b/src/input_common/main.h index 1d06fc5f51..6390d3f094 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -155,28 +155,28 @@ public: /// Retrieves the underlying udp touch handler. [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const; - /// Retrieves the underlying GameCube button handler. + /// Retrieves the underlying mouse button handler. [[nodiscard]] MouseButtonFactory* GetMouseButtons(); - /// Retrieves the underlying GameCube button handler. + /// Retrieves the underlying mouse button handler. [[nodiscard]] const MouseButtonFactory* GetMouseButtons() const; - /// Retrieves the underlying udp touch handler. + /// Retrieves the underlying mouse analog handler. [[nodiscard]] MouseAnalogFactory* GetMouseAnalogs(); - /// Retrieves the underlying udp touch handler. + /// Retrieves the underlying mouse analog handler. [[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const; - /// Retrieves the underlying udp motion handler. + /// Retrieves the underlying mouse motion handler. [[nodiscard]] MouseMotionFactory* GetMouseMotions(); - /// Retrieves the underlying udp motion handler. + /// Retrieves the underlying mouse motion handler. [[nodiscard]] const MouseMotionFactory* GetMouseMotions() const; - /// Retrieves the underlying udp touch handler. + /// Retrieves the underlying mouse touch handler. [[nodiscard]] MouseTouchFactory* GetMouseTouch(); - /// Retrieves the underlying udp touch handler. + /// Retrieves the underlying mouse touch handler. [[nodiscard]] const MouseTouchFactory* GetMouseTouch() const; /// Retrieves the underlying tas button handler. @@ -185,10 +185,10 @@ public: /// Retrieves the underlying tas button handler. [[nodiscard]] const TasButtonFactory* GetTasButtons() const; - /// Retrieves the underlying tas touch handler. + /// Retrieves the underlying tas analogs handler. [[nodiscard]] TasAnalogFactory* GetTasAnalogs(); - /// Retrieves the underlying tas touch handler. + /// Retrieves the underlying tas analogs handler. [[nodiscard]] const TasAnalogFactory* GetTasAnalogs() const; /// Reloads the input devices diff --git a/src/input_common/tas/tas_input.cpp b/src/input_common/tas/tas_input.cpp index eb3327520c..aceb13adcc 100644 --- a/src/input_common/tas/tas_input.cpp +++ b/src/input_common/tas/tas_input.cpp @@ -61,8 +61,8 @@ void Tas::LoadTasFile(size_t player_index) { commands[player_index].clear(); } std::string file = - Common::FS::ReadStringFromFile(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir) + - "script0-" + std::to_string(player_index + 1) + ".txt", + Common::FS::ReadStringFromFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / + fmt::format("script0-{}.txt", player_index + 1), Common::FS::FileType::BinaryFile); std::stringstream command_line(file); std::string line; @@ -102,7 +102,7 @@ void Tas::LoadTasFile(size_t player_index) { LOG_INFO(Input, "TAS file loaded! {} frames", frame_no); } -void Tas::WriteTasFile(std::string file_name) { +void Tas::WriteTasFile(std::u8string file_name) { std::string output_text; for (size_t frame = 0; frame < record_commands.size(); frame++) { if (!output_text.empty()) { @@ -112,8 +112,8 @@ void Tas::WriteTasFile(std::string file_name) { output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " + WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis); } - const size_t bytes_written = Common::FS::WriteStringToFile( - Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir) + file_name, + const auto bytes_written = Common::FS::WriteStringToFile( + Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name, Common::FS::FileType::TextFile, output_text); if (bytes_written == output_text.size()) { LOG_INFO(Input, "TAS file written to file!"); @@ -217,6 +217,9 @@ void Tas::UpdateThread() { is_running = Settings::values.tas_loop; current_command = 0; tas_data.fill({}); + if (!is_running) { + SwapToStoredController(); + } } } else { tas_data.fill({}); @@ -290,6 +293,52 @@ std::string Tas::WriteCommandButtons(u32 data) const { void Tas::StartStop() { is_running = !is_running; + if (is_running) { + SwapToTasController(); + } else { + SwapToStoredController(); + } +} + +void Tas::SwapToTasController() { + if (!Settings::values.tas_swap_controllers) { + return; + } + auto& players = Settings::values.players.GetValue(); + for (std::size_t index = 0; index < players.size(); index++) { + auto& player = players[index]; + player_mappings[index] = player; + + // Only swap active controllers + if (!player.connected) { + continue; + } + + auto tas_param = Common::ParamPackage{{"pad", static_cast(index)}}; + auto button_mapping = GetButtonMappingForDevice(tas_param); + auto analog_mapping = GetAnalogMappingForDevice(tas_param); + auto& buttons = player.buttons; + auto& analogs = player.analogs; + + for (std::size_t i = 0; i < buttons.size(); ++i) { + buttons[i] = button_mapping[static_cast(i)].Serialize(); + } + for (std::size_t i = 0; i < analogs.size(); ++i) { + analogs[i] = analog_mapping[static_cast(i)].Serialize(); + } + } + Settings::values.is_device_reload_pending.store(true); +} + +void Tas::SwapToStoredController() { + if (!Settings::values.tas_swap_controllers) { + return; + } + auto& players = Settings::values.players.GetValue(); + for (std::size_t index = 0; index < players.size(); index++) { + players[index] = player_mappings[index]; + } + Settings::values.is_device_reload_pending.store(true); } void Tas::Reset() { @@ -308,9 +357,9 @@ void Tas::SaveRecording(bool overwrite_file) { if (record_commands.empty()) { return; } - WriteTasFile("record.txt"); + WriteTasFile(u8"record.txt"); if (overwrite_file) { - WriteTasFile("script0-1.txt"); + WriteTasFile(u8"script0-1.txt"); } needs_reset = true; record_commands.clear(); diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h index e0462e8588..e1f3512511 100644 --- a/src/input_common/tas/tas_input.h +++ b/src/input_common/tas/tas_input.h @@ -7,6 +7,7 @@ #include #include "common/common_types.h" +#include "common/settings_input.h" #include "core/frontend/input.h" #include "input_common/main.h" @@ -91,7 +92,7 @@ private: }; void LoadTasFiles(); void LoadTasFile(size_t player_index); - void WriteTasFile(std::string file_name); + void WriteTasFile(std::u8string file_name); TasAnalog ReadCommandAxis(const std::string& line) const; u32 ReadCommandButtons(const std::string& line) const; std::string WriteCommandButtons(u32 data) const; @@ -105,6 +106,9 @@ private: std::string DebugInputs(const std::array& arr) const; std::string ButtonsToString(u32 button) const; + void SwapToTasController(); + void SwapToStoredController(); + size_t script_length{0}; std::array tas_data; bool is_recording{false}; @@ -114,5 +118,8 @@ private: std::vector record_commands{}; size_t current_command{0}; TASCommand last_input{}; // only used for recording + + // Old settings for swapping controllers + std::array player_mappings; }; } // namespace TasInput diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 99e318a8fe..a0dfa06dfa 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -1205,6 +1205,11 @@ void Config::SaveControlValues() { WriteBasicSetting(Settings::values.emulate_analog_keyboard); WriteBasicSetting(Settings::values.mouse_panning_sensitivity); + WriteSetting(QStringLiteral("enable_tas"), Settings::values.tas_enable, false); + WriteSetting(QStringLiteral("loop_tas"), Settings::values.tas_loop, false); + WriteSetting(QStringLiteral("swap_tas_controllers"), Settings::values.tas_swap_controllers, + true); + WriteSetting(QStringLiteral("tas_pause_on_load"), Settings::values.pause_tas_on_load, true); qt_config->endGroup(); } diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index b905fc73d6..ba3720c033 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -175,10 +175,7 @@ void PlayerControlPreview::ResetInputs() { } void PlayerControlPreview::UpdateInput() { - if (controller_callback.update != nullptr) { - controller_callback.update(std::move(true)); - } - if (!is_enabled && !mapping_active) { + if (!is_enabled && !mapping_active && !Settings::values.tas_enable) { return; } bool input_changed = false; @@ -223,20 +220,25 @@ void PlayerControlPreview::UpdateInput() { } } - ControllerInput input{}; if (input_changed) { update(); - input.changed = true; + ControllerInput input{ + .axis_values = + {std::pair{axis_values[Settings::NativeAnalog::LStick].value.x(), + axis_values[Settings::NativeAnalog::LStick].value.y()}, + std::pair{axis_values[Settings::NativeAnalog::RStick].value.x(), + axis_values[Settings::NativeAnalog::RStick].value.y()}}, + .button_values = button_values, + .changed = true, + }; + + if (controller_callback.input != nullptr) { + controller_callback.input(std::move(input)); + } } - input.axis_values[Settings::NativeAnalog::LStick] = { - axis_values[Settings::NativeAnalog::LStick].value.x(), - axis_values[Settings::NativeAnalog::LStick].value.y()}; - input.axis_values[Settings::NativeAnalog::RStick] = { - axis_values[Settings::NativeAnalog::RStick].value.x(), - axis_values[Settings::NativeAnalog::RStick].value.y()}; - input.button_values = button_values; - if (controller_callback.input != nullptr) { - controller_callback.input(std::move(input)); + + if (controller_callback.update != nullptr) { + controller_callback.update(std::move(true)); } if (mapping_active) { diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui index 906e073ff7..445904d8fa 100644 --- a/src/yuzu/configuration/configure_tas.ui +++ b/src/yuzu/configuration/configure_tas.ui @@ -31,9 +31,6 @@ - - false - Automatic controller profile swapping diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 560de89af5..7d12fcca76 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2921,7 +2921,7 @@ QString GMainWindow::GetTasStateDescription() const { case TasInput::TasState::Stopped: return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames); default: - return tr("INVALID TAS STATE"); + return tr("TAS State: Invalid"); } }