diff options
Diffstat (limited to 'src/input_common')
| -rw-r--r-- | src/input_common/main.cpp | 6 | ||||
| -rw-r--r-- | src/input_common/tas/tas_input.cpp | 189 | ||||
| -rw-r--r-- | src/input_common/tas/tas_input.h | 26 |
3 files changed, 130 insertions, 91 deletions
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 4f170493e..3b9906b53 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <thread> | 6 | #include <thread> |
| 7 | #include "common/param_package.h" | 7 | #include "common/param_package.h" |
| 8 | #include "common/settings.h" | ||
| 8 | #include "input_common/analog_from_button.h" | 9 | #include "input_common/analog_from_button.h" |
| 9 | #include "input_common/gcadapter/gc_adapter.h" | 10 | #include "input_common/gcadapter/gc_adapter.h" |
| 10 | #include "input_common/gcadapter/gc_poller.h" | 11 | #include "input_common/gcadapter/gc_poller.h" |
| @@ -114,8 +115,11 @@ struct InputSubsystem::Impl { | |||
| 114 | std::vector<Common::ParamPackage> devices = { | 115 | std::vector<Common::ParamPackage> devices = { |
| 115 | Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, | 116 | Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, |
| 116 | Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}}, | 117 | Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}}, |
| 117 | Common::ParamPackage{{"display", "TAS"}, {"class", "tas"}}, | ||
| 118 | }; | 118 | }; |
| 119 | if (Settings::values.tas_enable) { | ||
| 120 | devices.push_back( | ||
| 121 | Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}}); | ||
| 122 | } | ||
| 119 | #ifdef HAVE_SDL2 | 123 | #ifdef HAVE_SDL2 |
| 120 | auto sdl_devices = sdl->GetInputDevices(); | 124 | auto sdl_devices = sdl->GetInputDevices(); |
| 121 | devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); | 125 | devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); |
diff --git a/src/input_common/tas/tas_input.cpp b/src/input_common/tas/tas_input.cpp index 7320a7004..6efa1234a 100644 --- a/src/input_common/tas/tas_input.cpp +++ b/src/input_common/tas/tas_input.cpp | |||
| @@ -67,14 +67,13 @@ void Tas::LoadTasFile(size_t player_index) { | |||
| 67 | if (!commands[player_index].empty()) { | 67 | if (!commands[player_index].empty()) { |
| 68 | commands[player_index].clear(); | 68 | commands[player_index].clear(); |
| 69 | } | 69 | } |
| 70 | std::string file = Common::FS::ReadStringFromFile( | 70 | std::string file = |
| 71 | Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASFile) + "script0-" + | 71 | Common::FS::ReadStringFromFile(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir) + |
| 72 | std::to_string(player_index + 1) + ".txt", | 72 | "script0-" + std::to_string(player_index + 1) + ".txt", |
| 73 | Common::FS::FileType::BinaryFile); | 73 | Common::FS::FileType::BinaryFile); |
| 74 | std::stringstream command_line(file); | 74 | std::stringstream command_line(file); |
| 75 | std::string line; | 75 | std::string line; |
| 76 | int frameNo = 0; | 76 | int frame_no = 0; |
| 77 | TASCommand empty = {.buttons = 0, .l_axis = {0.f, 0.f}, .r_axis = {0.f, 0.f}}; | ||
| 78 | while (std::getline(command_line, line, '\n')) { | 77 | while (std::getline(command_line, line, '\n')) { |
| 79 | if (line.empty()) { | 78 | if (line.empty()) { |
| 80 | continue; | 79 | continue; |
| @@ -94,9 +93,9 @@ void Tas::LoadTasFile(size_t player_index) { | |||
| 94 | continue; | 93 | continue; |
| 95 | } | 94 | } |
| 96 | 95 | ||
| 97 | while (frameNo < std::stoi(seglist.at(0))) { | 96 | while (frame_no < std::stoi(seglist.at(0))) { |
| 98 | commands[player_index].push_back(empty); | 97 | commands[player_index].push_back({}); |
| 99 | frameNo++; | 98 | frame_no++; |
| 100 | } | 99 | } |
| 101 | 100 | ||
| 102 | TASCommand command = { | 101 | TASCommand command = { |
| @@ -105,30 +104,29 @@ void Tas::LoadTasFile(size_t player_index) { | |||
| 105 | .r_axis = ReadCommandAxis(seglist.at(3)), | 104 | .r_axis = ReadCommandAxis(seglist.at(3)), |
| 106 | }; | 105 | }; |
| 107 | commands[player_index].push_back(command); | 106 | commands[player_index].push_back(command); |
| 108 | frameNo++; | 107 | frame_no++; |
| 109 | } | 108 | } |
| 110 | LOG_INFO(Input, "TAS file loaded! {} frames", frameNo); | 109 | LOG_INFO(Input, "TAS file loaded! {} frames", frame_no); |
| 111 | } | 110 | } |
| 112 | 111 | ||
| 113 | void Tas::WriteTasFile() { | 112 | void Tas::WriteTasFile() { |
| 114 | LOG_DEBUG(Input, "WriteTasFile()"); | 113 | LOG_DEBUG(Input, "WriteTasFile()"); |
| 115 | std::string output_text = ""; | 114 | std::string output_text; |
| 116 | for (int frame = 0; frame < (signed)record_commands.size(); frame++) { | 115 | for (size_t frame = 0; frame < record_commands.size(); frame++) { |
| 117 | if (!output_text.empty()) { | 116 | if (!output_text.empty()) { |
| 118 | output_text += "\n"; | 117 | output_text += "\n"; |
| 119 | } | 118 | } |
| 120 | TASCommand line = record_commands.at(frame); | 119 | const TASCommand& line = record_commands[frame]; |
| 121 | output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " + | 120 | output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " + |
| 122 | WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis); | 121 | WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis); |
| 123 | } | 122 | } |
| 124 | size_t bytesWritten = Common::FS::WriteStringToFile( | 123 | const size_t bytes_written = Common::FS::WriteStringToFile( |
| 125 | Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASFile) + "record.txt", | 124 | Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir) + "record.txt", |
| 126 | Common::FS::FileType::TextFile, output_text); | 125 | Common::FS::FileType::TextFile, output_text); |
| 127 | if (bytesWritten == output_text.size()) { | 126 | if (bytes_written == output_text.size()) { |
| 128 | LOG_INFO(Input, "TAS file written to file!"); | 127 | LOG_INFO(Input, "TAS file written to file!"); |
| 129 | } | 128 | } else { |
| 130 | else { | 129 | LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written, |
| 131 | LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytesWritten, | ||
| 132 | output_text.size()); | 130 | output_text.size()); |
| 133 | } | 131 | } |
| 134 | } | 132 | } |
| @@ -142,30 +140,33 @@ void Tas::RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& | |||
| 142 | last_input = {buttons, FlipY(axes[0]), FlipY(axes[1])}; | 140 | last_input = {buttons, FlipY(axes[0]), FlipY(axes[1])}; |
| 143 | } | 141 | } |
| 144 | 142 | ||
| 145 | std::tuple<TasState, size_t, size_t> Tas::GetStatus() { | 143 | std::tuple<TasState, size_t, size_t> Tas::GetStatus() const { |
| 146 | TasState state; | 144 | TasState state; |
| 147 | if (Settings::values.tas_record) { | 145 | if (is_recording) { |
| 148 | return {TasState::RECORDING, record_commands.size(), record_commands.size()}; | 146 | return {TasState::Recording, 0, record_commands.size()}; |
| 149 | } else if (Settings::values.tas_enable) { | 147 | } |
| 150 | state = TasState::RUNNING; | 148 | |
| 149 | if (is_running) { | ||
| 150 | state = TasState::Running; | ||
| 151 | } else { | 151 | } else { |
| 152 | state = TasState::STOPPED; | 152 | state = TasState::Stopped; |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | return {state, current_command, script_length}; | 155 | return {state, current_command, script_length}; |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | static std::string DebugButtons(u32 buttons) { | 158 | static std::string DebugButtons(u32 buttons) { |
| 159 | return "{ " + TasInput::Tas::ButtonsToString(buttons) + " }"; | 159 | return fmt::format("{{ {} }}", TasInput::Tas::ButtonsToString(buttons)); |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | static std::string DebugJoystick(float x, float y) { | 162 | static std::string DebugJoystick(float x, float y) { |
| 163 | return "[ " + std::to_string(x) + "," + std::to_string(y) + " ]"; | 163 | return fmt::format("[ {} , {} ]", std::to_string(x), std::to_string(y)); |
| 164 | } | 164 | } |
| 165 | 165 | ||
| 166 | static std::string DebugInput(const TasData& data) { | 166 | static std::string DebugInput(const TasData& data) { |
| 167 | return "{ " + DebugButtons(data.buttons) + " , " + DebugJoystick(data.axis[0], data.axis[1]) + | 167 | return fmt::format("{{ {} , {} , {} }}", DebugButtons(data.buttons), |
| 168 | " , " + DebugJoystick(data.axis[2], data.axis[3]) + " }"; | 168 | DebugJoystick(data.axis[0], data.axis[1]), |
| 169 | DebugJoystick(data.axis[2], data.axis[3])); | ||
| 169 | } | 170 | } |
| 170 | 171 | ||
| 171 | static std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) { | 172 | static std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) { |
| @@ -180,66 +181,54 @@ static std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) { | |||
| 180 | } | 181 | } |
| 181 | 182 | ||
| 182 | void Tas::UpdateThread() { | 183 | void Tas::UpdateThread() { |
| 183 | if (update_thread_running) { | 184 | if (!update_thread_running) { |
| 184 | if (Settings::values.pause_tas_on_load && Settings::values.is_cpu_boosted) { | 185 | return; |
| 185 | for (size_t i = 0; i < PLAYER_NUMBER; i++) { | 186 | } |
| 186 | tas_data[i].buttons = 0; | ||
| 187 | tas_data[i].axis = {}; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | 187 | ||
| 191 | if (Settings::values.tas_record) { | 188 | if (is_recording) { |
| 192 | record_commands.push_back(last_input); | 189 | record_commands.push_back(last_input); |
| 193 | } | 190 | } |
| 194 | if (!Settings::values.tas_record && !record_commands.empty()) { | 191 | if (!is_recording && !record_commands.empty()) { |
| 195 | WriteTasFile(); | 192 | WriteTasFile(); |
| 196 | Settings::values.tas_reset = true; | 193 | needs_reset = true; |
| 197 | refresh_tas_fle = true; | 194 | refresh_tas_fle = true; |
| 198 | record_commands.clear(); | 195 | record_commands.clear(); |
| 199 | } | 196 | } |
| 200 | if (Settings::values.tas_reset) { | 197 | if (needs_reset) { |
| 201 | current_command = 0; | 198 | current_command = 0; |
| 202 | if (refresh_tas_fle) { | 199 | if (refresh_tas_fle) { |
| 203 | LoadTasFiles(); | ||
| 204 | refresh_tas_fle = false; | ||
| 205 | } | ||
| 206 | Settings::values.tas_reset = false; | ||
| 207 | LoadTasFiles(); | 200 | LoadTasFiles(); |
| 208 | LOG_DEBUG(Input, "tas_reset done"); | 201 | refresh_tas_fle = false; |
| 209 | } | 202 | } |
| 210 | if (Settings::values.tas_enable) { | 203 | needs_reset = false; |
| 211 | if ((signed)current_command < script_length) { | 204 | LoadTasFiles(); |
| 212 | LOG_INFO(Input, "Playing TAS {}/{}", current_command, script_length); | 205 | LOG_DEBUG(Input, "tas_reset done"); |
| 213 | size_t frame = current_command++; | 206 | } |
| 214 | for (size_t i = 0; i < PLAYER_NUMBER; i++) { | 207 | if (is_running) { |
| 215 | if (frame < commands[i].size()) { | 208 | if (current_command < script_length) { |
| 216 | TASCommand command = commands[i][frame]; | 209 | LOG_INFO(Input, "Playing TAS {}/{}", current_command, script_length); |
| 217 | tas_data[i].buttons = command.buttons; | 210 | size_t frame = current_command++; |
| 218 | auto [l_axis_x, l_axis_y] = command.l_axis; | 211 | for (size_t i = 0; i < PLAYER_NUMBER; i++) { |
| 219 | tas_data[i].axis[0] = l_axis_x; | 212 | if (frame < commands[i].size()) { |
| 220 | tas_data[i].axis[1] = l_axis_y; | 213 | TASCommand command = commands[i][frame]; |
| 221 | auto [r_axis_x, r_axis_y] = command.r_axis; | 214 | tas_data[i].buttons = command.buttons; |
| 222 | tas_data[i].axis[2] = r_axis_x; | 215 | auto [l_axis_x, l_axis_y] = command.l_axis; |
| 223 | tas_data[i].axis[3] = r_axis_y; | 216 | tas_data[i].axis[0] = l_axis_x; |
| 224 | } else { | 217 | tas_data[i].axis[1] = l_axis_y; |
| 225 | tas_data[i].buttons = 0; | 218 | auto [r_axis_x, r_axis_y] = command.r_axis; |
| 226 | tas_data[i].axis = {}; | 219 | tas_data[i].axis[2] = r_axis_x; |
| 227 | } | 220 | tas_data[i].axis[3] = r_axis_y; |
| 228 | } | 221 | } else { |
| 229 | } else { | 222 | tas_data[i] = {}; |
| 230 | Settings::values.tas_enable = false; | ||
| 231 | current_command = 0; | ||
| 232 | for (size_t i = 0; i < PLAYER_NUMBER; i++) { | ||
| 233 | tas_data[i].buttons = 0; | ||
| 234 | tas_data[i].axis = {}; | ||
| 235 | } | 223 | } |
| 236 | } | 224 | } |
| 237 | } else { | 225 | } else { |
| 238 | for (size_t i = 0; i < PLAYER_NUMBER; i++) { | 226 | is_running = Settings::values.tas_loop; |
| 239 | tas_data[i].buttons = 0; | 227 | current_command = 0; |
| 240 | tas_data[i].axis = {}; | 228 | tas_data.fill({}); |
| 241 | } | ||
| 242 | } | 229 | } |
| 230 | } else { | ||
| 231 | tas_data.fill({}); | ||
| 243 | } | 232 | } |
| 244 | LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data)); | 233 | LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data)); |
| 245 | } | 234 | } |
| @@ -284,8 +273,9 @@ std::string Tas::WriteCommandAxis(TasAnalog data) const { | |||
| 284 | } | 273 | } |
| 285 | 274 | ||
| 286 | std::string Tas::WriteCommandButtons(u32 data) const { | 275 | std::string Tas::WriteCommandButtons(u32 data) const { |
| 287 | if (data == 0) | 276 | if (data == 0) { |
| 288 | return "NONE"; | 277 | return "NONE"; |
| 278 | } | ||
| 289 | 279 | ||
| 290 | std::string line; | 280 | std::string line; |
| 291 | u32 index = 0; | 281 | u32 index = 0; |
| @@ -307,6 +297,37 @@ std::string Tas::WriteCommandButtons(u32 data) const { | |||
| 307 | return line; | 297 | return line; |
| 308 | } | 298 | } |
| 309 | 299 | ||
| 300 | void Tas::StartStop() { | ||
| 301 | is_running = !is_running; | ||
| 302 | } | ||
| 303 | |||
| 304 | void Tas::Reset() { | ||
| 305 | needs_reset = true; | ||
| 306 | } | ||
| 307 | |||
| 308 | void Tas::Record() { | ||
| 309 | is_recording = !is_recording; | ||
| 310 | <<<<<<< HEAD | ||
| 311 | ======= | ||
| 312 | return is_recording; | ||
| 313 | } | ||
| 314 | |||
| 315 | void Tas::SaveRecording(bool overwrite_file) { | ||
| 316 | if (is_recording) { | ||
| 317 | return; | ||
| 318 | } | ||
| 319 | if (record_commands.empty()) { | ||
| 320 | return; | ||
| 321 | } | ||
| 322 | WriteTasFile("record.txt"); | ||
| 323 | if (overwrite_file) { | ||
| 324 | WriteTasFile("script0-1.txt"); | ||
| 325 | } | ||
| 326 | needs_reset = true; | ||
| 327 | record_commands.clear(); | ||
| 328 | >>>>>>> 773d268db (config: disable pause on load) | ||
| 329 | } | ||
| 330 | |||
| 310 | InputCommon::ButtonMapping Tas::GetButtonMappingForDevice( | 331 | InputCommon::ButtonMapping Tas::GetButtonMappingForDevice( |
| 311 | const Common::ParamPackage& params) const { | 332 | const Common::ParamPackage& params) const { |
| 312 | // This list is missing ZL/ZR since those are not considered buttons. | 333 | // This list is missing ZL/ZR since those are not considered buttons. |
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 @@ | |||
| 14 | 14 | ||
| 15 | namespace TasInput { | 15 | namespace TasInput { |
| 16 | 16 | ||
| 17 | constexpr int PLAYER_NUMBER = 8; | 17 | constexpr size_t PLAYER_NUMBER = 8; |
| 18 | 18 | ||
| 19 | using TasAnalog = std::pair<float, float>; | 19 | using TasAnalog = std::pair<float, float>; |
| 20 | 20 | ||
| 21 | enum class TasState { | 21 | enum class TasState { |
| 22 | RUNNING, | 22 | Running, |
| 23 | RECORDING, | 23 | Recording, |
| 24 | STOPPED, | 24 | Stopped, |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | enum class TasButton : u32 { | 27 | enum class TasButton : u32 { |
| @@ -114,8 +114,19 @@ public: | |||
| 114 | void LoadTasFiles(); | 114 | void LoadTasFiles(); |
| 115 | void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes); | 115 | void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes); |
| 116 | void UpdateThread(); | 116 | void UpdateThread(); |
| 117 | std::tuple<TasState, size_t, size_t> GetStatus(); | ||
| 118 | 117 | ||
| 118 | void StartStop(); | ||
| 119 | void Reset(); | ||
| 120 | void Record(); | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Returns the current status values of TAS playback/recording | ||
| 124 | * @return Tuple of | ||
| 125 | * TasState indicating the current state out of Running, Recording or Stopped ; | ||
| 126 | * Current playback progress or amount of frames (so far) for Recording ; | ||
| 127 | * Total length of script file currently loaded or amount of frames (so far) for Recording | ||
| 128 | */ | ||
| 129 | std::tuple<TasState, size_t, size_t> GetStatus() const; | ||
| 119 | InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; | 130 | InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; |
| 120 | InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; | 131 | InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; |
| 121 | [[nodiscard]] const TasData& GetTasState(std::size_t pad) const; | 132 | [[nodiscard]] const TasData& GetTasState(std::size_t pad) const; |
| @@ -137,9 +148,12 @@ private: | |||
| 137 | std::array<TasData, PLAYER_NUMBER> tas_data; | 148 | std::array<TasData, PLAYER_NUMBER> tas_data; |
| 138 | bool update_thread_running{true}; | 149 | bool update_thread_running{true}; |
| 139 | bool refresh_tas_fle{false}; | 150 | bool refresh_tas_fle{false}; |
| 151 | bool is_recording{false}; | ||
| 152 | bool is_running{false}; | ||
| 153 | bool needs_reset{false}; | ||
| 140 | std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{}; | 154 | std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{}; |
| 141 | std::vector<TASCommand> record_commands{}; | 155 | std::vector<TASCommand> record_commands{}; |
| 142 | std::size_t current_command{0}; | 156 | size_t current_command{0}; |
| 143 | TASCommand last_input{}; // only used for recording | 157 | TASCommand last_input{}; // only used for recording |
| 144 | }; | 158 | }; |
| 145 | } // namespace TasInput | 159 | } // namespace TasInput |