diff options
| -rw-r--r-- | src/common/settings.h | 11 | ||||
| -rw-r--r-- | src/input_common/tas/tas_input.cpp | 3 | ||||
| -rw-r--r-- | src/input_common/tas/tas_input.h | 108 | ||||
| -rw-r--r-- | src/input_common/tas/tas_poller.h | 4 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 39 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_filesystem.cpp | 1 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_player_widget.cpp | 22 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_tas.cpp | 16 | 
8 files changed, 153 insertions, 51 deletions
| diff --git a/src/common/settings.h b/src/common/settings.h index cf12c325c..c53d5acc3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -512,17 +512,14 @@ struct Values {                                              "motion_device"};      BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; -    BasicSetting<bool> pause_tas_on_load { false, "pause_tas_on_load" }; -    BasicSetting<bool> tas_enable{ false, "tas_enable" }; -    BasicSetting<bool> tas_loop{ false, "tas_loop" }; -    BasicSetting<bool> tas_swap_controllers{ false, "tas_swap_controllers" }; -    BasicSetting<bool> is_cpu_boosted{ false,  "is_cpu_boosted" }; -" }; +    BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; +    BasicSetting<bool> tas_enable{false, "tas_enable"}; +    BasicSetting<bool> tas_loop{false, "tas_loop"}; +    BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"};      BasicSetting<bool> mouse_panning{false, "mouse_panning"};      BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};      BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; -      std::string mouse_device;      MouseButtonsRaw mouse_buttons; diff --git a/src/input_common/tas/tas_input.cpp b/src/input_common/tas/tas_input.cpp index aceb13adc..877d35088 100644 --- a/src/input_common/tas/tas_input.cpp +++ b/src/input_common/tas/tas_input.cpp @@ -14,6 +14,7 @@  namespace TasInput { +// Supported keywords and buttons from a TAS file  constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {      std::pair{"KEY_A", TasButton::BUTTON_A},      {"KEY_B", TasButton::BUTTON_B}, @@ -214,7 +215,7 @@ void Tas::UpdateThread() {                  }              }          } else { -            is_running = Settings::values.tas_loop; +            is_running = Settings::values.tas_loop.GetValue();              current_command = 0;              tas_data.fill({});              if (!is_running) { diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h index e1f351251..52d000db4 100644 --- a/src/input_common/tas/tas_input.h +++ b/src/input_common/tas/tas_input.h @@ -11,6 +11,38 @@  #include "core/frontend/input.h"  #include "input_common/main.h" +/* +To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below +Emulation -> Configure TAS. The file itself has normal text format and has to be called +script0-1.txt for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players). + +A script file has the same format as TAS-nx uses, so final files will look like this: + +1 KEY_B 0;0 0;0 +6 KEY_ZL 0;0 0;0 +41 KEY_ZL;KEY_Y 0;0 0;0 +43 KEY_X;KEY_A 32767;0 0;0 +44 KEY_A 32767;0 0;0 +45 KEY_A 32767;0 0;0 +46 KEY_A 32767;0 0;0 +47 KEY_A 32767;0 0;0 + +After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey +CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file +has. Playback can be started or stopped using CTRL+F5. + +However, for playback to actually work, the correct input device has to be selected: In the Controls +menu, select TAS from the device list for the controller that the script should be played on. + +Recording a new script file is really simple: Just make sure that the proper device (not TAS) is +connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke +again (CTRL+F7). The new script will be saved at the location previously selected, as the filename +record.txt. + +For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller +P1). +*/ +  namespace TasInput {  constexpr size_t PLAYER_NUMBER = 8; @@ -64,12 +96,26 @@ public:      Tas();      ~Tas(); +    // Changes the input status that will be stored in each frame      void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes); + +    // Main loop that records or executes input      void UpdateThread(); +    //  Sets the flag to start or stop the TAS command excecution and swaps controllers profiles      void StartStop(); + +    // Sets the flag to reload the file and start from the begining in the next update      void Reset(); + +    /** +     * Sets the flag to enable or disable recording of inputs +     * @return Returns true if the current recording status is enabled +     */      bool Record(); + +    // Saves contents of record_commands on a file if overwrite is enabled player 1 will be +    // overwritten with the recorded commands      void SaveRecording(bool overwrite_file);      /** @@ -80,7 +126,11 @@ public:       * Total length of script file currently loaded or amount of frames (so far) for Recording       */      std::tuple<TasState, size_t, size_t> GetStatus() const; + +    // Retuns an array of the default button mappings      InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; + +    // Retuns an array of the default analog mappings      InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;      [[nodiscard]] const TasData& GetTasState(std::size_t pad) const; @@ -90,23 +140,81 @@ private:          TasAnalog l_axis{};          TasAnalog r_axis{};      }; + +    // Loads TAS files from all players      void LoadTasFiles(); + +    // Loads TAS file from the specified player      void LoadTasFile(size_t player_index); + +    // Writes a TAS file from the recorded commands      void WriteTasFile(std::u8string file_name); + +    /** +     * Parses a string containing the axis values with the following format "x;y" +     * X and Y have a range from -32767 to 32767 +     * @return Returns a TAS analog object with axis values with range from -1.0 to 1.0 +     */      TasAnalog ReadCommandAxis(const std::string& line) const; + +    /** +     * Parses a string containing the button values with the following format "a;b;c;d..." +     * Each button is represented by it's text format specified in text_to_tas_button array +     * @return Returns a u32 with each bit representing the status of a button +     */      u32 ReadCommandButtons(const std::string& line) const; + +    /** +     * Converts an u32 containing the button status into the text equivalent +     * @return Returns a string with the name of the buttons to be written to the file +     */      std::string WriteCommandButtons(u32 data) const; + +    /** +     * Converts an TAS analog object containing the axis status into the text equivalent +     * @return Returns a string with the value of the axis to be written to the file +     */      std::string WriteCommandAxis(TasAnalog data) const; +    // Inverts the Y axis polarity      std::pair<float, float> FlipAxisY(std::pair<float, float> old); +    /** +     * Converts an u32 containing the button status into the text equivalent +     * @return Returns a string with the name of the buttons to be printed on console +     */      std::string DebugButtons(u32 buttons) const; + +    /** +     * Converts an TAS analog object containing the axis status into the text equivalent +     * @return Returns a string with the value of the axis to be printed on console +     */      std::string DebugJoystick(float x, float y) const; + +    /** +     * Converts the given TAS status into the text equivalent +     * @return Returns a string with the value of the TAS status to be printed on console +     */      std::string DebugInput(const TasData& data) const; + +    /** +     * Converts the given TAS status of multiple players into the text equivalent +     * @return Returns a string with the value of the status of all TAS players to be printed on +     * console +     */      std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const; + +    /** +     * Converts an u32 containing the button status into the text equivalent +     * @return Returns a string with the name of the buttons +     */      std::string ButtonsToString(u32 button) const; +    // Stores current controller configuration and sets a TAS controller for every active controller +    // to the current config      void SwapToTasController(); + +    // Sets the stored controller configuration to the current config      void SwapToStoredController();      size_t script_length{0}; diff --git a/src/input_common/tas/tas_poller.h b/src/input_common/tas/tas_poller.h index 1bc0d173b..09e426cef 100644 --- a/src/input_common/tas/tas_poller.h +++ b/src/input_common/tas/tas_poller.h @@ -11,7 +11,7 @@  namespace InputCommon {  /** - * A button device factory representing a mouse. It receives mouse events and forward them + * A button device factory representing a tas bot. It receives tas events and forward them   * to all button devices it created.   */  class TasButtonFactory final : public Input::Factory<Input::ButtonDevice> { @@ -29,7 +29,7 @@ private:      std::shared_ptr<TasInput::Tas> tas_input;  }; -/// An analog device factory that creates analog devices from mouse +/// An analog device factory that creates analog devices from tas  class TasAnalogFactory final : public Input::Factory<Input::AnalogDevice> {  public:      explicit TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index a0dfa06df..27b67fd9e 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -567,8 +567,10 @@ void Config::ReadControlValues() {      Settings::values.mouse_panning = false;      ReadBasicSetting(Settings::values.mouse_panning_sensitivity); -    ReadBasicSetting(Settings::values.tas_enable = false); -    ReadBasicSetting(Settings::values.tas_reset = false); +    ReadBasicSetting(Settings::values.tas_enable); +    ReadBasicSetting(Settings::values.tas_loop); +    ReadBasicSetting(Settings::values.tas_swap_controllers); +    ReadBasicSetting(Settings::values.pause_tas_on_load);      ReadGlobalSetting(Settings::values.use_docked_mode); @@ -667,20 +669,16 @@ void Config::ReadDataStorageValues() {                      QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)))              .toString()              .toStdString()); -    FS::SetYuzuPath( -        FS::YuzuPath::TASFile, -        qt_config -        ->value(QStringLiteral("tas_path"), -            QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASFile))) -        .toString() -        .toStdString()); - -    ReadBasicSetting(Settings::values.pauseTasOnLoad); +    FS::SetYuzuPath(FS::YuzuPath::TASDir, +                    qt_config +                        ->value(QStringLiteral("tas_directory"), +                                QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))) +                        .toString() +                        .toStdString());      ReadBasicSetting(Settings::values.gamecard_inserted);      ReadBasicSetting(Settings::values.gamecard_current_game);      ReadBasicSetting(Settings::values.gamecard_path); -        qt_config->endGroup();  } @@ -1205,11 +1203,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); +    WriteBasicSetting(Settings::values.tas_enable); +    WriteBasicSetting(Settings::values.tas_loop); +    WriteBasicSetting(Settings::values.tas_swap_controllers); +    WriteBasicSetting(Settings::values.pause_tas_on_load); +      qt_config->endGroup();  } @@ -1237,10 +1235,9 @@ void Config::SaveDataStorageValues() {      WriteSetting(QStringLiteral("dump_directory"),                   QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)),                   QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); -    WriteSetting(QStringLiteral("tas_path"), -        QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASFile)), -        QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASFile))); -    WriteSetting(QStringLiteral("tas_pause_on_load"), Settings::values.pauseTasOnLoad, true); +    WriteSetting(QStringLiteral("tas_directory"), +                 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)), +                 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));      WriteBasicSetting(Settings::values.gamecard_inserted);      WriteBasicSetting(Settings::values.gamecard_current_game); diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index 013de02db..9cb317822 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp @@ -52,7 +52,6 @@ void ConfigureFilesystem::setConfiguration() {      ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted.GetValue());      ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game.GetValue()); -    ui->tas_pause_on_load->setChecked(Settings::values.pauseTasOnLoad);      ui->dump_exefs->setChecked(Settings::values.dump_exefs.GetValue());      ui->dump_nso->setChecked(Settings::values.dump_nso.GetValue()); diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index ba3720c03..38c59263c 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -222,23 +222,23 @@ void PlayerControlPreview::UpdateInput() {      if (input_changed) {          update(); -        ControllerInput input{ -            .axis_values = -                {std::pair<float, float>{axis_values[Settings::NativeAnalog::LStick].value.x(), -                                         axis_values[Settings::NativeAnalog::LStick].value.y()}, -                 std::pair<float, float>{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) { +            ControllerInput input{ +                .axis_values = {std::pair<float, float>{ +                                    axis_values[Settings::NativeAnalog::LStick].value.x(), +                                    axis_values[Settings::NativeAnalog::LStick].value.y()}, +                                std::pair<float, float>{ +                                    axis_values[Settings::NativeAnalog::RStick].value.x(), +                                    axis_values[Settings::NativeAnalog::RStick].value.y()}}, +                .button_values = button_values, +                .changed = true, +            };              controller_callback.input(std::move(input));          }      }      if (controller_callback.update != nullptr) { -        controller_callback.update(std::move(true)); +        controller_callback.update(true);      }      if (mapping_active) { diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp index f2f91d84a..00d6c1ba5 100644 --- a/src/yuzu/configuration/configure_tas.cpp +++ b/src/yuzu/configuration/configure_tas.cpp @@ -30,18 +30,18 @@ ConfigureTasDialog::~ConfigureTasDialog() = default;  void ConfigureTasDialog::LoadConfiguration() {      ui->tas_path_edit->setText(          QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir))); -    ui->tas_enable->setChecked(Settings::values.tas_enable); -    ui->tas_control_swap->setChecked(Settings::values.tas_swap_controllers); -    ui->tas_loop_script->setChecked(Settings::values.tas_loop); -    ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load); +    ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue()); +    ui->tas_control_swap->setChecked(Settings::values.tas_swap_controllers.GetValue()); +    ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue()); +    ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue());  }  void ConfigureTasDialog::ApplyConfiguration() {      Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString()); -    Settings::values.tas_enable = ui->tas_enable->isChecked(); -    Settings::values.tas_swap_controllers = ui->tas_control_swap->isChecked(); -    Settings::values.tas_loop = ui->tas_loop_script->isChecked(); -    Settings::values.pause_tas_on_load = ui->tas_pause_on_load->isChecked(); +    Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked()); +    Settings::values.tas_swap_controllers.SetValue(ui->tas_control_swap->isChecked()); +    Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked()); +    Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked());  }  void ConfigureTasDialog::SetDirectory(DirectoryTarget target, QLineEdit* edit) { | 
