summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar MonsterDruide12021-06-18 16:32:46 +0200
committerGravatar MonsterDruide12021-09-18 23:22:20 +0200
commit4297d2fea2228ff4afe2a7c244fb8b3f1a97491a (patch)
tree25a1ce3a2d41bf9e066c7a57a441be65e282f16f
parentmain: TAS Playback state label (diff)
downloadyuzu-4297d2fea2228ff4afe2a7c244fb8b3f1a97491a.tar.gz
yuzu-4297d2fea2228ff4afe2a7c244fb8b3f1a97491a.tar.xz
yuzu-4297d2fea2228ff4afe2a7c244fb8b3f1a97491a.zip
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.
-rw-r--r--src/common/fs/fs_paths.h1
-rw-r--r--src/common/fs/path_util.cpp3
-rw-r--r--src/common/settings.h9
-rw-r--r--src/core/hle/service/apm/apm_interface.cpp2
-rw-r--r--src/core/hle/service/vi/vi.cpp3
-rw-r--r--src/input_common/tas/tas_input.cpp142
-rw-r--r--src/input_common/tas/tas_input.h58
-rw-r--r--src/yuzu/configuration/configure_filesystem.cpp2
-rw-r--r--src/yuzu/main.cpp27
9 files changed, 140 insertions, 107 deletions
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h
index b32614797..84968b8e0 100644
--- a/src/common/fs/fs_paths.h
+++ b/src/common/fs/fs_paths.h
@@ -21,6 +21,7 @@
21#define SCREENSHOTS_DIR "screenshots" 21#define SCREENSHOTS_DIR "screenshots"
22#define SDMC_DIR "sdmc" 22#define SDMC_DIR "sdmc"
23#define SHADER_DIR "shader" 23#define SHADER_DIR "shader"
24#define TAS_DIR "scripts"
24 25
25// yuzu-specific files 26// yuzu-specific files
26 27
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 5f76adedb..97d026eb8 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -116,8 +116,7 @@ private:
116 GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR); 116 GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
117 GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR); 117 GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
118 GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR); 118 GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
119 119 GenerateYuzuPath(YuzuPath::TASFile, yuzu_path / TAS_DIR);
120 GenerateYuzuPath(YuzuPath::TASFile, fs::path{""});
121 } 120 }
122 121
123 ~PathManagerImpl() = default; 122 ~PathManagerImpl() = default;
diff --git a/src/common/settings.h b/src/common/settings.h
index 884ea55f8..7333a64dc 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -500,7 +500,6 @@ struct Values {
500 500
501 // Controls 501 // Controls
502 InputSetting<std::array<PlayerInput, 10>> players; 502 InputSetting<std::array<PlayerInput, 10>> players;
503 std::shared_ptr<InputCommon::InputSubsystem> inputSubsystem = NULL;
504 503
505 Setting<bool> use_docked_mode{true, "use_docked_mode"}; 504 Setting<bool> use_docked_mode{true, "use_docked_mode"};
506 505
@@ -514,9 +513,12 @@ struct Values {
514 "motion_device"}; 513 "motion_device"};
515 BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; 514 BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
516 515
517 BasicSetting<bool> tas_enable{false, "tas_enable"}; 516 BasicSetting<bool> pause_tas_on_load { false, "pause_tas_on_load" };
517 BasicSetting<bool> tas_enable{ false, "tas_enable" };
518 BasicSetting<bool> tas_reset{ false, "tas_reset" }; 518 BasicSetting<bool> tas_reset{ false, "tas_reset" };
519 BasicSetting<bool> tas_record{ false, "tas_record" }; 519 BasicSetting<bool> tas_record{ false, "tas_record" };
520 BasicSetting<bool> is_cpu_boxted{ false, " BasicSetting<bool> is_cpu_boxted{ false, "cpuBoosted" };
521" };
520 522
521 BasicSetting<bool> mouse_panning{false, "mouse_panning"}; 523 BasicSetting<bool> mouse_panning{false, "mouse_panning"};
522 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; 524 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
@@ -550,9 +552,6 @@ struct Values {
550 BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"}; 552 BasicSetting<bool> gamecard_current_game{false, "gamecard_current_game"};
551 BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"}; 553 BasicSetting<std::string> gamecard_path{std::string(), "gamecard_path"};
552 554
553 // TAS
554 bool pauseTasOnLoad;
555
556 // Debugging 555 // Debugging
557 bool record_frame_times; 556 bool record_frame_times;
558 BasicSetting<bool> use_gdbstub{false, "use_gdbstub"}; 557 BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
diff --git a/src/core/hle/service/apm/apm_interface.cpp b/src/core/hle/service/apm/apm_interface.cpp
index e58bad083..724483107 100644
--- a/src/core/hle/service/apm/apm_interface.cpp
+++ b/src/core/hle/service/apm/apm_interface.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/settings.h"
6#include "core/hle/ipc_helpers.h" 7#include "core/hle/ipc_helpers.h"
7#include "core/hle/service/apm/apm.h" 8#include "core/hle/service/apm/apm.h"
8#include "core/hle/service/apm/apm_controller.h" 9#include "core/hle/service/apm/apm_controller.h"
@@ -120,6 +121,7 @@ void APM_Sys::SetCpuBoostMode(Kernel::HLERequestContext& ctx) {
120 121
121 LOG_DEBUG(Service_APM, "called, mode={:08X}", mode); 122 LOG_DEBUG(Service_APM, "called, mode={:08X}", mode);
122 123
124 Settings::values.is_cpu_boosted = (static_cast<u32>(mode) == 1);
123 controller.SetFromCpuBoostMode(mode); 125 controller.SetFromCpuBoostMode(mode);
124 126
125 IPC::ResponseBuilder rb{ctx, 2}; 127 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 8e8fc40ca..f4eac0bca 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -32,6 +32,8 @@
32#include "core/hle/service/vi/vi_s.h" 32#include "core/hle/service/vi/vi_s.h"
33#include "core/hle/service/vi/vi_u.h" 33#include "core/hle/service/vi/vi_u.h"
34 34
35#include "input_common/tas/tas_input.h"
36
35namespace Service::VI { 37namespace Service::VI {
36 38
37constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1}; 39constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
@@ -595,6 +597,7 @@ private:
595 597
596 IGBPQueueBufferResponseParcel response{1280, 720}; 598 IGBPQueueBufferResponseParcel response{1280, 720};
597 ctx.WriteBuffer(response.Serialize()); 599 ctx.WriteBuffer(response.Serialize());
600 Settings::values.input_subsystem->GetTas()->UpdateThread();
598 break; 601 break;
599 } 602 }
600 case TransactionId::Query: { 603 case TransactionId::Query: {
diff --git a/src/input_common/tas/tas_input.cpp b/src/input_common/tas/tas_input.cpp
index 343641945..7320a7004 100644
--- a/src/input_common/tas/tas_input.cpp
+++ b/src/input_common/tas/tas_input.cpp
@@ -19,6 +19,29 @@
19 19
20namespace TasInput { 20namespace TasInput {
21 21
22constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
23 std::pair{"KEY_A", TasButton::BUTTON_A},
24 {"KEY_B", TasButton::BUTTON_B},
25 {"KEY_X", TasButton::BUTTON_X},
26 {"KEY_Y", TasButton::BUTTON_Y},
27 {"KEY_LSTICK", TasButton::STICK_L},
28 {"KEY_RSTICK", TasButton::STICK_R},
29 {"KEY_L", TasButton::TRIGGER_L},
30 {"KEY_R", TasButton::TRIGGER_R},
31 {"KEY_PLUS", TasButton::BUTTON_PLUS},
32 {"KEY_MINUS", TasButton::BUTTON_MINUS},
33 {"KEY_DLEFT", TasButton::BUTTON_LEFT},
34 {"KEY_DUP", TasButton::BUTTON_UP},
35 {"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
36 {"KEY_DDOWN", TasButton::BUTTON_DOWN},
37 {"KEY_SL", TasButton::BUTTON_SL},
38 {"KEY_SR", TasButton::BUTTON_SR},
39 {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
40 {"KEY_HOME", TasButton::BUTTON_HOME},
41 {"KEY_ZL", TasButton::TRIGGER_ZL},
42 {"KEY_ZR", TasButton::TRIGGER_ZR},
43};
44
22Tas::Tas() { 45Tas::Tas() {
23 LoadTasFiles(); 46 LoadTasFiles();
24} 47}
@@ -31,29 +54,31 @@ void Tas::RefreshTasFile() {
31 refresh_tas_fle = true; 54 refresh_tas_fle = true;
32} 55}
33void Tas::LoadTasFiles() { 56void Tas::LoadTasFiles() {
34 scriptLength = 0; 57 script_length = 0;
35 for (int i = 0; i < PLAYER_NUMBER; i++) { 58 for (size_t i = 0; i < PLAYER_NUMBER; i++) {
36 LoadTasFile(i); 59 LoadTasFile(i);
37 if (newCommands[i].size() > scriptLength) 60 if (commands[i].size() > script_length) {
38 scriptLength = newCommands[i].size(); 61 script_length = commands[i].size();
62 }
39 } 63 }
40} 64}
41void Tas::LoadTasFile(int playerIndex) { 65void Tas::LoadTasFile(size_t player_index) {
42 LOG_DEBUG(Input, "LoadTasFile()"); 66 LOG_DEBUG(Input, "LoadTasFile()");
43 if (!newCommands[playerIndex].empty()) { 67 if (!commands[player_index].empty()) {
44 newCommands[playerIndex].clear(); 68 commands[player_index].clear();
45 } 69 }
46 std::string file = Common::FS::ReadStringFromFile( 70 std::string file = Common::FS::ReadStringFromFile(
47 Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASFile) + "script0-" + 71 Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASFile) + "script0-" +
48 std::to_string(playerIndex + 1) + ".txt", 72 std::to_string(player_index + 1) + ".txt",
49 Common::FS::FileType::BinaryFile); 73 Common::FS::FileType::BinaryFile);
50 std::stringstream command_line(file); 74 std::stringstream command_line(file);
51 std::string line; 75 std::string line;
52 int frameNo = 0; 76 int frameNo = 0;
53 TASCommand empty = {.buttons = 0, .l_axis = {0.f, 0.f}, .r_axis = {0.f, 0.f}}; 77 TASCommand empty = {.buttons = 0, .l_axis = {0.f, 0.f}, .r_axis = {0.f, 0.f}};
54 while (std::getline(command_line, line, '\n')) { 78 while (std::getline(command_line, line, '\n')) {
55 if (line.empty()) 79 if (line.empty()) {
56 continue; 80 continue;
81 }
57 LOG_DEBUG(Input, "Loading line: {}", line); 82 LOG_DEBUG(Input, "Loading line: {}", line);
58 std::smatch m; 83 std::smatch m;
59 84
@@ -65,11 +90,12 @@ void Tas::LoadTasFile(int playerIndex) {
65 seglist.push_back(segment); 90 seglist.push_back(segment);
66 } 91 }
67 92
68 if (seglist.size() < 4) 93 if (seglist.size() < 4) {
69 continue; 94 continue;
95 }
70 96
71 while (frameNo < std::stoi(seglist.at(0))) { 97 while (frameNo < std::stoi(seglist.at(0))) {
72 newCommands[playerIndex].push_back(empty); 98 commands[player_index].push_back(empty);
73 frameNo++; 99 frameNo++;
74 } 100 }
75 101
@@ -78,7 +104,7 @@ void Tas::LoadTasFile(int playerIndex) {
78 .l_axis = ReadCommandAxis(seglist.at(2)), 104 .l_axis = ReadCommandAxis(seglist.at(2)),
79 .r_axis = ReadCommandAxis(seglist.at(3)), 105 .r_axis = ReadCommandAxis(seglist.at(3)),
80 }; 106 };
81 newCommands[playerIndex].push_back(command); 107 commands[player_index].push_back(command);
82 frameNo++; 108 frameNo++;
83 } 109 }
84 LOG_INFO(Input, "TAS file loaded! {} frames", frameNo); 110 LOG_INFO(Input, "TAS file loaded! {} frames", frameNo);
@@ -87,84 +113,89 @@ void Tas::LoadTasFile(int playerIndex) {
87void Tas::WriteTasFile() { 113void Tas::WriteTasFile() {
88 LOG_DEBUG(Input, "WriteTasFile()"); 114 LOG_DEBUG(Input, "WriteTasFile()");
89 std::string output_text = ""; 115 std::string output_text = "";
90 for (int frame = 0; frame < (signed)recordCommands.size(); frame++) { 116 for (int frame = 0; frame < (signed)record_commands.size(); frame++) {
91 if (!output_text.empty()) 117 if (!output_text.empty()) {
92 output_text += "\n"; 118 output_text += "\n";
93 TASCommand line = recordCommands.at(frame); 119 }
120 TASCommand line = record_commands.at(frame);
94 output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " + 121 output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " +
95 WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis); 122 WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis);
96 } 123 }
97 size_t bytesWritten = Common::FS::WriteStringToFile( 124 size_t bytesWritten = Common::FS::WriteStringToFile(
98 Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASFile) + "record.txt", 125 Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASFile) + "record.txt",
99 Common::FS::FileType::TextFile, output_text); 126 Common::FS::FileType::TextFile, output_text);
100 if (bytesWritten == output_text.size()) 127 if (bytesWritten == output_text.size()) {
101 LOG_INFO(Input, "TAS file written to file!"); 128 LOG_INFO(Input, "TAS file written to file!");
102 else 129 }
130 else {
103 LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytesWritten, 131 LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytesWritten,
104 output_text.size()); 132 output_text.size());
133 }
105} 134}
106 135
107void Tas::RecordInput(u32 buttons, std::array<std::pair<float, float>, 2> axes) { 136static std::pair<float, float> FlipY(std::pair<float, float> old) {
108 lastInput = {buttons, flipY(axes[0]), flipY(axes[1])};
109}
110
111std::pair<float, float> Tas::flipY(std::pair<float, float> old) const {
112 auto [x, y] = old; 137 auto [x, y] = old;
113 return {x, -y}; 138 return {x, -y};
114} 139}
115 140
116std::string Tas::GetStatusDescription() { 141void Tas::RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes) {
142 last_input = {buttons, FlipY(axes[0]), FlipY(axes[1])};
143}
144
145std::tuple<TasState, size_t, size_t> Tas::GetStatus() {
146 TasState state;
117 if (Settings::values.tas_record) { 147 if (Settings::values.tas_record) {
118 return "Recording TAS: " + std::to_string(recordCommands.size()); 148 return {TasState::RECORDING, record_commands.size(), record_commands.size()};
119 } 149 } else if (Settings::values.tas_enable) {
120 if (Settings::values.tas_enable) { 150 state = TasState::RUNNING;
121 return "Playing TAS: " + std::to_string(current_command) + "/" + 151 } else {
122 std::to_string(scriptLength); 152 state = TasState::STOPPED;
123 } 153 }
124 return "TAS not running: " + std::to_string(current_command) + "/" + 154
125 std::to_string(scriptLength); 155 return {state, current_command, script_length};
126} 156}
127 157
128std::string debugButtons(u32 buttons) { 158static std::string DebugButtons(u32 buttons) {
129 return "{ " + TasInput::Tas::buttonsToString(buttons) + " }"; 159 return "{ " + TasInput::Tas::ButtonsToString(buttons) + " }";
130} 160}
131 161
132std::string debugJoystick(float x, float y) { 162static std::string DebugJoystick(float x, float y) {
133 return "[ " + std::to_string(x) + "," + std::to_string(y) + " ]"; 163 return "[ " + std::to_string(x) + "," + std::to_string(y) + " ]";
134} 164}
135 165
136std::string debugInput(TasData data) { 166static std::string DebugInput(const TasData& data) {
137 return "{ " + debugButtons(data.buttons) + " , " + debugJoystick(data.axis[0], data.axis[1]) + 167 return "{ " + DebugButtons(data.buttons) + " , " + DebugJoystick(data.axis[0], data.axis[1]) +
138 " , " + debugJoystick(data.axis[2], data.axis[3]) + " }"; 168 " , " + DebugJoystick(data.axis[2], data.axis[3]) + " }";
139} 169}
140 170
141std::string debugInputs(std::array<TasData, PLAYER_NUMBER> arr) { 171static std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) {
142 std::string returns = "[ "; 172 std::string returns = "[ ";
143 for (size_t i = 0; i < arr.size(); i++) { 173 for (size_t i = 0; i < arr.size(); i++) {
144 returns += debugInput(arr[i]); 174 returns += DebugInput(arr[i]);
145 if (i != arr.size() - 1) 175 if (i != arr.size() - 1) {
146 returns += " , "; 176 returns += " , ";
177 }
147 } 178 }
148 return returns + "]"; 179 return returns + "]";
149} 180}
150 181
151void Tas::UpdateThread() { 182void Tas::UpdateThread() {
152 if (update_thread_running) { 183 if (update_thread_running) {
153 if (Settings::values.pauseTasOnLoad && Settings::values.cpuBoosted) { 184 if (Settings::values.pause_tas_on_load && Settings::values.is_cpu_boosted) {
154 for (int i = 0; i < PLAYER_NUMBER; i++) { 185 for (size_t i = 0; i < PLAYER_NUMBER; i++) {
155 tas_data[i].buttons = 0; 186 tas_data[i].buttons = 0;
156 tas_data[i].axis = {}; 187 tas_data[i].axis = {};
157 } 188 }
158 } 189 }
159 190
160 if (Settings::values.tas_record) { 191 if (Settings::values.tas_record) {
161 recordCommands.push_back(lastInput); 192 record_commands.push_back(last_input);
162 } 193 }
163 if (!Settings::values.tas_record && !recordCommands.empty()) { 194 if (!Settings::values.tas_record && !record_commands.empty()) {
164 WriteTasFile(); 195 WriteTasFile();
165 Settings::values.tas_reset = true; 196 Settings::values.tas_reset = true;
166 refresh_tas_fle = true; 197 refresh_tas_fle = true;
167 recordCommands.clear(); 198 record_commands.clear();
168 } 199 }
169 if (Settings::values.tas_reset) { 200 if (Settings::values.tas_reset) {
170 current_command = 0; 201 current_command = 0;
@@ -177,12 +208,12 @@ void Tas::UpdateThread() {
177 LOG_DEBUG(Input, "tas_reset done"); 208 LOG_DEBUG(Input, "tas_reset done");
178 } 209 }
179 if (Settings::values.tas_enable) { 210 if (Settings::values.tas_enable) {
180 if ((signed)current_command < scriptLength) { 211 if ((signed)current_command < script_length) {
181 LOG_INFO(Input, "Playing TAS {}/{}", current_command, scriptLength); 212 LOG_INFO(Input, "Playing TAS {}/{}", current_command, script_length);
182 size_t frame = current_command++; 213 size_t frame = current_command++;
183 for (int i = 0; i < PLAYER_NUMBER; i++) { 214 for (size_t i = 0; i < PLAYER_NUMBER; i++) {
184 if (frame < newCommands[i].size()) { 215 if (frame < commands[i].size()) {
185 TASCommand command = newCommands[i][frame]; 216 TASCommand command = commands[i][frame];
186 tas_data[i].buttons = command.buttons; 217 tas_data[i].buttons = command.buttons;
187 auto [l_axis_x, l_axis_y] = command.l_axis; 218 auto [l_axis_x, l_axis_y] = command.l_axis;
188 tas_data[i].axis[0] = l_axis_x; 219 tas_data[i].axis[0] = l_axis_x;
@@ -198,22 +229,22 @@ void Tas::UpdateThread() {
198 } else { 229 } else {
199 Settings::values.tas_enable = false; 230 Settings::values.tas_enable = false;
200 current_command = 0; 231 current_command = 0;
201 for (int i = 0; i < PLAYER_NUMBER; i++) { 232 for (size_t i = 0; i < PLAYER_NUMBER; i++) {
202 tas_data[i].buttons = 0; 233 tas_data[i].buttons = 0;
203 tas_data[i].axis = {}; 234 tas_data[i].axis = {};
204 } 235 }
205 } 236 }
206 } else { 237 } else {
207 for (int i = 0; i < PLAYER_NUMBER; i++) { 238 for (size_t i = 0; i < PLAYER_NUMBER; i++) {
208 tas_data[i].buttons = 0; 239 tas_data[i].buttons = 0;
209 tas_data[i].axis = {}; 240 tas_data[i].axis = {};
210 } 241 }
211 } 242 }
212 } 243 }
213 LOG_DEBUG(Input, "TAS inputs: {}", debugInputs(tas_data)); 244 LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data));
214} 245}
215 246
216TasAnalog Tas::ReadCommandAxis(const std::string line) const { 247TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
217 std::stringstream linestream(line); 248 std::stringstream linestream(line);
218 std::string segment; 249 std::string segment;
219 std::vector<std::string> seglist; 250 std::vector<std::string> seglist;
@@ -228,7 +259,7 @@ TasAnalog Tas::ReadCommandAxis(const std::string line) const {
228 return {x, y}; 259 return {x, y};
229} 260}
230 261
231u32 Tas::ReadCommandButtons(const std::string data) const { 262u32 Tas::ReadCommandButtons(const std::string& data) const {
232 std::stringstream button_text(data); 263 std::stringstream button_text(data);
233 std::string line; 264 std::string line;
234 u32 buttons = 0; 265 u32 buttons = 0;
@@ -262,8 +293,9 @@ std::string Tas::WriteCommandButtons(u32 data) const {
262 if ((data & 1) == 1) { 293 if ((data & 1) == 1) {
263 for (auto [text, tas_button] : text_to_tas_button) { 294 for (auto [text, tas_button] : text_to_tas_button) {
264 if (tas_button == static_cast<TasButton>(1 << index)) { 295 if (tas_button == static_cast<TasButton>(1 << index)) {
265 if (line.size() > 0) 296 if (line.size() > 0) {
266 line += ";"; 297 line += ";";
298 }
267 line += text; 299 line += text;
268 break; 300 break;
269 } 301 }
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 @@
12#include "core/frontend/input.h" 12#include "core/frontend/input.h"
13#include "input_common/main.h" 13#include "input_common/main.h"
14 14
15#define PLAYER_NUMBER 8
16
17namespace TasInput { 15namespace TasInput {
18 16
19using TasAnalog = std::tuple<float, float>; 17constexpr int PLAYER_NUMBER = 8;
18
19using TasAnalog = std::pair<float, float>;
20
21enum class TasState {
22 RUNNING,
23 RECORDING,
24 STOPPED,
25};
20 26
21enum class TasButton : u32 { 27enum class TasButton : u32 {
22 BUTTON_A = 0x000001, 28 BUTTON_A = 0x000001,
@@ -41,29 +47,6 @@ enum class TasButton : u32 {
41 BUTTON_CAPTURE = 0x080000, 47 BUTTON_CAPTURE = 0x080000,
42}; 48};
43 49
44static const std::array<std::pair<std::string, TasButton>, 20> text_to_tas_button = {
45 std::pair{"KEY_A", TasButton::BUTTON_A},
46 {"KEY_B", TasButton::BUTTON_B},
47 {"KEY_X", TasButton::BUTTON_X},
48 {"KEY_Y", TasButton::BUTTON_Y},
49 {"KEY_LSTICK", TasButton::STICK_L},
50 {"KEY_RSTICK", TasButton::STICK_R},
51 {"KEY_L", TasButton::TRIGGER_L},
52 {"KEY_R", TasButton::TRIGGER_R},
53 {"KEY_PLUS", TasButton::BUTTON_PLUS},
54 {"KEY_MINUS", TasButton::BUTTON_MINUS},
55 {"KEY_DLEFT", TasButton::BUTTON_LEFT},
56 {"KEY_DUP", TasButton::BUTTON_UP},
57 {"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
58 {"KEY_DDOWN", TasButton::BUTTON_DOWN},
59 {"KEY_SL", TasButton::BUTTON_SL},
60 {"KEY_SR", TasButton::BUTTON_SR},
61 {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
62 {"KEY_HOME", TasButton::BUTTON_HOME},
63 {"KEY_ZL", TasButton::TRIGGER_ZL},
64 {"KEY_ZR", TasButton::TRIGGER_ZR},
65};
66
67enum class TasAxes : u8 { 50enum class TasAxes : u8 {
68 StickX, 51 StickX,
69 StickY, 52 StickY,
@@ -82,7 +65,7 @@ public:
82 Tas(); 65 Tas();
83 ~Tas(); 66 ~Tas();
84 67
85 static std::string buttonsToString(u32 button) { 68 static std::string ButtonsToString(u32 button) {
86 std::string returns; 69 std::string returns;
87 if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_A)) != 0) 70 if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_A)) != 0)
88 returns += ", A"; 71 returns += ", A";
@@ -124,14 +107,14 @@ public:
124 returns += ", HOME"; 107 returns += ", HOME";
125 if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_CAPTURE)) != 0) 108 if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_CAPTURE)) != 0)
126 returns += ", CAPTURE"; 109 returns += ", CAPTURE";
127 return returns.length() != 0 ? returns.substr(2) : ""; 110 return returns.empty() ? "" : returns.substr(2);
128 } 111 }
129 112
130 void RefreshTasFile(); 113 void RefreshTasFile();
131 void LoadTasFiles(); 114 void LoadTasFiles();
132 void RecordInput(u32 buttons, std::array<std::pair<float, float>, 2> axes); 115 void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes);
133 void UpdateThread(); 116 void UpdateThread();
134 std::string GetStatusDescription(); 117 std::tuple<TasState, size_t, size_t> GetStatus();
135 118
136 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; 119 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
137 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; 120 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
@@ -143,21 +126,20 @@ private:
143 TasAnalog l_axis{}; 126 TasAnalog l_axis{};
144 TasAnalog r_axis{}; 127 TasAnalog r_axis{};
145 }; 128 };
146 void LoadTasFile(int playerIndex); 129 void LoadTasFile(size_t player_index);
147 void WriteTasFile(); 130 void WriteTasFile();
148 TasAnalog ReadCommandAxis(const std::string line) const; 131 TasAnalog ReadCommandAxis(const std::string& line) const;
149 u32 ReadCommandButtons(const std::string line) const; 132 u32 ReadCommandButtons(const std::string& line) const;
150 std::string WriteCommandButtons(u32 data) const; 133 std::string WriteCommandButtons(u32 data) const;
151 std::string WriteCommandAxis(TasAnalog data) const; 134 std::string WriteCommandAxis(TasAnalog data) const;
152 std::pair<float, float> flipY(std::pair<float, float> old) const;
153 135
154 size_t scriptLength{0}; 136 size_t script_length{0};
155 std::array<TasData, PLAYER_NUMBER> tas_data; 137 std::array<TasData, PLAYER_NUMBER> tas_data;
156 bool update_thread_running{true}; 138 bool update_thread_running{true};
157 bool refresh_tas_fle{false}; 139 bool refresh_tas_fle{false};
158 std::array<std::vector<TASCommand>, PLAYER_NUMBER> newCommands{}; 140 std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{};
159 std::vector<TASCommand> recordCommands{}; 141 std::vector<TASCommand> record_commands{};
160 std::size_t current_command{0}; 142 std::size_t current_command{0};
161 TASCommand lastInput{}; // only used for recording 143 TASCommand last_input{}; // only used for recording
162}; 144};
163} // namespace TasInput 145} // namespace TasInput
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
index cd9ba0a90..4636d476e 100644
--- a/src/yuzu/configuration/configure_filesystem.cpp
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -78,7 +78,7 @@ void ConfigureFilesystem::applyConfiguration() {
78 78
79 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked(); 79 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
80 Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked(); 80 Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked();
81 Settings::values.pauseTasOnLoad = ui->tas_pause_on_load->isChecked(); 81 Settings::values.pause_tas_on_load = ui->tas_pause_on_load->isChecked();
82 Settings::values.dump_exefs = ui->dump_exefs->isChecked(); 82 Settings::values.dump_exefs = ui->dump_exefs->isChecked();
83 Settings::values.dump_nso = ui->dump_nso->isChecked(); 83 Settings::values.dump_nso = ui->dump_nso->isChecked();
84 84
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 10057b9ca..0ee0fd8cd 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -826,11 +826,11 @@ void GMainWindow::InitializeWidgets() {
826 }); 826 });
827 statusBar()->insertPermanentWidget(0, renderer_status_button); 827 statusBar()->insertPermanentWidget(0, renderer_status_button);
828 828
829 TASlabel = new QLabel(); 829 tas_label = new QLabel();
830 TASlabel->setObjectName(QStringLiteral("TASlabel")); 830 tas_label->setObjectName(QStringLiteral("TASlabel"));
831 TASlabel->setText(tr("TAS not running")); 831 tas_label->setText(tr("TAS not running"));
832 TASlabel->setFocusPolicy(Qt::NoFocus); 832 tas_label->setFocusPolicy(Qt::NoFocus);
833 statusBar()->insertPermanentWidget(0, TASlabel); 833 statusBar()->insertPermanentWidget(0, tas_label);
834 834
835 statusBar()->setVisible(true); 835 statusBar()->setVisible(true);
836 setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); 836 setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}"));
@@ -2896,13 +2896,28 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie
2896 } 2896 }
2897} 2897}
2898 2898
2899static std::string GetTasStateDescription(TasInput::TasState state) {
2900 switch (state) {
2901 case TasInput::TasState::RUNNING:
2902 return "Running";
2903 case TasInput::TasState::RECORDING:
2904 return "Recording";
2905 case TasInput::TasState::STOPPED:
2906 return "Stopped";
2907 default:
2908 return "INVALID STATE";
2909 }
2910}
2911
2899void GMainWindow::UpdateStatusBar() { 2912void GMainWindow::UpdateStatusBar() {
2900 if (emu_thread == nullptr) { 2913 if (emu_thread == nullptr) {
2901 status_bar_update_timer.stop(); 2914 status_bar_update_timer.stop();
2902 return; 2915 return;
2903 } 2916 }
2904 2917
2905 TASlabel->setText(tr(input_subsystem->GetTas()->GetStatusDescription().c_str())); 2918 auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus();
2919 tas_label->setText(tr("%1 TAS %2/%3").arg(tr(GetTasStateDescription(tas_status).c_str())).arg(current_tas_frame).arg(total_tas_frames));
2920
2906 auto& system = Core::System::GetInstance(); 2921 auto& system = Core::System::GetInstance();
2907 auto results = system.GetAndResetPerfStats(); 2922 auto results = system.GetAndResetPerfStats();
2908 auto& shader_notify = system.GPU().ShaderNotify(); 2923 auto& shader_notify = system.GPU().ShaderNotify();