| // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | |
| // SPDX-License-Identifier: GPL-2.0-or-later | |
| /* | |
| To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below | |
| 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: | |
| 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 InputCommon::TasInput { | |
| constexpr size_t PLAYER_NUMBER = 10; | |
| enum class TasButton : u64 { | |
| 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, | |
| }; | |
| struct TasAnalog { | |
| float x{}; | |
| float y{}; | |
| }; | |
| enum class TasState { | |
| Running, | |
| Recording, | |
| Stopped, | |
| }; | |
| class Tas final : public InputEngine { | |
| public: | |
| explicit Tas(std::string input_engine_); | |
| ~Tas() override; | |
| /** | |
| * Changes the input status that will be stored in each frame | |
| * @param buttons Bitfield with the status of the buttons | |
| * @param left_axis Value of the left axis | |
| * @param right_axis Value of the right axis | |
| */ | |
| void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis); | |
| // Main loop that records or executes input | |
| void UpdateThread(); | |
| // Sets the flag to start or stop the TAS command execution 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 beginning in the next update | |
| void Reset(); | |
| /** | |
| * Sets the flag to enable or disable recording of inputs | |
| * @returns true if the current recording status is enabled | |
| */ | |
| bool Record(); | |
| /** | |
| * Saves contents of record_commands on a file | |
| * @param overwrite_file Indicates if player 1 should be overwritten | |
| */ | |
| void SaveRecording(bool overwrite_file); | |
| /** | |
| * Returns the current status values of TAS playback/recording | |
| * @returns A Tuple of | |
| * TasState indicating the current state out of Running ; | |
| * Current playback progress ; | |
| * Total length of script file currently loaded or being recorded | |
| */ | |
| std::tuple<TasState, size_t, std::array<size_t, PLAYER_NUMBER>> GetStatus() const; | |
| private: | |
| enum class TasAxis : u8; | |
| struct TASCommand { | |
| u64 buttons{}; | |
| TasAnalog l_axis{}; | |
| TasAnalog r_axis{}; | |
| }; | |
| /// Loads TAS files from all players | |
| void LoadTasFiles(); | |
| /** | |
| * Loads TAS file from the specified player | |
| * @param player_index Player number to save the script | |
| * @param file_index Script number of the file | |
| */ | |
| void LoadTasFile(size_t player_index, size_t file_index); | |
| /** | |
| * Writes a TAS file from the recorded commands | |
| * @param file_name Name of the file to be written | |
| */ | |
| void WriteTasFile(std::u8string_view file_name); | |
| /** | |
| * Parses a string containing the axis values. X and Y have a range from -32767 to 32767 | |
| * @param line String containing axis values with the following format "x;y" | |
| * @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. Each button is represented by it's text format | |
| * specified in text_to_tas_button array | |
| * @param line string containing button name with the following format "a;b;c;d..." | |
| * @returns A u64 with each bit representing the status of a button | |
| */ | |
| u64 ReadCommandButtons(const std::string& line) const; | |
| /** | |
| * Reset state of all players | |
| */ | |
| void ClearInput(); | |
| /** | |
| * Converts an u64 containing the button status into the text equivalent | |
| * @param buttons Bitfield with the status of the buttons | |
| * @returns A string with the name of the buttons to be written to the file | |
| */ | |
| std::string WriteCommandButtons(u64 buttons) const; | |
| /** | |
| * Converts an TAS analog object containing the axis status into the text equivalent | |
| * @param analog Value of the axis | |
| * @returns A string with the value of the axis to be written to the file | |
| */ | |
| std::string WriteCommandAxis(TasAnalog analog) const; | |
| /// Sets an axis for a particular pad to the given value. | |
| void SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value); | |
| size_t script_length{0}; | |
| bool is_recording{false}; | |
| bool is_running{false}; | |
| bool needs_reset{false}; | |
| std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{}; | |
| std::vector<TASCommand> record_commands{}; | |
| size_t current_command{0}; | |
| TASCommand last_input{}; // only used for recording | |
| }; | |
| } // namespace InputCommon::TasInput | |