From b42c3ce21db249d5e3bc04b4f73202e757da317c Mon Sep 17 00:00:00 2001 From: MonsterDruide1 <5958456@gmail.com> Date: Fri, 18 Jun 2021 16:15:42 +0200 Subject: input_common/tas: Base playback & recording system The base playback system supports up to 8 controllers (specified by `PLAYER_NUMBER` in `tas_input.h`), which all change their inputs simulataneously when `TAS::UpdateThread` is called. The recording system uses the controller debugger to read the state of the first controller and forwards that data to the TASing system for recording. Currently, this process sadly is not frame-perfect and pixel-accurate. Co-authored-by: Naii-the-Baf Co-authored-by: Narr-the-Reg --- src/input_common/tas/tas_input.h | 163 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 src/input_common/tas/tas_input.h (limited to 'src/input_common/tas/tas_input.h') diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h new file mode 100644 index 000000000..0a152a04f --- /dev/null +++ b/src/input_common/tas/tas_input.h @@ -0,0 +1,163 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" +#include "core/frontend/input.h" +#include "input_common/main.h" + +#define PLAYER_NUMBER 8 + +namespace TasInput { + +using TasAnalog = std::tuple; + +enum class TasButton : u32 { + BUTTON_A = 0x000001, + BUTTON_B = 0x000002, + BUTTON_X = 0x000004, + BUTTON_Y = 0x000008, + STICK_L = 0x000010, + STICK_R = 0x000020, + TRIGGER_L = 0x000040, + TRIGGER_R = 0x000080, + TRIGGER_ZL = 0x000100, + TRIGGER_ZR = 0x000200, + BUTTON_PLUS = 0x000400, + BUTTON_MINUS = 0x000800, + BUTTON_LEFT = 0x001000, + BUTTON_UP = 0x002000, + BUTTON_RIGHT = 0x004000, + BUTTON_DOWN = 0x008000, + BUTTON_SL = 0x010000, + BUTTON_SR = 0x020000, + BUTTON_HOME = 0x040000, + BUTTON_CAPTURE = 0x080000, +}; + +static const std::array, 20> text_to_tas_button = { + std::pair{"KEY_A", TasButton::BUTTON_A}, + {"KEY_B", TasButton::BUTTON_B}, + {"KEY_X", TasButton::BUTTON_X}, + {"KEY_Y", TasButton::BUTTON_Y}, + {"KEY_LSTICK", TasButton::STICK_L}, + {"KEY_RSTICK", TasButton::STICK_R}, + {"KEY_L", TasButton::TRIGGER_L}, + {"KEY_R", TasButton::TRIGGER_R}, + {"KEY_PLUS", TasButton::BUTTON_PLUS}, + {"KEY_MINUS", TasButton::BUTTON_MINUS}, + {"KEY_DLEFT", TasButton::BUTTON_LEFT}, + {"KEY_DUP", TasButton::BUTTON_UP}, + {"KEY_DRIGHT", TasButton::BUTTON_RIGHT}, + {"KEY_DDOWN", TasButton::BUTTON_DOWN}, + {"KEY_SL", TasButton::BUTTON_SL}, + {"KEY_SR", TasButton::BUTTON_SR}, + {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE}, + {"KEY_HOME", TasButton::BUTTON_HOME}, + {"KEY_ZL", TasButton::TRIGGER_ZL}, + {"KEY_ZR", TasButton::TRIGGER_ZR}, +}; + +enum class TasAxes : u8 { + StickX, + StickY, + SubstickX, + SubstickY, + Undefined, +}; + +struct TasData { + u32 buttons{}; + std::array axis{}; +}; + +class Tas { +public: + Tas(); + ~Tas(); + + static std::string buttonsToString(u32 button) { + std::string returns; + if ((button & static_cast(TasInput::TasButton::BUTTON_A)) != 0) + returns += ", A"; + if ((button & static_cast(TasInput::TasButton::BUTTON_B)) != 0) + returns += ", B"; + if ((button & static_cast(TasInput::TasButton::BUTTON_X)) != 0) + returns += ", X"; + if ((button & static_cast(TasInput::TasButton::BUTTON_Y)) != 0) + returns += ", Y"; + if ((button & static_cast(TasInput::TasButton::STICK_L)) != 0) + returns += ", STICK_L"; + if ((button & static_cast(TasInput::TasButton::STICK_R)) != 0) + returns += ", STICK_R"; + if ((button & static_cast(TasInput::TasButton::TRIGGER_L)) != 0) + returns += ", TRIGGER_L"; + if ((button & static_cast(TasInput::TasButton::TRIGGER_R)) != 0) + returns += ", TRIGGER_R"; + if ((button & static_cast(TasInput::TasButton::TRIGGER_ZL)) != 0) + returns += ", TRIGGER_ZL"; + if ((button & static_cast(TasInput::TasButton::TRIGGER_ZR)) != 0) + returns += ", TRIGGER_ZR"; + if ((button & static_cast(TasInput::TasButton::BUTTON_PLUS)) != 0) + returns += ", PLUS"; + if ((button & static_cast(TasInput::TasButton::BUTTON_MINUS)) != 0) + returns += ", MINUS"; + if ((button & static_cast(TasInput::TasButton::BUTTON_LEFT)) != 0) + returns += ", LEFT"; + if ((button & static_cast(TasInput::TasButton::BUTTON_UP)) != 0) + returns += ", UP"; + if ((button & static_cast(TasInput::TasButton::BUTTON_RIGHT)) != 0) + returns += ", RIGHT"; + if ((button & static_cast(TasInput::TasButton::BUTTON_DOWN)) != 0) + returns += ", DOWN"; + if ((button & static_cast(TasInput::TasButton::BUTTON_SL)) != 0) + returns += ", SL"; + if ((button & static_cast(TasInput::TasButton::BUTTON_SR)) != 0) + returns += ", SR"; + if ((button & static_cast(TasInput::TasButton::BUTTON_HOME)) != 0) + returns += ", HOME"; + if ((button & static_cast(TasInput::TasButton::BUTTON_CAPTURE)) != 0) + returns += ", CAPTURE"; + return returns.length() != 0 ? returns.substr(2) : ""; + } + + void RefreshTasFile(); + void LoadTasFiles(); + void RecordInput(u32 buttons, std::array, 2> axes); + void UpdateThread(); + std::string GetStatusDescription(); + + InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; + InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; + [[nodiscard]] const TasData& GetTasState(std::size_t pad) const; + +private: + struct TASCommand { + u32 buttons{}; + TasAnalog l_axis{}; + TasAnalog r_axis{}; + }; + void LoadTasFile(int playerIndex); + void WriteTasFile(); + TasAnalog ReadCommandAxis(const std::string line) const; + u32 ReadCommandButtons(const std::string line) const; + std::string WriteCommandButtons(u32 data) const; + std::string WriteCommandAxis(TasAnalog data) const; + std::pair flipY(std::pair old) const; + + size_t scriptLength{0}; + std::array tas_data; + bool update_thread_running{true}; + bool refresh_tas_fle{false}; + std::array, PLAYER_NUMBER> newCommands{}; + std::vector recordCommands{}; + std::size_t current_command{0}; + TASCommand lastInput{}; // only used for recording +}; +} // namespace TasInput -- cgit v1.2.3 From 4297d2fea2228ff4afe2a7c244fb8b3f1a97491a Mon Sep 17 00:00:00 2001 From: MonsterDruide1 <5958456@gmail.com> Date: Fri, 18 Jun 2021 16:32:46 +0200 Subject: core: Hacky TAS syncing & load pausing To keep the TAS inputs synced to the game speed even through lag spikes and loading zones, deeper access is required. First, the `TAS::UpdateThread` has to be executed exactly once per frame. This is done by connecting it to the service method the game calls to pass parameters to the GPU: `Service::VI::QueueBuffer`. Second, the loading time of new subareas and/or kingdoms (SMO) can vary. To counteract that, the `CPU_BOOST_MODE` can be detected: In the `APM`-interface, the call to enabling/disabling the boost mode can be caught and forwarded to the TASing system, which can pause the script execution if neccessary and enabled in the settings. --- src/input_common/tas/tas_input.h | 58 ++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 38 deletions(-) (limited to 'src/input_common/tas/tas_input.h') diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h index 0a152a04f..8ee70bcaf 100644 --- a/src/input_common/tas/tas_input.h +++ b/src/input_common/tas/tas_input.h @@ -12,11 +12,17 @@ #include "core/frontend/input.h" #include "input_common/main.h" -#define PLAYER_NUMBER 8 - namespace TasInput { -using TasAnalog = std::tuple; +constexpr int PLAYER_NUMBER = 8; + +using TasAnalog = std::pair; + +enum class TasState { + RUNNING, + RECORDING, + STOPPED, +}; enum class TasButton : u32 { BUTTON_A = 0x000001, @@ -41,29 +47,6 @@ enum class TasButton : u32 { BUTTON_CAPTURE = 0x080000, }; -static const std::array, 20> text_to_tas_button = { - std::pair{"KEY_A", TasButton::BUTTON_A}, - {"KEY_B", TasButton::BUTTON_B}, - {"KEY_X", TasButton::BUTTON_X}, - {"KEY_Y", TasButton::BUTTON_Y}, - {"KEY_LSTICK", TasButton::STICK_L}, - {"KEY_RSTICK", TasButton::STICK_R}, - {"KEY_L", TasButton::TRIGGER_L}, - {"KEY_R", TasButton::TRIGGER_R}, - {"KEY_PLUS", TasButton::BUTTON_PLUS}, - {"KEY_MINUS", TasButton::BUTTON_MINUS}, - {"KEY_DLEFT", TasButton::BUTTON_LEFT}, - {"KEY_DUP", TasButton::BUTTON_UP}, - {"KEY_DRIGHT", TasButton::BUTTON_RIGHT}, - {"KEY_DDOWN", TasButton::BUTTON_DOWN}, - {"KEY_SL", TasButton::BUTTON_SL}, - {"KEY_SR", TasButton::BUTTON_SR}, - {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE}, - {"KEY_HOME", TasButton::BUTTON_HOME}, - {"KEY_ZL", TasButton::TRIGGER_ZL}, - {"KEY_ZR", TasButton::TRIGGER_ZR}, -}; - enum class TasAxes : u8 { StickX, StickY, @@ -82,7 +65,7 @@ public: Tas(); ~Tas(); - static std::string buttonsToString(u32 button) { + static std::string ButtonsToString(u32 button) { std::string returns; if ((button & static_cast(TasInput::TasButton::BUTTON_A)) != 0) returns += ", A"; @@ -124,14 +107,14 @@ public: returns += ", HOME"; if ((button & static_cast(TasInput::TasButton::BUTTON_CAPTURE)) != 0) returns += ", CAPTURE"; - return returns.length() != 0 ? returns.substr(2) : ""; + return returns.empty() ? "" : returns.substr(2); } void RefreshTasFile(); void LoadTasFiles(); - void RecordInput(u32 buttons, std::array, 2> axes); + void RecordInput(u32 buttons, const std::array, 2>& axes); void UpdateThread(); - std::string GetStatusDescription(); + std::tuple GetStatus(); InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; @@ -143,21 +126,20 @@ private: TasAnalog l_axis{}; TasAnalog r_axis{}; }; - void LoadTasFile(int playerIndex); + void LoadTasFile(size_t player_index); void WriteTasFile(); - TasAnalog ReadCommandAxis(const std::string line) const; - u32 ReadCommandButtons(const std::string line) const; + TasAnalog ReadCommandAxis(const std::string& line) const; + u32 ReadCommandButtons(const std::string& line) const; std::string WriteCommandButtons(u32 data) const; std::string WriteCommandAxis(TasAnalog data) const; - std::pair flipY(std::pair old) const; - size_t scriptLength{0}; + size_t script_length{0}; std::array tas_data; bool update_thread_running{true}; bool refresh_tas_fle{false}; - std::array, PLAYER_NUMBER> newCommands{}; - std::vector recordCommands{}; + std::array, PLAYER_NUMBER> commands{}; + std::vector record_commands{}; std::size_t current_command{0}; - TASCommand lastInput{}; // only used for recording + TASCommand last_input{}; // only used for recording }; } // namespace TasInput -- cgit v1.2.3 From c01a872c8efa90065b6ba1a74079ddf6ec12058f Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 19 Jun 2021 14:38:49 -0500 Subject: config: Move TAS options to it's own menu --- src/input_common/tas/tas_input.h | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'src/input_common/tas/tas_input.h') diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h index 8ee70bcaf..49ef10ff9 100644 --- a/src/input_common/tas/tas_input.h +++ b/src/input_common/tas/tas_input.h @@ -14,14 +14,14 @@ namespace TasInput { -constexpr int PLAYER_NUMBER = 8; +constexpr size_t PLAYER_NUMBER = 8; using TasAnalog = std::pair; enum class TasState { - RUNNING, - RECORDING, - STOPPED, + Running, + Recording, + Stopped, }; enum class TasButton : u32 { @@ -114,8 +114,19 @@ public: void LoadTasFiles(); void RecordInput(u32 buttons, const std::array, 2>& axes); void UpdateThread(); - std::tuple GetStatus(); + void StartStop(); + void Reset(); + void Record(); + + /** + * Returns the current status values of TAS playback/recording + * @return Tuple of + * TasState indicating the current state out of Running, Recording or Stopped ; + * Current playback progress or amount of frames (so far) for Recording ; + * Total length of script file currently loaded or amount of frames (so far) for Recording + */ + std::tuple GetStatus() const; InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; [[nodiscard]] const TasData& GetTasState(std::size_t pad) const; @@ -137,9 +148,12 @@ private: std::array tas_data; bool update_thread_running{true}; bool refresh_tas_fle{false}; + bool is_recording{false}; + bool is_running{false}; + bool needs_reset{false}; std::array, PLAYER_NUMBER> commands{}; std::vector record_commands{}; - std::size_t current_command{0}; + size_t current_command{0}; TASCommand last_input{}; // only used for recording }; } // namespace TasInput -- cgit v1.2.3 From f078b15565c8cab08587b8f8629d878615705cfb Mon Sep 17 00:00:00 2001 From: MonsterDruide1 <5958456@gmail.com> Date: Sun, 20 Jun 2021 00:04:34 +0200 Subject: input_common/tas: Fallback to simple update --- src/input_common/tas/tas_input.h | 59 ++++++---------------------------------- 1 file changed, 9 insertions(+), 50 deletions(-) (limited to 'src/input_common/tas/tas_input.h') diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h index 49ef10ff9..e011e559e 100644 --- a/src/input_common/tas/tas_input.h +++ b/src/input_common/tas/tas_input.h @@ -5,8 +5,6 @@ #pragma once #include -#include -#include #include "common/common_types.h" #include "core/frontend/input.h" @@ -65,53 +63,6 @@ public: Tas(); ~Tas(); - static std::string ButtonsToString(u32 button) { - std::string returns; - if ((button & static_cast(TasInput::TasButton::BUTTON_A)) != 0) - returns += ", A"; - if ((button & static_cast(TasInput::TasButton::BUTTON_B)) != 0) - returns += ", B"; - if ((button & static_cast(TasInput::TasButton::BUTTON_X)) != 0) - returns += ", X"; - if ((button & static_cast(TasInput::TasButton::BUTTON_Y)) != 0) - returns += ", Y"; - if ((button & static_cast(TasInput::TasButton::STICK_L)) != 0) - returns += ", STICK_L"; - if ((button & static_cast(TasInput::TasButton::STICK_R)) != 0) - returns += ", STICK_R"; - if ((button & static_cast(TasInput::TasButton::TRIGGER_L)) != 0) - returns += ", TRIGGER_L"; - if ((button & static_cast(TasInput::TasButton::TRIGGER_R)) != 0) - returns += ", TRIGGER_R"; - if ((button & static_cast(TasInput::TasButton::TRIGGER_ZL)) != 0) - returns += ", TRIGGER_ZL"; - if ((button & static_cast(TasInput::TasButton::TRIGGER_ZR)) != 0) - returns += ", TRIGGER_ZR"; - if ((button & static_cast(TasInput::TasButton::BUTTON_PLUS)) != 0) - returns += ", PLUS"; - if ((button & static_cast(TasInput::TasButton::BUTTON_MINUS)) != 0) - returns += ", MINUS"; - if ((button & static_cast(TasInput::TasButton::BUTTON_LEFT)) != 0) - returns += ", LEFT"; - if ((button & static_cast(TasInput::TasButton::BUTTON_UP)) != 0) - returns += ", UP"; - if ((button & static_cast(TasInput::TasButton::BUTTON_RIGHT)) != 0) - returns += ", RIGHT"; - if ((button & static_cast(TasInput::TasButton::BUTTON_DOWN)) != 0) - returns += ", DOWN"; - if ((button & static_cast(TasInput::TasButton::BUTTON_SL)) != 0) - returns += ", SL"; - if ((button & static_cast(TasInput::TasButton::BUTTON_SR)) != 0) - returns += ", SR"; - if ((button & static_cast(TasInput::TasButton::BUTTON_HOME)) != 0) - returns += ", HOME"; - if ((button & static_cast(TasInput::TasButton::BUTTON_CAPTURE)) != 0) - returns += ", CAPTURE"; - return returns.empty() ? "" : returns.substr(2); - } - - void RefreshTasFile(); - void LoadTasFiles(); void RecordInput(u32 buttons, const std::array, 2>& axes); void UpdateThread(); @@ -137,6 +88,7 @@ private: TasAnalog l_axis{}; TasAnalog r_axis{}; }; + void LoadTasFiles(); void LoadTasFile(size_t player_index); void WriteTasFile(); TasAnalog ReadCommandAxis(const std::string& line) const; @@ -144,9 +96,16 @@ private: std::string WriteCommandButtons(u32 data) const; std::string WriteCommandAxis(TasAnalog data) const; + std::pair FlipAxisY(std::pair old); + + std::string DebugButtons(u32 buttons) const; + std::string DebugJoystick(float x, float y) const; + std::string DebugInput(const TasData& data) const; + std::string DebugInputs(const std::array& arr) const; + std::string ButtonsToString(u32 button) const; + size_t script_length{0}; std::array tas_data; - bool update_thread_running{true}; bool refresh_tas_fle{false}; bool is_recording{false}; bool is_running{false}; -- cgit v1.2.3 From 9bb6580d89efb76534d9395bc052459d5f58e7c4 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 26 Jun 2021 10:38:39 -0500 Subject: input_common/tas: overwrite file dialog --- src/input_common/tas/tas_input.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/input_common/tas/tas_input.h') diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h index e011e559e..e0462e858 100644 --- a/src/input_common/tas/tas_input.h +++ b/src/input_common/tas/tas_input.h @@ -68,7 +68,8 @@ public: void StartStop(); void Reset(); - void Record(); + bool Record(); + void SaveRecording(bool overwrite_file); /** * Returns the current status values of TAS playback/recording @@ -90,7 +91,7 @@ private: }; void LoadTasFiles(); void LoadTasFile(size_t player_index); - void WriteTasFile(); + void WriteTasFile(std::string file_name); TasAnalog ReadCommandAxis(const std::string& line) const; u32 ReadCommandButtons(const std::string& line) const; std::string WriteCommandButtons(u32 data) const; @@ -106,7 +107,6 @@ private: size_t script_length{0}; std::array tas_data; - bool refresh_tas_fle{false}; bool is_recording{false}; bool is_running{false}; bool needs_reset{false}; -- cgit v1.2.3 From e6c4bf52f0eb2c9c78e983ffbc667891463d3253 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 27 Jun 2021 14:02:38 -0500 Subject: input_common/tas: Add swap controller --- src/input_common/tas/tas_input.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/input_common/tas/tas_input.h') diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h index e0462e858..e1f351251 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 -- cgit v1.2.3 From 33a1d790e8a5f67c73d0eef4a141f936345f104f Mon Sep 17 00:00:00 2001 From: german77 Date: Mon, 5 Jul 2021 20:58:52 -0500 Subject: input_common/tas: Document the main class --- src/input_common/tas/tas_input.h | 108 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) (limited to 'src/input_common/tas/tas_input.h') 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, 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 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 FlipAxisY(std::pair 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& 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}; -- cgit v1.2.3 From 75d8ec1e9f474ce6c2bfc0b8ebe574ca44f9f3d8 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 25 Jul 2021 20:52:19 -0500 Subject: UI: Relocate tas menu and add brief description --- src/input_common/tas/tas_input.h | 48 ++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'src/input_common/tas/tas_input.h') diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h index 52d000db4..3e2db8f00 100644 --- a/src/input_common/tas/tas_input.h +++ b/src/input_common/tas/tas_input.h @@ -13,8 +13,8 @@ /* 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). +Tools -> 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: @@ -56,26 +56,26 @@ enum class TasState { }; enum class TasButton : u32 { - BUTTON_A = 0x000001, - BUTTON_B = 0x000002, - BUTTON_X = 0x000004, - BUTTON_Y = 0x000008, - STICK_L = 0x000010, - STICK_R = 0x000020, - TRIGGER_L = 0x000040, - TRIGGER_R = 0x000080, - TRIGGER_ZL = 0x000100, - TRIGGER_ZR = 0x000200, - BUTTON_PLUS = 0x000400, - BUTTON_MINUS = 0x000800, - BUTTON_LEFT = 0x001000, - BUTTON_UP = 0x002000, - BUTTON_RIGHT = 0x004000, - BUTTON_DOWN = 0x008000, - BUTTON_SL = 0x010000, - BUTTON_SR = 0x020000, - BUTTON_HOME = 0x040000, - BUTTON_CAPTURE = 0x080000, + BUTTON_A = 1U << 0, + BUTTON_B = 1U << 1, + BUTTON_X = 1U << 2, + BUTTON_Y = 1U << 3, + STICK_L = 1U << 4, + STICK_R = 1U << 5, + TRIGGER_L = 1U << 6, + TRIGGER_R = 1U << 7, + TRIGGER_ZL = 1U << 8, + TRIGGER_ZR = 1U << 9, + BUTTON_PLUS = 1U << 10, + BUTTON_MINUS = 1U << 11, + BUTTON_LEFT = 1U << 12, + BUTTON_UP = 1U << 13, + BUTTON_RIGHT = 1U << 14, + BUTTON_DOWN = 1U << 15, + BUTTON_SL = 1U << 16, + BUTTON_SR = 1U << 17, + BUTTON_HOME = 1U << 18, + BUTTON_CAPTURE = 1U << 19, }; enum class TasAxes : u8 { @@ -105,6 +105,9 @@ public: // Sets the flag to start or stop the TAS command excecution and swaps controllers profiles void StartStop(); + // Stop the TAS and reverts any controller profile + void Stop(); + // Sets the flag to reload the file and start from the begining in the next update void Reset(); @@ -219,6 +222,7 @@ private: size_t script_length{0}; std::array tas_data; + bool is_old_input_saved{false}; bool is_recording{false}; bool is_running{false}; bool needs_reset{false}; -- cgit v1.2.3