tas_input.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <array>
  5. #include <string>
  6. #include <vector>
  7. #include "common/common_types.h"
  8. #include "input_common/input_engine.h"
  9. /*
  10. To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
  11. Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt
  12. for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
  13. A script file has the same format as TAS-nx uses, so final files will look like this:
  14. 1 KEY_B 0;0 0;0
  15. 6 KEY_ZL 0;0 0;0
  16. 41 KEY_ZL;KEY_Y 0;0 0;0
  17. 43 KEY_X;KEY_A 32767;0 0;0
  18. 44 KEY_A 32767;0 0;0
  19. 45 KEY_A 32767;0 0;0
  20. 46 KEY_A 32767;0 0;0
  21. 47 KEY_A 32767;0 0;0
  22. After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey
  23. CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file
  24. has. Playback can be started or stopped using CTRL+F5.
  25. However, for playback to actually work, the correct input device has to be selected: In the Controls
  26. menu, select TAS from the device list for the controller that the script should be played on.
  27. Recording a new script file is really simple: Just make sure that the proper device (not TAS) is
  28. connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke
  29. again (CTRL+F7). The new script will be saved at the location previously selected, as the filename
  30. record.txt.
  31. For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller
  32. P1).
  33. */
  34. namespace InputCommon::TasInput {
  35. constexpr size_t PLAYER_NUMBER = 10;
  36. enum class TasButton : u64 {
  37. BUTTON_A = 1U << 0,
  38. BUTTON_B = 1U << 1,
  39. BUTTON_X = 1U << 2,
  40. BUTTON_Y = 1U << 3,
  41. STICK_L = 1U << 4,
  42. STICK_R = 1U << 5,
  43. TRIGGER_L = 1U << 6,
  44. TRIGGER_R = 1U << 7,
  45. TRIGGER_ZL = 1U << 8,
  46. TRIGGER_ZR = 1U << 9,
  47. BUTTON_PLUS = 1U << 10,
  48. BUTTON_MINUS = 1U << 11,
  49. BUTTON_LEFT = 1U << 12,
  50. BUTTON_UP = 1U << 13,
  51. BUTTON_RIGHT = 1U << 14,
  52. BUTTON_DOWN = 1U << 15,
  53. BUTTON_SL = 1U << 16,
  54. BUTTON_SR = 1U << 17,
  55. BUTTON_HOME = 1U << 18,
  56. BUTTON_CAPTURE = 1U << 19,
  57. };
  58. struct TasAnalog {
  59. float x{};
  60. float y{};
  61. };
  62. enum class TasState {
  63. Running,
  64. Recording,
  65. Stopped,
  66. };
  67. class Tas final : public InputEngine {
  68. public:
  69. explicit Tas(std::string input_engine_);
  70. ~Tas() override;
  71. /**
  72. * Changes the input status that will be stored in each frame
  73. * @param buttons Bitfield with the status of the buttons
  74. * @param left_axis Value of the left axis
  75. * @param right_axis Value of the right axis
  76. */
  77. void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis);
  78. // Main loop that records or executes input
  79. void UpdateThread();
  80. // Sets the flag to start or stop the TAS command execution and swaps controllers profiles
  81. void StartStop();
  82. // Stop the TAS and reverts any controller profile
  83. void Stop();
  84. // Sets the flag to reload the file and start from the beginning in the next update
  85. void Reset();
  86. /**
  87. * Sets the flag to enable or disable recording of inputs
  88. * @returns true if the current recording status is enabled
  89. */
  90. bool Record();
  91. /**
  92. * Saves contents of record_commands on a file
  93. * @param overwrite_file Indicates if player 1 should be overwritten
  94. */
  95. void SaveRecording(bool overwrite_file);
  96. /**
  97. * Returns the current status values of TAS playback/recording
  98. * @returns A Tuple of
  99. * TasState indicating the current state out of Running ;
  100. * Current playback progress ;
  101. * Total length of script file currently loaded or being recorded
  102. */
  103. std::tuple<TasState, size_t, std::array<size_t, PLAYER_NUMBER>> GetStatus() const;
  104. private:
  105. enum class TasAxis : u8;
  106. struct TASCommand {
  107. u64 buttons{};
  108. TasAnalog l_axis{};
  109. TasAnalog r_axis{};
  110. };
  111. /// Loads TAS files from all players
  112. void LoadTasFiles();
  113. /**
  114. * Loads TAS file from the specified player
  115. * @param player_index Player number to save the script
  116. * @param file_index Script number of the file
  117. */
  118. void LoadTasFile(size_t player_index, size_t file_index);
  119. /**
  120. * Writes a TAS file from the recorded commands
  121. * @param file_name Name of the file to be written
  122. */
  123. void WriteTasFile(std::u8string_view file_name);
  124. /**
  125. * Parses a string containing the axis values. X and Y have a range from -32767 to 32767
  126. * @param line String containing axis values with the following format "x;y"
  127. * @returns A TAS analog object with axis values with range from -1.0 to 1.0
  128. */
  129. TasAnalog ReadCommandAxis(const std::string& line) const;
  130. /**
  131. * Parses a string containing the button values. Each button is represented by it's text format
  132. * specified in text_to_tas_button array
  133. * @param line string containing button name with the following format "a;b;c;d..."
  134. * @returns A u64 with each bit representing the status of a button
  135. */
  136. u64 ReadCommandButtons(const std::string& line) const;
  137. /**
  138. * Reset state of all players
  139. */
  140. void ClearInput();
  141. /**
  142. * Converts an u64 containing the button status into the text equivalent
  143. * @param buttons Bitfield with the status of the buttons
  144. * @returns A string with the name of the buttons to be written to the file
  145. */
  146. std::string WriteCommandButtons(u64 buttons) const;
  147. /**
  148. * Converts an TAS analog object containing the axis status into the text equivalent
  149. * @param analog Value of the axis
  150. * @returns A string with the value of the axis to be written to the file
  151. */
  152. std::string WriteCommandAxis(TasAnalog analog) const;
  153. /// Sets an axis for a particular pad to the given value.
  154. void SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value);
  155. size_t script_length{0};
  156. bool is_recording{false};
  157. bool is_running{false};
  158. bool needs_reset{false};
  159. std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{};
  160. std::vector<TASCommand> record_commands{};
  161. size_t current_command{0};
  162. TASCommand last_input{}; // only used for recording
  163. };
  164. } // namespace InputCommon::TasInput