summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/fs/fs_util.cpp14
-rw-r--r--src/common/fs/fs_util.h33
-rw-r--r--src/common/fs/path_util.cpp6
-rw-r--r--src/common/fs/path_util.h9
-rw-r--r--src/input_common/sdl/sdl_impl.cpp281
-rw-r--r--src/input_common/sdl/sdl_impl.h37
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp67
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h7
-rw-r--r--src/yuzu/configuration/config.cpp4
-rw-r--r--src/yuzu/configuration/config.h4
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp9
-rw-r--r--src/yuzu/configuration/configure_per_game.h3
-rw-r--r--src/yuzu/game_list.cpp8
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/main.cpp21
-rw-r--r--src/yuzu/main.h5
16 files changed, 383 insertions, 128 deletions
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp
index 0ddfc3131..357cf5855 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6
5#include "common/fs/fs_util.h" 7#include "common/fs/fs_util.h"
6 8
7namespace Common::FS { 9namespace Common::FS {
@@ -10,4 +12,16 @@ std::u8string ToU8String(std::string_view utf8_string) {
10 return std::u8string{utf8_string.begin(), utf8_string.end()}; 12 return std::u8string{utf8_string.begin(), utf8_string.end()};
11} 13}
12 14
15std::u8string BufferToU8String(std::span<const u8> buffer) {
16 return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
17}
18
19std::string ToUTF8String(std::u8string_view u8_string) {
20 return std::string{u8_string.begin(), u8_string.end()};
21}
22
23std::string PathToUTF8String(const std::filesystem::path& path) {
24 return ToUTF8String(path.u8string());
25}
26
13} // namespace Common::FS 27} // namespace Common::FS
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h
index 951df53b6..ec9950ee7 100644
--- a/src/common/fs/fs_util.h
+++ b/src/common/fs/fs_util.h
@@ -5,9 +5,13 @@
5#pragma once 5#pragma once
6 6
7#include <concepts> 7#include <concepts>
8#include <filesystem>
9#include <span>
8#include <string> 10#include <string>
9#include <string_view> 11#include <string_view>
10 12
13#include "common/common_types.h"
14
11namespace Common::FS { 15namespace Common::FS {
12 16
13template <typename T> 17template <typename T>
@@ -22,4 +26,33 @@ concept IsChar = std::same_as<T, char>;
22 */ 26 */
23[[nodiscard]] std::u8string ToU8String(std::string_view utf8_string); 27[[nodiscard]] std::u8string ToU8String(std::string_view utf8_string);
24 28
29/**
30 * Converts a buffer of bytes to a UTF8-encoded std::u8string.
31 * This converts from the start of the buffer until the first encountered null-terminator.
32 * If no null-terminator is found, this converts the entire buffer instead.
33 *
34 * @param buffer Buffer of bytes
35 *
36 * @returns UTF-8 encoded std::u8string.
37 */
38[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
39
40/**
41 * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
42 *
43 * @param u8_string UTF-8 encoded u8string
44 *
45 * @returns UTF-8 encoded std::string.
46 */
47[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string);
48
49/**
50 * Converts a filesystem path to a UTF-8 encoded std::string.
51 *
52 * @param path Filesystem path
53 *
54 * @returns UTF-8 encoded std::string.
55 */
56[[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path);
57
25} // namespace Common::FS 58} // namespace Common::FS
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 8b732a21c..6cdd14f13 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -129,12 +129,6 @@ private:
129 std::unordered_map<YuzuPath, fs::path> yuzu_paths; 129 std::unordered_map<YuzuPath, fs::path> yuzu_paths;
130}; 130};
131 131
132std::string PathToUTF8String(const fs::path& path) {
133 const auto utf8_string = path.u8string();
134
135 return std::string{utf8_string.begin(), utf8_string.end()};
136}
137
138bool ValidatePath(const fs::path& path) { 132bool ValidatePath(const fs::path& path) {
139 if (path.empty()) { 133 if (path.empty()) {
140 LOG_ERROR(Common_Filesystem, "Input path is empty, path={}", PathToUTF8String(path)); 134 LOG_ERROR(Common_Filesystem, "Input path is empty, path={}", PathToUTF8String(path));
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h
index a9fadbceb..14e8c35d7 100644
--- a/src/common/fs/path_util.h
+++ b/src/common/fs/path_util.h
@@ -26,15 +26,6 @@ enum class YuzuPath {
26}; 26};
27 27
28/** 28/**
29 * Converts a filesystem path to a UTF-8 encoded std::string.
30 *
31 * @param path Filesystem path
32 *
33 * @returns UTF-8 encoded std::string.
34 */
35[[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path);
36
37/**
38 * Validates a given path. 29 * Validates a given path.
39 * 30 *
40 * A given path is valid if it meets these conditions: 31 * A given path is valid if it meets these conditions:
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index b9b584b2a..68672a92b 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -18,16 +18,6 @@
18#include <utility> 18#include <utility>
19#include <vector> 19#include <vector>
20 20
21// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
22#ifdef __clang__
23#pragma clang diagnostic push
24#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
25#endif
26#include <SDL.h>
27#ifdef __clang__
28#pragma clang diagnostic pop
29#endif
30
31#include "common/logging/log.h" 21#include "common/logging/log.h"
32#include "common/math_util.h" 22#include "common/math_util.h"
33#include "common/param_package.h" 23#include "common/param_package.h"
@@ -214,6 +204,40 @@ public:
214 sdl_controller.reset(controller); 204 sdl_controller.reset(controller);
215 } 205 }
216 206
207 bool IsJoyconLeft() const {
208 return std::strstr(GetControllerName().c_str(), "Joy-Con Left") != nullptr;
209 }
210
211 bool IsJoyconRight() const {
212 return std::strstr(GetControllerName().c_str(), "Joy-Con Right") != nullptr;
213 }
214
215 std::string GetControllerName() const {
216 if (sdl_controller) {
217 switch (SDL_GameControllerGetType(sdl_controller.get())) {
218 case SDL_CONTROLLER_TYPE_XBOX360:
219 return "XBox 360 Controller";
220 case SDL_CONTROLLER_TYPE_XBOXONE:
221 return "XBox One Controller";
222 default:
223 break;
224 }
225 const auto name = SDL_GameControllerName(sdl_controller.get());
226 if (name) {
227 return name;
228 }
229 }
230
231 if (sdl_joystick) {
232 const auto name = SDL_JoystickName(sdl_joystick.get());
233 if (name) {
234 return name;
235 }
236 }
237
238 return "Unknown";
239 }
240
217private: 241private:
218 struct State { 242 struct State {
219 std::unordered_map<int, bool> buttons; 243 std::unordered_map<int, bool> buttons;
@@ -858,23 +882,42 @@ SDLState::~SDLState() {
858std::vector<Common::ParamPackage> SDLState::GetInputDevices() { 882std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
859 std::scoped_lock lock(joystick_map_mutex); 883 std::scoped_lock lock(joystick_map_mutex);
860 std::vector<Common::ParamPackage> devices; 884 std::vector<Common::ParamPackage> devices;
885 std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs;
886 for (const auto& [key, value] : joystick_map) {
887 for (const auto& joystick : value) {
888 if (!joystick->GetSDLJoystick()) {
889 continue;
890 }
891 std::string name =
892 fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort());
893 devices.emplace_back(Common::ParamPackage{
894 {"class", "sdl"},
895 {"display", std::move(name)},
896 {"guid", joystick->GetGUID()},
897 {"port", std::to_string(joystick->GetPort())},
898 });
899 if (joystick->IsJoyconLeft()) {
900 joycon_pairs.insert_or_assign(joystick->GetPort(), joystick);
901 }
902 }
903 }
904
905 // Add dual controllers
861 for (const auto& [key, value] : joystick_map) { 906 for (const auto& [key, value] : joystick_map) {
862 for (const auto& joystick : value) { 907 for (const auto& joystick : value) {
863 if (auto* const controller = joystick->GetSDLGameController()) { 908 if (joystick->IsJoyconRight()) {
909 if (!joycon_pairs.contains(joystick->GetPort())) {
910 continue;
911 }
912 const auto joystick2 = joycon_pairs.at(joystick->GetPort());
913
864 std::string name = 914 std::string name =
865 fmt::format("{} {}", GetControllerName(controller), joystick->GetPort()); 915 fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort());
866 devices.emplace_back(Common::ParamPackage{
867 {"class", "sdl"},
868 {"display", std::move(name)},
869 {"guid", joystick->GetGUID()},
870 {"port", std::to_string(joystick->GetPort())},
871 });
872 } else if (auto* const joy = joystick->GetSDLJoystick()) {
873 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
874 devices.emplace_back(Common::ParamPackage{ 916 devices.emplace_back(Common::ParamPackage{
875 {"class", "sdl"}, 917 {"class", "sdl"},
876 {"display", std::move(name)}, 918 {"display", std::move(name)},
877 {"guid", joystick->GetGUID()}, 919 {"guid", joystick->GetGUID()},
920 {"guid2", joystick2->GetGUID()},
878 {"port", std::to_string(joystick->GetPort())}, 921 {"port", std::to_string(joystick->GetPort())},
879 }); 922 });
880 } 923 }
@@ -883,17 +926,6 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
883 return devices; 926 return devices;
884} 927}
885 928
886std::string SDLState::GetControllerName(SDL_GameController* controller) const {
887 switch (SDL_GameControllerGetType(controller)) {
888 case SDL_CONTROLLER_TYPE_XBOX360:
889 return "XBox 360 Controller";
890 case SDL_CONTROLLER_TYPE_XBOXONE:
891 return "XBox One Controller";
892 default:
893 return SDL_GameControllerName(controller);
894 }
895}
896
897namespace { 929namespace {
898Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, 930Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
899 float value = 0.1f) { 931 float value = 0.1f) {
@@ -1073,24 +1105,48 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa
1073 return {}; 1105 return {};
1074 } 1106 }
1075 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 1107 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1108
1076 auto* controller = joystick->GetSDLGameController(); 1109 auto* controller = joystick->GetSDLGameController();
1077 if (controller == nullptr) { 1110 if (controller == nullptr) {
1078 return {}; 1111 return {};
1079 } 1112 }
1080 1113
1081 const bool invert =
1082 SDL_GameControllerGetType(controller) != SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO;
1083
1084 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. 1114 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
1085 // We will add those afterwards 1115 // We will add those afterwards
1086 // This list also excludes Screenshot since theres not really a mapping for that 1116 // This list also excludes Screenshot since theres not really a mapping for that
1087 using ButtonBindings = 1117 ButtonBindings switch_to_sdl_button;
1088 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; 1118
1089 const ButtonBindings switch_to_sdl_button{{ 1119 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) {
1090 {Settings::NativeButton::A, invert ? SDL_CONTROLLER_BUTTON_B : SDL_CONTROLLER_BUTTON_A}, 1120 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
1091 {Settings::NativeButton::B, invert ? SDL_CONTROLLER_BUTTON_A : SDL_CONTROLLER_BUTTON_B}, 1121 } else {
1092 {Settings::NativeButton::X, invert ? SDL_CONTROLLER_BUTTON_Y : SDL_CONTROLLER_BUTTON_X}, 1122 switch_to_sdl_button = GetDefaultButtonBinding();
1093 {Settings::NativeButton::Y, invert ? SDL_CONTROLLER_BUTTON_X : SDL_CONTROLLER_BUTTON_Y}, 1123 }
1124
1125 // Add the missing bindings for ZL/ZR
1126 static constexpr ZButtonBindings switch_to_sdl_axis{{
1127 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
1128 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
1129 }};
1130
1131 // Parameters contain two joysticks return dual
1132 if (params.Has("guid2")) {
1133 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1134
1135 if (joystick2->GetSDLGameController() != nullptr) {
1136 return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button,
1137 switch_to_sdl_axis);
1138 }
1139 }
1140
1141 return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
1142}
1143
1144ButtonBindings SDLState::GetDefaultButtonBinding() const {
1145 return {
1146 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
1147 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
1148 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
1149 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
1094 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, 1150 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1095 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, 1151 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1096 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, 1152 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
@@ -1104,18 +1160,51 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa
1104 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, 1160 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1105 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, 1161 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1106 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, 1162 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1107 }}; 1163 };
1164}
1108 1165
1109 // Add the missing bindings for ZL/ZR 1166ButtonBindings SDLState::GetNintendoButtonBinding(
1110 using ZBindings = 1167 const std::shared_ptr<SDLJoystick>& joystick) const {
1111 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; 1168 // Default SL/SR mapping for pro controllers
1112 static constexpr ZBindings switch_to_sdl_axis{{ 1169 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
1113 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, 1170 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
1114 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, 1171
1115 }}; 1172 if (joystick->IsJoyconLeft()) {
1173 sl_button = SDL_CONTROLLER_BUTTON_PADDLE2;
1174 sr_button = SDL_CONTROLLER_BUTTON_PADDLE4;
1175 }
1176 if (joystick->IsJoyconRight()) {
1177 sl_button = SDL_CONTROLLER_BUTTON_PADDLE3;
1178 sr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
1179 }
1116 1180
1181 return {
1182 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A},
1183 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B},
1184 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X},
1185 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y},
1186 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1187 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1188 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1189 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1190 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
1191 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
1192 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
1193 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
1194 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
1195 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
1196 {Settings::NativeButton::SL, sl_button},
1197 {Settings::NativeButton::SR, sr_button},
1198 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1199 };
1200}
1201
1202ButtonMapping SDLState::GetSingleControllerMapping(
1203 const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button,
1204 const ZButtonBindings& switch_to_sdl_axis) const {
1117 ButtonMapping mapping; 1205 ButtonMapping mapping;
1118 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); 1206 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1207 auto* controller = joystick->GetSDLGameController();
1119 1208
1120 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { 1209 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1121 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); 1210 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
@@ -1133,11 +1222,68 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa
1133 return mapping; 1222 return mapping;
1134} 1223}
1135 1224
1225ButtonMapping SDLState::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
1226 const std::shared_ptr<SDLJoystick>& joystick2,
1227 const ButtonBindings& switch_to_sdl_button,
1228 const ZButtonBindings& switch_to_sdl_axis) const {
1229 ButtonMapping mapping;
1230 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1231 auto* controller = joystick->GetSDLGameController();
1232 auto* controller2 = joystick2->GetSDLGameController();
1233
1234 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1235 if (IsButtonOnLeftSide(switch_button)) {
1236 const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button);
1237 mapping.insert_or_assign(
1238 switch_button,
1239 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1240 continue;
1241 }
1242 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
1243 mapping.insert_or_assign(
1244 switch_button,
1245 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1246 }
1247 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
1248 if (IsButtonOnLeftSide(switch_button)) {
1249 const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis);
1250 mapping.insert_or_assign(
1251 switch_button,
1252 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1253 continue;
1254 }
1255 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
1256 mapping.insert_or_assign(
1257 switch_button,
1258 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1259 }
1260
1261 return mapping;
1262}
1263
1264bool SDLState::IsButtonOnLeftSide(Settings::NativeButton::Values button) const {
1265 switch (button) {
1266 case Settings::NativeButton::DDown:
1267 case Settings::NativeButton::DLeft:
1268 case Settings::NativeButton::DRight:
1269 case Settings::NativeButton::DUp:
1270 case Settings::NativeButton::L:
1271 case Settings::NativeButton::LStick:
1272 case Settings::NativeButton::Minus:
1273 case Settings::NativeButton::Screenshot:
1274 case Settings::NativeButton::ZL:
1275 return true;
1276 default:
1277 return false;
1278 }
1279}
1280
1136AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) { 1281AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
1137 if (!params.Has("guid") || !params.Has("port")) { 1282 if (!params.Has("guid") || !params.Has("port")) {
1138 return {}; 1283 return {};
1139 } 1284 }
1140 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 1285 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1286 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1141 auto* controller = joystick->GetSDLGameController(); 1287 auto* controller = joystick->GetSDLGameController();
1142 if (controller == nullptr) { 1288 if (controller == nullptr) {
1143 return {}; 1289 return {};
@@ -1148,10 +1294,17 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
1148 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); 1294 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
1149 const auto& binding_left_y = 1295 const auto& binding_left_y =
1150 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 1296 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
1151 mapping.insert_or_assign(Settings::NativeAnalog::LStick, 1297 if (params.Has("guid2")) {
1152 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1298 mapping.insert_or_assign(
1153 binding_left_x.value.axis, 1299 Settings::NativeAnalog::LStick,
1154 binding_left_y.value.axis)); 1300 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
1301 binding_left_x.value.axis, binding_left_y.value.axis));
1302 } else {
1303 mapping.insert_or_assign(
1304 Settings::NativeAnalog::LStick,
1305 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1306 binding_left_x.value.axis, binding_left_y.value.axis));
1307 }
1155 const auto& binding_right_x = 1308 const auto& binding_right_x =
1156 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); 1309 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
1157 const auto& binding_right_y = 1310 const auto& binding_right_y =
@@ -1168,20 +1321,32 @@ MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& pa
1168 return {}; 1321 return {};
1169 } 1322 }
1170 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 1323 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1324 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1171 auto* controller = joystick->GetSDLGameController(); 1325 auto* controller = joystick->GetSDLGameController();
1172 if (controller == nullptr) { 1326 if (controller == nullptr) {
1173 return {}; 1327 return {};
1174 } 1328 }
1175 1329
1330 MotionMapping mapping = {};
1176 joystick->EnableMotion(); 1331 joystick->EnableMotion();
1177 1332
1178 if (!joystick->HasGyro() && !joystick->HasAccel()) { 1333 if (joystick->HasGyro() || joystick->HasAccel()) {
1179 return {}; 1334 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
1335 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1336 }
1337 if (params.Has("guid2")) {
1338 joystick2->EnableMotion();
1339 if (joystick2->HasGyro() || joystick2->HasAccel()) {
1340 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1341 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
1342 }
1343 } else {
1344 if (joystick->HasGyro() || joystick->HasAccel()) {
1345 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1346 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1347 }
1180 } 1348 }
1181 1349
1182 MotionMapping mapping = {};
1183 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1184 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1185 return mapping; 1350 return mapping;
1186} 1351}
1187namespace Polling { 1352namespace Polling {
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index 121e01913..b77afcbd8 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -9,6 +9,17 @@
9#include <mutex> 9#include <mutex>
10#include <thread> 10#include <thread>
11#include <unordered_map> 11#include <unordered_map>
12
13// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
14#ifdef __GNUC__
15#pragma GCC diagnostic push
16#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
17#endif
18#include <SDL.h>
19#ifdef __GNUC__
20#pragma GCC diagnostic pop
21#endif
22
12#include "common/common_types.h" 23#include "common/common_types.h"
13#include "common/threadsafe_queue.h" 24#include "common/threadsafe_queue.h"
14#include "input_common/sdl/sdl.h" 25#include "input_common/sdl/sdl.h"
@@ -18,6 +29,11 @@ using SDL_GameController = struct _SDL_GameController;
18using SDL_Joystick = struct _SDL_Joystick; 29using SDL_Joystick = struct _SDL_Joystick;
19using SDL_JoystickID = s32; 30using SDL_JoystickID = s32;
20 31
32using ButtonBindings =
33 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
34using ZButtonBindings =
35 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
36
21namespace InputCommon::SDL { 37namespace InputCommon::SDL {
22 38
23class SDLAnalogFactory; 39class SDLAnalogFactory;
@@ -66,8 +82,25 @@ private:
66 /// Needs to be called before SDL_QuitSubSystem. 82 /// Needs to be called before SDL_QuitSubSystem.
67 void CloseJoysticks(); 83 void CloseJoysticks();
68 84
69 /// Returns a custom name for specific controllers because the default name is not correct 85 /// Returns the default button bindings list for generic controllers
70 std::string GetControllerName(SDL_GameController* controller) const; 86 ButtonBindings GetDefaultButtonBinding() const;
87
88 /// Returns the default button bindings list for nintendo controllers
89 ButtonBindings GetNintendoButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const;
90
91 /// Returns the button mappings from a single controller
92 ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
93 const ButtonBindings& switch_to_sdl_button,
94 const ZButtonBindings& switch_to_sdl_axis) const;
95
96 /// Returns the button mappings from two different controllers
97 ButtonMapping GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
98 const std::shared_ptr<SDLJoystick>& joystick2,
99 const ButtonBindings& switch_to_sdl_button,
100 const ZButtonBindings& switch_to_sdl_axis) const;
101
102 /// Returns true if the button is on the left joycon
103 bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
71 104
72 // Set to true if SDL supports game controller subsystem 105 // Set to true if SDL supports game controller subsystem
73 bool has_gamecontroller = false; 106 bool has_gamecontroller = false;
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index fa37aa79a..5edd06ebc 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -53,6 +53,18 @@ struct Range {
53 UNREACHABLE_MSG("Invalid memory usage={}", usage); 53 UNREACHABLE_MSG("Invalid memory usage={}", usage);
54 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; 54 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
55} 55}
56
57constexpr VkExportMemoryAllocateInfo EXPORT_ALLOCATE_INFO{
58 .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
59 .pNext = nullptr,
60#ifdef _WIN32
61 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT,
62#elif __unix__
63 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
64#else
65 .handleTypes = 0,
66#endif
67};
56} // Anonymous namespace 68} // Anonymous namespace
57 69
58class MemoryAllocation { 70class MemoryAllocation {
@@ -131,7 +143,7 @@ public:
131 143
132 /// Returns whether this allocation is compatible with the arguments. 144 /// Returns whether this allocation is compatible with the arguments.
133 [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { 145 [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const {
134 return (flags & property_flags) && (type_mask & shifted_memory_type) != 0; 146 return (flags & property_flags) == property_flags && (type_mask & shifted_memory_type) != 0;
135 } 147 }
136 148
137private: 149private:
@@ -217,14 +229,18 @@ MemoryAllocator::~MemoryAllocator() = default;
217 229
218MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { 230MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
219 // Find the fastest memory flags we can afford with the current requirements 231 // Find the fastest memory flags we can afford with the current requirements
220 const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage); 232 const u32 type_mask = requirements.memoryTypeBits;
233 const VkMemoryPropertyFlags usage_flags = MemoryUsagePropertyFlags(usage);
234 const VkMemoryPropertyFlags flags = MemoryPropertyFlags(type_mask, usage_flags);
221 if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) { 235 if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) {
222 return std::move(*commit); 236 return std::move(*commit);
223 } 237 }
224 // Commit has failed, allocate more memory. 238 // Commit has failed, allocate more memory.
225 // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. 239 const u64 chunk_size = AllocationChunkSize(requirements.size);
226 AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size)); 240 if (!TryAllocMemory(flags, type_mask, chunk_size)) {
227 241 // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory.
242 throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY);
243 }
228 // Commit again, this time it won't fail since there's a fresh allocation above. 244 // Commit again, this time it won't fail since there's a fresh allocation above.
229 // If it does, there's a bug. 245 // If it does, there's a bug.
230 return TryCommit(requirements, flags).value(); 246 return TryCommit(requirements, flags).value();
@@ -242,26 +258,25 @@ MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage)
242 return commit; 258 return commit;
243} 259}
244 260
245void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { 261bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) {
246 const u32 type = FindType(flags, type_mask).value(); 262 const u32 type = FindType(flags, type_mask).value();
247 const VkExportMemoryAllocateInfo export_allocate_info{ 263 vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
248 .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
249 .pNext = nullptr,
250#ifdef _WIN32
251 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT,
252#elif __unix__
253 .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
254#else
255 .handleTypes = 0,
256#endif
257 };
258 vk::DeviceMemory memory = device.GetLogical().AllocateMemory({
259 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 264 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
260 .pNext = export_allocations ? &export_allocate_info : nullptr, 265 .pNext = export_allocations ? &EXPORT_ALLOCATE_INFO : nullptr,
261 .allocationSize = size, 266 .allocationSize = size,
262 .memoryTypeIndex = type, 267 .memoryTypeIndex = type,
263 }); 268 });
269 if (!memory) {
270 if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) {
271 // Try to allocate non device local memory
272 return TryAllocMemory(flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, type_mask, size);
273 } else {
274 // RIP
275 return false;
276 }
277 }
264 allocations.push_back(std::make_unique<MemoryAllocation>(std::move(memory), flags, size, type)); 278 allocations.push_back(std::make_unique<MemoryAllocation>(std::move(memory), flags, size, type));
279 return true;
265} 280}
266 281
267std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, 282std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements,
@@ -274,24 +289,24 @@ std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirement
274 return commit; 289 return commit;
275 } 290 }
276 } 291 }
292 if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) {
293 // Look for non device local commits on failure
294 return TryCommit(requirements, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
295 }
277 return std::nullopt; 296 return std::nullopt;
278} 297}
279 298
280VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const {
281 return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage));
282}
283
284VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, 299VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
285 VkMemoryPropertyFlags flags) const { 300 VkMemoryPropertyFlags flags) const {
286 if (FindType(flags, type_mask)) { 301 if (FindType(flags, type_mask)) {
287 // Found a memory type with those requirements 302 // Found a memory type with those requirements
288 return flags; 303 return flags;
289 } 304 }
290 if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { 305 if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0) {
291 // Remove host cached bit in case it's not supported 306 // Remove host cached bit in case it's not supported
292 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT); 307 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
293 } 308 }
294 if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { 309 if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) {
295 // Remove device local, if it's not supported by the requested resource 310 // Remove device local, if it's not supported by the requested resource
296 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); 311 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
297 } 312 }
@@ -302,7 +317,7 @@ VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
302std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { 317std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const {
303 for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { 318 for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) {
304 const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags; 319 const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags;
305 if ((type_mask & (1U << type_index)) && (type_flags & flags)) { 320 if ((type_mask & (1U << type_index)) != 0 && (type_flags & flags) == flags) {
306 // The type matches in type and in the wanted properties. 321 // The type matches in type and in the wanted properties.
307 return type_index; 322 return type_index;
308 } 323 }
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h
index d1ce29450..db12d02f4 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.h
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h
@@ -101,16 +101,13 @@ public:
101 MemoryCommit Commit(const vk::Image& image, MemoryUsage usage); 101 MemoryCommit Commit(const vk::Image& image, MemoryUsage usage);
102 102
103private: 103private:
104 /// Allocates a chunk of memory. 104 /// Tries to allocate a chunk of memory.
105 void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); 105 bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size);
106 106
107 /// Tries to allocate a memory commit. 107 /// Tries to allocate a memory commit.
108 std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements, 108 std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements,
109 VkMemoryPropertyFlags flags); 109 VkMemoryPropertyFlags flags);
110 110
111 /// Returns the fastest compatible memory property flags from a wanted usage.
112 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const;
113
114 /// Returns the fastest compatible memory property flags from the wanted flags. 111 /// Returns the fastest compatible memory property flags from the wanted flags.
115 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const; 112 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const;
116 113
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index eb58bfa5b..552454acf 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -16,7 +16,7 @@
16 16
17namespace FS = Common::FS; 17namespace FS = Common::FS;
18 18
19Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) { 19Config::Config(std::string_view config_name, ConfigType config_type) : type(config_type) {
20 global = config_type == ConfigType::GlobalConfig; 20 global = config_type == ConfigType::GlobalConfig;
21 21
22 Initialize(config_name); 22 Initialize(config_name);
@@ -242,7 +242,7 @@ const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{
242}}; 242}};
243// clang-format on 243// clang-format on
244 244
245void Config::Initialize(const std::string& config_name) { 245void Config::Initialize(std::string_view config_name) {
246 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); 246 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
247 const auto config_file = fmt::format("{}.ini", config_name); 247 const auto config_file = fmt::format("{}.ini", config_name);
248 248
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index ce3355588..114a2eaa7 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -22,7 +22,7 @@ public:
22 InputProfile, 22 InputProfile,
23 }; 23 };
24 24
25 explicit Config(const std::string& config_name = "qt-config", 25 explicit Config(std::string_view config_name = "qt-config",
26 ConfigType config_type = ConfigType::GlobalConfig); 26 ConfigType config_type = ConfigType::GlobalConfig);
27 ~Config(); 27 ~Config();
28 28
@@ -45,7 +45,7 @@ public:
45 static const std::array<UISettings::Shortcut, 17> default_hotkeys; 45 static const std::array<UISettings::Shortcut, 17> default_hotkeys;
46 46
47private: 47private:
48 void Initialize(const std::string& config_name); 48 void Initialize(std::string_view config_name);
49 49
50 void ReadValues(); 50 void ReadValues();
51 void ReadPlayerValue(std::size_t player_index); 51 void ReadPlayerValue(std::size_t player_index);
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index d89f1ad4b..7dfcf150c 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <string>
7#include <utility> 8#include <utility>
8 9
9#include <QAbstractButton> 10#include <QAbstractButton>
@@ -17,6 +18,7 @@
17#include <QTimer> 18#include <QTimer>
18#include <QTreeView> 19#include <QTreeView>
19 20
21#include "common/fs/path_util.h"
20#include "core/core.h" 22#include "core/core.h"
21#include "core/file_sys/control_metadata.h" 23#include "core/file_sys/control_metadata.h"
22#include "core/file_sys/patch_manager.h" 24#include "core/file_sys/patch_manager.h"
@@ -29,10 +31,11 @@
29#include "yuzu/uisettings.h" 31#include "yuzu/uisettings.h"
30#include "yuzu/util/util.h" 32#include "yuzu/util/util.h"
31 33
32ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) 34ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id, std::string_view file_name)
33 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { 35 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
34 game_config = std::make_unique<Config>(fmt::format("{:016X}", title_id), 36 const auto config_file_name =
35 Config::ConfigType::PerGameConfig); 37 title_id == 0 ? Common::FS::GetFilename(file_name) : fmt::format("{:016X}", title_id);
38 game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
36 39
37 Settings::SetConfiguringGlobal(false); 40 Settings::SetConfiguringGlobal(false);
38 41
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index f6e6ab7c4..dc6b68763 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string>
8#include <vector> 9#include <vector>
9 10
10#include <QDialog> 11#include <QDialog>
@@ -27,7 +28,7 @@ class ConfigurePerGame : public QDialog {
27 Q_OBJECT 28 Q_OBJECT
28 29
29public: 30public:
30 explicit ConfigurePerGame(QWidget* parent, u64 title_id); 31 explicit ConfigurePerGame(QWidget* parent, u64 title_id, std::string_view file_name);
31 ~ConfigurePerGame() override; 32 ~ConfigurePerGame() override;
32 33
33 /// Save all button configurations to settings file 34 /// Save all button configurations to settings file
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 63cf82f7d..aa3bd5e34 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -561,11 +561,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
561 connect(remove_dlc, &QAction::triggered, [this, program_id]() { 561 connect(remove_dlc, &QAction::triggered, [this, program_id]() {
562 emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent); 562 emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent);
563 }); 563 });
564 connect(remove_shader_cache, &QAction::triggered, [this, program_id]() { 564 connect(remove_shader_cache, &QAction::triggered, [this, program_id, path]() {
565 emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache); 565 emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache, path);
566 }); 566 });
567 connect(remove_custom_config, &QAction::triggered, [this, program_id]() { 567 connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() {
568 emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration); 568 emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path);
569 }); 569 });
570 connect(dump_romfs, &QAction::triggered, 570 connect(dump_romfs, &QAction::triggered,
571 [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); 571 [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); });
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 9c0a1a482..2867f6653 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -88,7 +88,8 @@ signals:
88 const std::string& game_path); 88 const std::string& game_path);
89 void OpenTransferableShaderCacheRequested(u64 program_id); 89 void OpenTransferableShaderCacheRequested(u64 program_id);
90 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); 90 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
91 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target); 91 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target,
92 std::string_view game_path);
92 void DumpRomFSRequested(u64 program_id, const std::string& game_path); 93 void DumpRomFSRequested(u64 program_id, const std::string& game_path);
93 void CopyTIDRequested(u64 program_id); 94 void CopyTIDRequested(u64 program_id);
94 void NavigateToGamedbEntryRequested(u64 program_id, 95 void NavigateToGamedbEntryRequested(u64 program_id,
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 0f0e228b0..dd8dd3233 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1334,7 +1334,10 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
1334 1334
1335 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { 1335 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
1336 // Load per game settings 1336 // Load per game settings
1337 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); 1337 const auto config_file_name = title_id == 0
1338 ? Common::FS::GetFilename(filename.toStdString())
1339 : fmt::format("{:016X}", title_id);
1340 Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
1338 } 1341 }
1339 1342
1340 ConfigureVibration::SetAllVibrationDevices(); 1343 ConfigureVibration::SetAllVibrationDevices();
@@ -1795,7 +1798,8 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type)
1795 tr("Successfully removed %1 installed DLC.").arg(count)); 1798 tr("Successfully removed %1 installed DLC.").arg(count));
1796} 1799}
1797 1800
1798void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target) { 1801void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
1802 std::string_view game_path) {
1799 const QString question = [this, target] { 1803 const QString question = [this, target] {
1800 switch (target) { 1804 switch (target) {
1801 case GameListRemoveTarget::ShaderCache: 1805 case GameListRemoveTarget::ShaderCache:
@@ -1817,7 +1821,7 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
1817 RemoveTransferableShaderCache(program_id); 1821 RemoveTransferableShaderCache(program_id);
1818 break; 1822 break;
1819 case GameListRemoveTarget::CustomConfiguration: 1823 case GameListRemoveTarget::CustomConfiguration:
1820 RemoveCustomConfiguration(program_id); 1824 RemoveCustomConfiguration(program_id, game_path);
1821 break; 1825 break;
1822 } 1826 }
1823} 1827}
@@ -1842,9 +1846,12 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
1842 } 1846 }
1843} 1847}
1844 1848
1845void GMainWindow::RemoveCustomConfiguration(u64 program_id) { 1849void GMainWindow::RemoveCustomConfiguration(u64 program_id, std::string_view game_path) {
1846 const auto custom_config_file_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / 1850 const auto config_file_name = program_id == 0
1847 "custom" / fmt::format("{:016X}.ini", program_id); 1851 ? fmt::format("{:s}.ini", Common::FS::GetFilename(game_path))
1852 : fmt::format("{:016X}.ini", program_id);
1853 const auto custom_config_file_path =
1854 Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "custom" / config_file_name;
1848 1855
1849 if (!Common::FS::Exists(custom_config_file_path)) { 1856 if (!Common::FS::Exists(custom_config_file_path)) {
1850 QMessageBox::warning(this, tr("Error Removing Custom Configuration"), 1857 QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
@@ -2635,7 +2642,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
2635 const auto v_file = Core::GetGameFileFromPath(vfs, file_name); 2642 const auto v_file = Core::GetGameFileFromPath(vfs, file_name);
2636 const auto& system = Core::System::GetInstance(); 2643 const auto& system = Core::System::GetInstance();
2637 2644
2638 ConfigurePerGame dialog(this, title_id); 2645 ConfigurePerGame dialog(this, title_id, file_name);
2639 dialog.LoadFromFile(v_file); 2646 dialog.LoadFromFile(v_file);
2640 const auto result = dialog.exec(); 2647 const auto result = dialog.exec();
2641 2648
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b3a5033ce..135681d41 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -236,7 +236,8 @@ private slots:
236 const std::string& game_path); 236 const std::string& game_path);
237 void OnTransferableShaderCacheOpenFile(u64 program_id); 237 void OnTransferableShaderCacheOpenFile(u64 program_id);
238 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); 238 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
239 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target); 239 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
240 std::string_view game_path);
240 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); 241 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
241 void OnGameListCopyTID(u64 program_id); 242 void OnGameListCopyTID(u64 program_id);
242 void OnGameListNavigateToGamedbEntry(u64 program_id, 243 void OnGameListNavigateToGamedbEntry(u64 program_id,
@@ -275,7 +276,7 @@ private:
275 void RemoveUpdateContent(u64 program_id, const QString& entry_type); 276 void RemoveUpdateContent(u64 program_id, const QString& entry_type);
276 void RemoveAddOnContent(u64 program_id, const QString& entry_type); 277 void RemoveAddOnContent(u64 program_id, const QString& entry_type);
277 void RemoveTransferableShaderCache(u64 program_id); 278 void RemoveTransferableShaderCache(u64 program_id);
278 void RemoveCustomConfiguration(u64 program_id); 279 void RemoveCustomConfiguration(u64 program_id, std::string_view game_path);
279 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 280 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
280 InstallResult InstallNSPXCI(const QString& filename); 281 InstallResult InstallNSPXCI(const QString& filename);
281 InstallResult InstallNCA(const QString& filename); 282 InstallResult InstallNCA(const QString& filename);