summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts16
-rw-r--r--src/android/app/src/main/AndroidManifest.xml1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt25
-rw-r--r--src/android/build.gradle.kts4
-rw-r--r--src/audio_core/sink/sink_stream.cpp4
-rw-r--r--src/common/polyfill_thread.h21
-rw-r--r--src/common/settings.cpp5
-rw-r--r--src/common/settings.h5
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/core/core.cpp7
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp84
-rw-r--r--src/core/file_sys/patch_manager.cpp8
-rw-r--r--src/core/file_sys/registered_cache.cpp3
-rw-r--r--src/core/file_sys/romfs.cpp5
-rw-r--r--src/core/file_sys/vfs_cached.cpp6
-rw-r--r--src/core/file_sys/vfs_cached.h2
-rw-r--r--src/core/file_sys/vfs_concat.cpp27
-rw-r--r--src/core/file_sys/vfs_concat.h12
-rw-r--r--src/core/file_sys/vfs_layered.cpp8
-rw-r--r--src/core/hle/service/acc/acc.cpp43
-rw-r--r--src/core/hle/service/acc/acc.h3
-rw-r--r--src/core/hle/service/acc/acc_su.cpp6
-rw-r--r--src/core/hle/service/acc/profile_manager.h3
-rw-r--r--src/core/hle/service/am/am.cpp41
-rw-r--r--src/core/hle/service/am/am.h3
-rw-r--r--src/input_common/drivers/udp_client.cpp1
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h4
-rw-r--r--src/video_core/engines/draw_manager.cpp24
-rw-r--r--src/video_core/engines/draw_manager.h2
-rw-r--r--src/video_core/host1x/codecs/codec.cpp10
-rw-r--r--src/video_core/host_shaders/convert_d24s8_to_abgr8.frag8
-rw-r--r--src/video_core/host_shaders/convert_s8d24_to_abgr8.frag8
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.cpp2
-rw-r--r--src/video_core/texture_cache/formatter.cpp8
-rw-r--r--src/video_core/texture_cache/samples_helper.h2
-rw-r--r--src/video_core/texture_cache/util.cpp11
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/configuration/config.cpp4
-rw-r--r--src/yuzu/configuration/configure_input.cpp36
-rw-r--r--src/yuzu/configuration/configure_input.h1
-rw-r--r--src/yuzu/configuration/shared_translation.cpp8
-rw-r--r--src/yuzu/main.cpp115
-rw-r--r--src/yuzu/main.h18
-rw-r--r--src/yuzu/uisettings.h11
46 files changed, 428 insertions, 205 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 84a3308b7..ac43d84b7 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -27,7 +27,7 @@ android {
27 namespace = "org.yuzu.yuzu_emu" 27 namespace = "org.yuzu.yuzu_emu"
28 28
29 compileSdkVersion = "android-34" 29 compileSdkVersion = "android-34"
30 ndkVersion = "25.2.9519653" 30 ndkVersion = "26.1.10909125"
31 31
32 buildFeatures { 32 buildFeatures {
33 viewBinding = true 33 viewBinding = true
@@ -203,23 +203,23 @@ ktlint {
203} 203}
204 204
205dependencies { 205dependencies {
206 implementation("androidx.core:core-ktx:1.10.1") 206 implementation("androidx.core:core-ktx:1.12.0")
207 implementation("androidx.appcompat:appcompat:1.6.1") 207 implementation("androidx.appcompat:appcompat:1.6.1")
208 implementation("androidx.recyclerview:recyclerview:1.3.0") 208 implementation("androidx.recyclerview:recyclerview:1.3.1")
209 implementation("androidx.constraintlayout:constraintlayout:2.1.4") 209 implementation("androidx.constraintlayout:constraintlayout:2.1.4")
210 implementation("androidx.fragment:fragment-ktx:1.6.0") 210 implementation("androidx.fragment:fragment-ktx:1.6.1")
211 implementation("androidx.documentfile:documentfile:1.0.1") 211 implementation("androidx.documentfile:documentfile:1.0.1")
212 implementation("com.google.android.material:material:1.9.0") 212 implementation("com.google.android.material:material:1.9.0")
213 implementation("androidx.preference:preference:1.2.0") 213 implementation("androidx.preference:preference-ktx:1.2.1")
214 implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1") 214 implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
215 implementation("io.coil-kt:coil:2.2.2") 215 implementation("io.coil-kt:coil:2.2.2")
216 implementation("androidx.core:core-splashscreen:1.0.1") 216 implementation("androidx.core:core-splashscreen:1.0.1")
217 implementation("androidx.window:window:1.2.0-beta03") 217 implementation("androidx.window:window:1.2.0-beta03")
218 implementation("org.ini4j:ini4j:0.5.4") 218 implementation("org.ini4j:ini4j:0.5.4")
219 implementation("androidx.constraintlayout:constraintlayout:2.1.4") 219 implementation("androidx.constraintlayout:constraintlayout:2.1.4")
220 implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") 220 implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
221 implementation("androidx.navigation:navigation-fragment-ktx:2.6.0") 221 implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
222 implementation("androidx.navigation:navigation-ui-ktx:2.6.0") 222 implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
223 implementation("info.debatty:java-string-similarity:2.0.0") 223 implementation("info.debatty:java-string-similarity:2.0.0")
224 implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") 224 implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
225} 225}
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 832c08e15..a67351727 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -28,7 +28,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
28 android:appCategory="game" 28 android:appCategory="game"
29 android:localeConfig="@xml/locales_config" 29 android:localeConfig="@xml/locales_config"
30 android:banner="@drawable/tv_banner" 30 android:banner="@drawable/tv_banner"
31 android:extractNativeLibs="true"
32 android:fullBackupContent="@xml/data_extraction_rules" 31 android:fullBackupContent="@xml/data_extraction_rules"
33 android:dataExtractionRules="@xml/data_extraction_rules_api_31" 32 android:dataExtractionRules="@xml/data_extraction_rules_api_31"
34 android:enableOnBackInvokedCallback="true"> 33 android:enableOnBackInvokedCallback="true">
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index 10b1d3547..df21d74b2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.model.DriverViewModel
26import org.yuzu.yuzu_emu.model.HomeViewModel 26import org.yuzu.yuzu_emu.model.HomeViewModel
27import org.yuzu.yuzu_emu.utils.FileUtil 27import org.yuzu.yuzu_emu.utils.FileUtil
28import org.yuzu.yuzu_emu.utils.GpuDriverHelper 28import org.yuzu.yuzu_emu.utils.GpuDriverHelper
29import java.io.File
29import java.io.IOException 30import java.io.IOException
30 31
31class DriverManagerFragment : Fragment() { 32class DriverManagerFragment : Fragment() {
@@ -154,29 +155,29 @@ class DriverManagerFragment : Fragment() {
154 R.string.installing_driver, 155 R.string.installing_driver,
155 false 156 false
156 ) { 157 ) {
158 val driverPath =
159 "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
160 val driverFile = File(driverPath)
161
157 // Ignore file exceptions when a user selects an invalid zip 162 // Ignore file exceptions when a user selects an invalid zip
158 try { 163 try {
159 GpuDriverHelper.copyDriverToInternalStorage(result) 164 if (!GpuDriverHelper.copyDriverToInternalStorage(result)) {
165 throw IOException("Driver failed validation!")
166 }
160 } catch (_: IOException) { 167 } catch (_: IOException) {
168 if (driverFile.exists()) {
169 driverFile.delete()
170 }
161 return@newInstance getString(R.string.select_gpu_driver_error) 171 return@newInstance getString(R.string.select_gpu_driver_error)
162 } 172 }
163 173
164 val driverData = GpuDriverHelper.customDriverData 174 val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
165 if (driverData.name == null) {
166 return@newInstance getString(R.string.select_gpu_driver_error)
167 }
168
169 val driverInList = 175 val driverInList =
170 driverViewModel.driverList.value.firstOrNull { it.second == driverData } 176 driverViewModel.driverList.value.firstOrNull { it.second == driverData }
171 if (driverInList != null) { 177 if (driverInList != null) {
172 return@newInstance getString(R.string.driver_already_installed) 178 return@newInstance getString(R.string.driver_already_installed)
173 } else { 179 } else {
174 driverViewModel.addDriver( 180 driverViewModel.addDriver(Pair(driverPath, driverData))
175 Pair(
176 "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}",
177 driverData
178 )
179 )
180 driverViewModel.setNewDriverInstalled(true) 181 driverViewModel.setNewDriverInstalled(true)
181 } 182 }
182 return@newInstance Any() 183 return@newInstance Any()
diff --git a/src/android/build.gradle.kts b/src/android/build.gradle.kts
index 80f370c16..51e559321 100644
--- a/src/android/build.gradle.kts
+++ b/src/android/build.gradle.kts
@@ -3,8 +3,8 @@
3 3
4// Top-level build file where you can add configuration options common to all sub-projects/modules. 4// Top-level build file where you can add configuration options common to all sub-projects/modules.
5plugins { 5plugins {
6 id("com.android.application") version "8.0.2" apply false 6 id("com.android.application") version "8.1.2" apply false
7 id("com.android.library") version "8.0.2" apply false 7 id("com.android.library") version "8.1.2" apply false
8 id("org.jetbrains.kotlin.android") version "1.8.21" apply false 8 id("org.jetbrains.kotlin.android") version "1.8.21" apply false
9} 9}
10 10
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index 6081352a2..d66d04fae 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -204,6 +204,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
204 // paused and we'll desync, so just play silence. 204 // paused and we'll desync, so just play silence.
205 if (system.IsPaused() || system.IsShuttingDown()) { 205 if (system.IsPaused() || system.IsShuttingDown()) {
206 if (system.IsShuttingDown()) { 206 if (system.IsShuttingDown()) {
207 {
208 std::scoped_lock lk{release_mutex};
209 queued_buffers.store(0);
210 }
207 release_cv.notify_one(); 211 release_cv.notify_one();
208 } 212 }
209 213
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
index 41cbb9ed5..12e59a893 100644
--- a/src/common/polyfill_thread.h
+++ b/src/common/polyfill_thread.h
@@ -15,12 +15,13 @@
15#include <condition_variable> 15#include <condition_variable>
16#include <stop_token> 16#include <stop_token>
17#include <thread> 17#include <thread>
18#include <utility>
18 19
19namespace Common { 20namespace Common {
20 21
21template <typename Condvar, typename Lock, typename Pred> 22template <typename Condvar, typename Lock, typename Pred>
22void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) { 23void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) {
23 cv.wait(lk, token, std::move(pred)); 24 cv.wait(lk, token, std::forward<Pred>(pred));
24} 25}
25 26
26template <typename Rep, typename Period> 27template <typename Rep, typename Period>
@@ -109,7 +110,7 @@ public:
109 110
110 // Insert the callback. 111 // Insert the callback.
111 stop_state_callback ret = ++m_next_callback; 112 stop_state_callback ret = ++m_next_callback;
112 m_callbacks.emplace(ret, move(f)); 113 m_callbacks.emplace(ret, std::move(f));
113 return ret; 114 return ret;
114 } 115 }
115 116
@@ -162,7 +163,7 @@ private:
162 friend class stop_source; 163 friend class stop_source;
163 template <typename Callback> 164 template <typename Callback>
164 friend class stop_callback; 165 friend class stop_callback;
165 stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {} 166 stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(std::move(stop_state)) {}
166 167
167private: 168private:
168 shared_ptr<polyfill::stop_state> m_stop_state; 169 shared_ptr<polyfill::stop_state> m_stop_state;
@@ -198,7 +199,7 @@ public:
198private: 199private:
199 friend class jthread; 200 friend class jthread;
200 explicit stop_source(shared_ptr<polyfill::stop_state> stop_state) 201 explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
201 : m_stop_state(move(stop_state)) {} 202 : m_stop_state(std::move(stop_state)) {}
202 203
203private: 204private:
204 shared_ptr<polyfill::stop_state> m_stop_state; 205 shared_ptr<polyfill::stop_state> m_stop_state;
@@ -218,16 +219,16 @@ public:
218 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) 219 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
219 : m_stop_state(st.m_stop_state) { 220 : m_stop_state(st.m_stop_state) {
220 if (m_stop_state) { 221 if (m_stop_state) {
221 m_callback = m_stop_state->insert_callback(move(cb)); 222 m_callback = m_stop_state->insert_callback(std::move(cb));
222 } 223 }
223 } 224 }
224 template <typename C> 225 template <typename C>
225 requires constructible_from<Callback, C> 226 requires constructible_from<Callback, C>
226 explicit stop_callback(stop_token&& st, 227 explicit stop_callback(stop_token&& st,
227 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) 228 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
228 : m_stop_state(move(st.m_stop_state)) { 229 : m_stop_state(std::move(st.m_stop_state)) {
229 if (m_stop_state) { 230 if (m_stop_state) {
230 m_callback = m_stop_state->insert_callback(move(cb)); 231 m_callback = m_stop_state->insert_callback(std::move(cb));
231 } 232 }
232 } 233 }
233 ~stop_callback() { 234 ~stop_callback() {
@@ -260,7 +261,7 @@ public:
260 typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>> 261 typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
261 explicit jthread(F&& f, Args&&... args) 262 explicit jthread(F&& f, Args&&... args)
262 : m_stop_state(make_shared<polyfill::stop_state>()), 263 : m_stop_state(make_shared<polyfill::stop_state>()),
263 m_thread(make_thread(move(f), move(args)...)) {} 264 m_thread(make_thread(std::forward<F>(f), std::forward<Args>(args)...)) {}
264 265
265 ~jthread() { 266 ~jthread() {
266 if (joinable()) { 267 if (joinable()) {
@@ -317,9 +318,9 @@ private:
317 template <typename F, typename... Args> 318 template <typename F, typename... Args>
318 thread make_thread(F&& f, Args&&... args) { 319 thread make_thread(F&& f, Args&&... args) {
319 if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) { 320 if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
320 return thread(move(f), get_stop_token(), move(args)...); 321 return thread(std::forward<F>(f), get_stop_token(), std::forward<Args>(args)...);
321 } else { 322 } else {
322 return thread(move(f), move(args)...); 323 return thread(std::forward<F>(f), std::forward<Args>(args)...);
323 } 324 }
324 } 325 }
325 326
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 3fde3cae6..98b43e49c 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -45,6 +45,7 @@ SWITCHABLE(CpuAccuracy, true);
45SWITCHABLE(FullscreenMode, true); 45SWITCHABLE(FullscreenMode, true);
46SWITCHABLE(GpuAccuracy, true); 46SWITCHABLE(GpuAccuracy, true);
47SWITCHABLE(Language, true); 47SWITCHABLE(Language, true);
48SWITCHABLE(MemoryLayout, true);
48SWITCHABLE(NvdecEmulation, false); 49SWITCHABLE(NvdecEmulation, false);
49SWITCHABLE(Region, true); 50SWITCHABLE(Region, true);
50SWITCHABLE(RendererBackend, true); 51SWITCHABLE(RendererBackend, true);
@@ -61,6 +62,10 @@ SWITCHABLE(u32, false);
61SWITCHABLE(u8, false); 62SWITCHABLE(u8, false);
62SWITCHABLE(u8, true); 63SWITCHABLE(u8, true);
63 64
65// Used in UISettings
66// TODO see if we can move this to uisettings.cpp
67SWITCHABLE(ConfirmStop, true);
68
64#undef SETTING 69#undef SETTING
65#undef SWITCHABLE 70#undef SWITCHABLE
66#endif 71#endif
diff --git a/src/common/settings.h b/src/common/settings.h
index 98ab0ec2e..236e33bee 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -67,6 +67,7 @@ SWITCHABLE(CpuAccuracy, true);
67SWITCHABLE(FullscreenMode, true); 67SWITCHABLE(FullscreenMode, true);
68SWITCHABLE(GpuAccuracy, true); 68SWITCHABLE(GpuAccuracy, true);
69SWITCHABLE(Language, true); 69SWITCHABLE(Language, true);
70SWITCHABLE(MemoryLayout, true);
70SWITCHABLE(NvdecEmulation, false); 71SWITCHABLE(NvdecEmulation, false);
71SWITCHABLE(Region, true); 72SWITCHABLE(Region, true);
72SWITCHABLE(RendererBackend, true); 73SWITCHABLE(RendererBackend, true);
@@ -83,6 +84,10 @@ SWITCHABLE(u32, false);
83SWITCHABLE(u8, false); 84SWITCHABLE(u8, false);
84SWITCHABLE(u8, true); 85SWITCHABLE(u8, true);
85 86
87// Used in UISettings
88// TODO see if we can move this to uisettings.h
89SWITCHABLE(ConfirmStop, true);
90
86#undef SETTING 91#undef SETTING
87#undef SWITCHABLE 92#undef SWITCHABLE
88#endif 93#endif
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index 815cafe15..11429d7a8 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -133,6 +133,8 @@ ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
133 133
134ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); 134ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
135 135
136ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
137
136ENUM(FullscreenMode, Borderless, Exclusive); 138ENUM(FullscreenMode, Borderless, Exclusive);
137 139
138ENUM(NvdecEmulation, Off, Cpu, Gpu); 140ENUM(NvdecEmulation, Off, Cpu, Gpu);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 0ab2e3b76..d7e2efbd7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -116,11 +116,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
116 } 116 }
117 } 117 }
118 118
119 if (concat.empty()) { 119 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(dir->GetName(),
120 return nullptr; 120 std::move(concat));
121 }
122
123 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
124 } 121 }
125 122
126 if (Common::FS::IsDir(path)) { 123 if (Common::FS::IsDir(path)) {
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index e39c7b62b..f1d3e4129 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -107,62 +107,56 @@ static u64 romfs_get_hash_table_count(u64 num_entries) {
107 107
108void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir, 108void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
109 std::shared_ptr<RomFSBuildDirectoryContext> parent) { 109 std::shared_ptr<RomFSBuildDirectoryContext> parent) {
110 std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; 110 for (auto& child_romfs_file : romfs_dir->GetFiles()) {
111 const auto name = child_romfs_file->GetName();
112 const auto child = std::make_shared<RomFSBuildFileContext>();
113 // Set child's path.
114 child->cur_path_ofs = parent->path_len + 1;
115 child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
116 child->path = parent->path + "/" + name;
117
118 if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
119 continue;
120 }
111 121
112 const auto entries = romfs_dir->GetEntries(); 122 // Sanity check on path_len
123 ASSERT(child->path_len < FS_MAX_PATH);
113 124
114 for (const auto& kv : entries) { 125 child->source = std::move(child_romfs_file);
115 if (kv.second == VfsEntryType::Directory) {
116 const auto child = std::make_shared<RomFSBuildDirectoryContext>();
117 // Set child's path.
118 child->cur_path_ofs = parent->path_len + 1;
119 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
120 child->path = parent->path + "/" + kv.first;
121 126
122 if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) { 127 if (ext_dir != nullptr) {
123 continue; 128 if (const auto ips = ext_dir->GetFile(name + ".ips")) {
129 if (auto patched = PatchIPS(child->source, ips)) {
130 child->source = std::move(patched);
131 }
124 } 132 }
133 }
125 134
126 // Sanity check on path_len 135 child->size = child->source->GetSize();
127 ASSERT(child->path_len < FS_MAX_PATH);
128
129 if (AddDirectory(parent, child)) {
130 child_dirs.push_back(child);
131 }
132 } else {
133 const auto child = std::make_shared<RomFSBuildFileContext>();
134 // Set child's path.
135 child->cur_path_ofs = parent->path_len + 1;
136 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
137 child->path = parent->path + "/" + kv.first;
138
139 if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
140 continue;
141 }
142 136
143 // Sanity check on path_len 137 AddFile(parent, child);
144 ASSERT(child->path_len < FS_MAX_PATH); 138 }
145 139
146 child->source = romfs_dir->GetFile(kv.first); 140 for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) {
141 const auto name = child_romfs_dir->GetName();
142 const auto child = std::make_shared<RomFSBuildDirectoryContext>();
143 // Set child's path.
144 child->cur_path_ofs = parent->path_len + 1;
145 child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
146 child->path = parent->path + "/" + name;
147 147
148 if (ext_dir != nullptr) { 148 if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
149 if (const auto ips = ext_dir->GetFile(kv.first + ".ips")) { 149 continue;
150 if (auto patched = PatchIPS(child->source, ips)) { 150 }
151 child->source = std::move(patched);
152 }
153 }
154 }
155 151
156 child->size = child->source->GetSize(); 152 // Sanity check on path_len
153 ASSERT(child->path_len < FS_MAX_PATH);
157 154
158 AddFile(parent, child); 155 if (!AddDirectory(parent, child)) {
156 continue;
159 } 157 }
160 }
161 158
162 for (auto& child : child_dirs) { 159 auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(name) : nullptr;
163 auto subdir_name = std::string_view(child->path).substr(child->cur_path_ofs);
164 auto child_romfs_dir = romfs_dir->GetSubdirectory(subdir_name);
165 auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(subdir_name) : nullptr;
166 this->VisitDirectory(child_romfs_dir, child_ext_dir, child); 160 this->VisitDirectory(child_romfs_dir, child_ext_dir, child);
167 } 161 }
168} 162}
@@ -293,7 +287,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
293 287
294 cur_entry.name_size = name_size; 288 cur_entry.name_size = name_size;
295 289
296 out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source); 290 out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source));
297 std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry)); 291 std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
298 std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0, 292 std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
299 Common::AlignUp(cur_entry.name_size, 4)); 293 Common::AlignUp(cur_entry.name_size, 4));
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 8e475f25a..0bca05587 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -377,16 +377,16 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
377 377
378 auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs"); 378 auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs");
379 if (romfs_dir != nullptr) 379 if (romfs_dir != nullptr)
380 layers.push_back(std::make_shared<CachedVfsDirectory>(romfs_dir)); 380 layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfs_dir)));
381 381
382 auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext"); 382 auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
383 if (ext_dir != nullptr) 383 if (ext_dir != nullptr)
384 layers_ext.push_back(std::make_shared<CachedVfsDirectory>(ext_dir)); 384 layers_ext.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(ext_dir)));
385 385
386 if (type == ContentRecordType::HtmlDocument) { 386 if (type == ContentRecordType::HtmlDocument) {
387 auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html"); 387 auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html");
388 if (manual_dir != nullptr) 388 if (manual_dir != nullptr)
389 layers.push_back(std::make_shared<CachedVfsDirectory>(manual_dir)); 389 layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(manual_dir)));
390 } 390 }
391 } 391 }
392 392
@@ -400,7 +400,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
400 return; 400 return;
401 } 401 }
402 402
403 layers.push_back(std::move(extracted)); 403 layers.emplace_back(std::move(extracted));
404 404
405 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); 405 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
406 if (layered == nullptr) { 406 if (layered == nullptr) {
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 04da93d5c..1cc77ad14 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -322,7 +322,8 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& open_di
322 return nullptr; 322 return nullptr;
323 } 323 }
324 324
325 return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName()); 325 auto name = concat.front()->GetName();
326 return ConcatenatedVfsFile::MakeConcatenatedFile(std::move(name), std::move(concat));
326} 327}
327 328
328VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { 329VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 614da2130..1c580de57 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -133,7 +133,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
133 out = out->GetSubdirectories().front(); 133 out = out->GetSubdirectories().front();
134 } 134 }
135 135
136 return std::make_shared<CachedVfsDirectory>(out); 136 return std::make_shared<CachedVfsDirectory>(std::move(out));
137} 137}
138 138
139VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { 139VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
@@ -141,8 +141,7 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
141 return nullptr; 141 return nullptr;
142 142
143 RomFSBuildContext ctx{dir, ext}; 143 RomFSBuildContext ctx{dir, ext};
144 auto file_map = ctx.Build(); 144 return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build());
145 return ConcatenatedVfsFile::MakeConcatenatedFile(0, file_map, dir->GetName());
146} 145}
147 146
148} // namespace FileSys 147} // namespace FileSys
diff --git a/src/core/file_sys/vfs_cached.cpp b/src/core/file_sys/vfs_cached.cpp
index c3154ee81..7ee5300e5 100644
--- a/src/core/file_sys/vfs_cached.cpp
+++ b/src/core/file_sys/vfs_cached.cpp
@@ -6,13 +6,13 @@
6 6
7namespace FileSys { 7namespace FileSys {
8 8
9CachedVfsDirectory::CachedVfsDirectory(VirtualDir& source_dir) 9CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir)
10 : name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) { 10 : name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) {
11 for (auto& dir : source_dir->GetSubdirectories()) { 11 for (auto& dir : source_dir->GetSubdirectories()) {
12 dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(dir)); 12 dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir)));
13 } 13 }
14 for (auto& file : source_dir->GetFiles()) { 14 for (auto& file : source_dir->GetFiles()) {
15 files.emplace(file->GetName(), file); 15 files.emplace(file->GetName(), std::move(file));
16 } 16 }
17} 17}
18 18
diff --git a/src/core/file_sys/vfs_cached.h b/src/core/file_sys/vfs_cached.h
index 113acac12..1e5300784 100644
--- a/src/core/file_sys/vfs_cached.h
+++ b/src/core/file_sys/vfs_cached.h
@@ -11,7 +11,7 @@ namespace FileSys {
11 11
12class CachedVfsDirectory : public ReadOnlyVfsDirectory { 12class CachedVfsDirectory : public ReadOnlyVfsDirectory {
13public: 13public:
14 CachedVfsDirectory(VirtualDir& source_directory); 14 CachedVfsDirectory(VirtualDir&& source_directory);
15 15
16 ~CachedVfsDirectory() override; 16 ~CachedVfsDirectory() override;
17 VirtualFile GetFile(std::string_view file_name) const override; 17 VirtualFile GetFile(std::string_view file_name) const override;
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index 311a59e5f..168b9cbec 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -10,7 +10,7 @@
10 10
11namespace FileSys { 11namespace FileSys {
12 12
13ConcatenatedVfsFile::ConcatenatedVfsFile(ConcatenationMap&& concatenation_map_, std::string&& name_) 13ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_)
14 : concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) { 14 : concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
15 DEBUG_ASSERT(this->VerifyContinuity()); 15 DEBUG_ASSERT(this->VerifyContinuity());
16} 16}
@@ -30,8 +30,8 @@ bool ConcatenatedVfsFile::VerifyContinuity() const {
30 30
31ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; 31ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
32 32
33VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualFile>& files, 33VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
34 std::string&& name) { 34 std::vector<VirtualFile>&& files) {
35 // Fold trivial cases. 35 // Fold trivial cases.
36 if (files.empty()) { 36 if (files.empty()) {
37 return nullptr; 37 return nullptr;
@@ -46,20 +46,21 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualF
46 u64 last_offset = 0; 46 u64 last_offset = 0;
47 47
48 for (auto& file : files) { 48 for (auto& file : files) {
49 const auto size = file->GetSize();
50
49 concatenation_map.emplace_back(ConcatenationEntry{ 51 concatenation_map.emplace_back(ConcatenationEntry{
50 .offset = last_offset, 52 .offset = last_offset,
51 .file = file, 53 .file = std::move(file),
52 }); 54 });
53 55
54 last_offset += file->GetSize(); 56 last_offset += size;
55 } 57 }
56 58
57 return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name))); 59 return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
58} 60}
59 61
60VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, 62VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::string&& name,
61 const std::multimap<u64, VirtualFile>& files, 63 std::multimap<u64, VirtualFile>&& files) {
62 std::string&& name) {
63 // Fold trivial cases. 64 // Fold trivial cases.
64 if (files.empty()) { 65 if (files.empty()) {
65 return nullptr; 66 return nullptr;
@@ -76,6 +77,8 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
76 77
77 // Iteration of a multimap is ordered, so offset will be strictly non-decreasing. 78 // Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
78 for (auto& [offset, file] : files) { 79 for (auto& [offset, file] : files) {
80 const auto size = file->GetSize();
81
79 if (offset > last_offset) { 82 if (offset > last_offset) {
80 concatenation_map.emplace_back(ConcatenationEntry{ 83 concatenation_map.emplace_back(ConcatenationEntry{
81 .offset = last_offset, 84 .offset = last_offset,
@@ -85,13 +88,13 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
85 88
86 concatenation_map.emplace_back(ConcatenationEntry{ 89 concatenation_map.emplace_back(ConcatenationEntry{
87 .offset = offset, 90 .offset = offset,
88 .file = file, 91 .file = std::move(file),
89 }); 92 });
90 93
91 last_offset = offset + file->GetSize(); 94 last_offset = offset + size;
92 } 95 }
93 96
94 return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name))); 97 return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
95} 98}
96 99
97std::string ConcatenatedVfsFile::GetName() const { 100std::string ConcatenatedVfsFile::GetName() const {
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index 6b329d545..cbddd12bd 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -24,22 +24,20 @@ private:
24 }; 24 };
25 using ConcatenationMap = std::vector<ConcatenationEntry>; 25 using ConcatenationMap = std::vector<ConcatenationEntry>;
26 26
27 explicit ConcatenatedVfsFile(std::vector<ConcatenationEntry>&& concatenation_map, 27 explicit ConcatenatedVfsFile(std::string&& name,
28 std::string&& name); 28 std::vector<ConcatenationEntry>&& concatenation_map);
29 bool VerifyContinuity() const; 29 bool VerifyContinuity() const;
30 30
31public: 31public:
32 ~ConcatenatedVfsFile() override; 32 ~ConcatenatedVfsFile() override;
33 33
34 /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. 34 /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
35 static VirtualFile MakeConcatenatedFile(const std::vector<VirtualFile>& files, 35 static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files);
36 std::string&& name);
37 36
38 /// Convenience function that turns a map of offsets to files into a concatenated file, filling 37 /// Convenience function that turns a map of offsets to files into a concatenated file, filling
39 /// gaps with a given filler byte. 38 /// gaps with a given filler byte.
40 static VirtualFile MakeConcatenatedFile(u8 filler_byte, 39 static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name,
41 const std::multimap<u64, VirtualFile>& files, 40 std::multimap<u64, VirtualFile>&& files);
42 std::string&& name);
43 41
44 std::string GetName() const override; 42 std::string GetName() const override;
45 std::size_t GetSize() const override; 43 std::size_t GetSize() const override;
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
index 3e6426afc..08daca397 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -38,7 +38,7 @@ VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) cons
38 for (const auto& layer : dirs) { 38 for (const auto& layer : dirs) {
39 auto dir = layer->GetDirectoryRelative(path); 39 auto dir = layer->GetDirectoryRelative(path);
40 if (dir != nullptr) { 40 if (dir != nullptr) {
41 out.push_back(std::move(dir)); 41 out.emplace_back(std::move(dir));
42 } 42 }
43 } 43 }
44 44
@@ -62,11 +62,11 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
62 std::set<std::string, std::less<>> out_names; 62 std::set<std::string, std::less<>> out_names;
63 63
64 for (const auto& layer : dirs) { 64 for (const auto& layer : dirs) {
65 for (const auto& file : layer->GetFiles()) { 65 for (auto& file : layer->GetFiles()) {
66 auto file_name = file->GetName(); 66 auto file_name = file->GetName();
67 if (!out_names.contains(file_name)) { 67 if (!out_names.contains(file_name)) {
68 out_names.emplace(std::move(file_name)); 68 out_names.emplace(std::move(file_name));
69 out.push_back(file); 69 out.emplace_back(std::move(file));
70 } 70 }
71 } 71 }
72 } 72 }
@@ -86,7 +86,7 @@ std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
86 std::vector<VirtualDir> out; 86 std::vector<VirtualDir> out;
87 out.reserve(names.size()); 87 out.reserve(names.size());
88 for (const auto& subdir : names) 88 for (const auto& subdir : names)
89 out.push_back(GetSubdirectory(subdir)); 89 out.emplace_back(GetSubdirectory(subdir));
90 90
91 return out; 91 return out;
92} 92}
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 3fe7e565c..1b1c8190e 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -49,7 +49,7 @@ public:
49 : ServiceFramework{system_, "IManagerForSystemService"} { 49 : ServiceFramework{system_, "IManagerForSystemService"} {
50 // clang-format off 50 // clang-format off
51 static const FunctionInfo functions[] = { 51 static const FunctionInfo functions[] = {
52 {0, nullptr, "CheckAvailability"}, 52 {0, &IManagerForSystemService::CheckAvailability, "CheckAvailability"},
53 {1, nullptr, "GetAccountId"}, 53 {1, nullptr, "GetAccountId"},
54 {2, nullptr, "EnsureIdTokenCacheAsync"}, 54 {2, nullptr, "EnsureIdTokenCacheAsync"},
55 {3, nullptr, "LoadIdTokenCache"}, 55 {3, nullptr, "LoadIdTokenCache"},
@@ -78,6 +78,13 @@ public:
78 78
79 RegisterHandlers(functions); 79 RegisterHandlers(functions);
80 } 80 }
81
82private:
83 void CheckAvailability(HLERequestContext& ctx) {
84 LOG_WARNING(Service_ACC, "(STUBBED) called");
85 IPC::ResponseBuilder rb{ctx, 2};
86 rb.Push(ResultSuccess);
87 }
81}; 88};
82 89
83// 3.0.0+ 90// 3.0.0+
@@ -837,6 +844,29 @@ void Module::Interface::InitializeApplicationInfoV2(HLERequestContext& ctx) {
837 rb.Push(ResultSuccess); 844 rb.Push(ResultSuccess);
838} 845}
839 846
847void Module::Interface::BeginUserRegistration(HLERequestContext& ctx) {
848 const auto user_id = Common::UUID::MakeRandom();
849 profile_manager->CreateNewUser(user_id, "yuzu");
850
851 LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString());
852
853 IPC::ResponseBuilder rb{ctx, 6};
854 rb.Push(ResultSuccess);
855 rb.PushRaw(user_id);
856}
857
858void Module::Interface::CompleteUserRegistration(HLERequestContext& ctx) {
859 IPC::RequestParser rp{ctx};
860 Common::UUID user_id = rp.PopRaw<Common::UUID>();
861
862 LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString());
863
864 profile_manager->WriteUserSaveFile();
865
866 IPC::ResponseBuilder rb{ctx, 2};
867 rb.Push(ResultSuccess);
868}
869
840void Module::Interface::GetProfileEditor(HLERequestContext& ctx) { 870void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
841 IPC::RequestParser rp{ctx}; 871 IPC::RequestParser rp{ctx};
842 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 872 Common::UUID user_id = rp.PopRaw<Common::UUID>();
@@ -880,6 +910,17 @@ void Module::Interface::StoreSaveDataThumbnailApplication(HLERequestContext& ctx
880 StoreSaveDataThumbnail(ctx, uuid, tid); 910 StoreSaveDataThumbnail(ctx, uuid, tid);
881} 911}
882 912
913void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext& ctx) {
914 IPC::RequestParser rp{ctx};
915 const auto uuid = rp.PopRaw<Common::UUID>();
916
917 LOG_INFO(Service_ACC, "called, uuid=0x{}", uuid.RawString());
918
919 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
920 rb.Push(ResultSuccess);
921 rb.PushIpcInterface<IManagerForSystemService>(system, uuid);
922}
923
883void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) { 924void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
884 IPC::RequestParser rp{ctx}; 925 IPC::RequestParser rp{ctx};
885 const auto uuid = rp.PopRaw<Common::UUID>(); 926 const auto uuid = rp.PopRaw<Common::UUID>();
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 6b4735c2f..0395229b4 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -33,10 +33,13 @@ public:
33 void TrySelectUserWithoutInteraction(HLERequestContext& ctx); 33 void TrySelectUserWithoutInteraction(HLERequestContext& ctx);
34 void IsUserAccountSwitchLocked(HLERequestContext& ctx); 34 void IsUserAccountSwitchLocked(HLERequestContext& ctx);
35 void InitializeApplicationInfoV2(HLERequestContext& ctx); 35 void InitializeApplicationInfoV2(HLERequestContext& ctx);
36 void BeginUserRegistration(HLERequestContext& ctx);
37 void CompleteUserRegistration(HLERequestContext& ctx);
36 void GetProfileEditor(HLERequestContext& ctx); 38 void GetProfileEditor(HLERequestContext& ctx);
37 void ListQualifiedUsers(HLERequestContext& ctx); 39 void ListQualifiedUsers(HLERequestContext& ctx);
38 void ListOpenContextStoredUsers(HLERequestContext& ctx); 40 void ListOpenContextStoredUsers(HLERequestContext& ctx);
39 void StoreSaveDataThumbnailApplication(HLERequestContext& ctx); 41 void StoreSaveDataThumbnailApplication(HLERequestContext& ctx);
42 void GetBaasAccountManagerForSystemService(HLERequestContext& ctx);
40 void StoreSaveDataThumbnailSystem(HLERequestContext& ctx); 43 void StoreSaveDataThumbnailSystem(HLERequestContext& ctx);
41 44
42 private: 45 private:
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index d9882ecd3..770d13ec5 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -23,7 +23,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
23 {99, nullptr, "DebugActivateOpenContextRetention"}, 23 {99, nullptr, "DebugActivateOpenContextRetention"},
24 {100, nullptr, "GetUserRegistrationNotifier"}, 24 {100, nullptr, "GetUserRegistrationNotifier"},
25 {101, nullptr, "GetUserStateChangeNotifier"}, 25 {101, nullptr, "GetUserStateChangeNotifier"},
26 {102, nullptr, "GetBaasAccountManagerForSystemService"}, 26 {102, &ACC_SU::GetBaasAccountManagerForSystemService, "GetBaasAccountManagerForSystemService"},
27 {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, 27 {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"},
28 {104, nullptr, "GetProfileUpdateNotifier"}, 28 {104, nullptr, "GetProfileUpdateNotifier"},
29 {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, 29 {105, nullptr, "CheckNetworkServiceAvailabilityAsync"},
@@ -40,8 +40,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
40 {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, 40 {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"},
41 {190, nullptr, "GetUserLastOpenedApplication"}, 41 {190, nullptr, "GetUserLastOpenedApplication"},
42 {191, nullptr, "ActivateOpenContextHolder"}, 42 {191, nullptr, "ActivateOpenContextHolder"},
43 {200, nullptr, "BeginUserRegistration"}, 43 {200, &ACC_SU::BeginUserRegistration, "BeginUserRegistration"},
44 {201, nullptr, "CompleteUserRegistration"}, 44 {201, &ACC_SU::CompleteUserRegistration, "CompleteUserRegistration"},
45 {202, nullptr, "CancelUserRegistration"}, 45 {202, nullptr, "CancelUserRegistration"},
46 {203, nullptr, "DeleteUser"}, 46 {203, nullptr, "DeleteUser"},
47 {204, nullptr, "SetUserPosition"}, 47 {204, nullptr, "SetUserPosition"},
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 993a5a57a..900e32200 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -96,9 +96,10 @@ public:
96 bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, 96 bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
97 const UserData& data_new); 97 const UserData& data_new);
98 98
99 void WriteUserSaveFile();
100
99private: 101private:
100 void ParseUserSaveFile(); 102 void ParseUserSaveFile();
101 void WriteUserSaveFile();
102 std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); 103 std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
103 bool RemoveProfileAtIndex(std::size_t index); 104 bool RemoveProfileAtIndex(std::size_t index);
104 105
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index ac376b55a..98765b81a 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -210,8 +210,8 @@ IDisplayController::IDisplayController(Core::System& system_)
210 {21, nullptr, "ClearAppletTransitionBuffer"}, 210 {21, nullptr, "ClearAppletTransitionBuffer"},
211 {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, 211 {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"},
212 {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, 212 {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
213 {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, 213 {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"},
214 {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, 214 {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"},
215 {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"}, 215 {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"},
216 {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"}, 216 {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"},
217 {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, 217 {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
@@ -239,6 +239,22 @@ void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
239 rb.Push(ResultSuccess); 239 rb.Push(ResultSuccess);
240} 240}
241 241
242void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
243 LOG_WARNING(Service_AM, "(STUBBED) called");
244
245 IPC::ResponseBuilder rb{ctx, 4};
246 rb.Push(ResultSuccess);
247 rb.Push(1U);
248 rb.Push(0);
249}
250
251void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
252 LOG_WARNING(Service_AM, "(STUBBED) called");
253
254 IPC::ResponseBuilder rb{ctx, 2};
255 rb.Push(ResultSuccess);
256}
257
242void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { 258void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
243 LOG_WARNING(Service_AM, "(STUBBED) called"); 259 LOG_WARNING(Service_AM, "(STUBBED) called");
244 260
@@ -1557,7 +1573,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
1557 {100, nullptr, "CreateGameMovieTrimmer"}, 1573 {100, nullptr, "CreateGameMovieTrimmer"},
1558 {101, nullptr, "ReserveResourceForMovieOperation"}, 1574 {101, nullptr, "ReserveResourceForMovieOperation"},
1559 {102, nullptr, "UnreserveResourceForMovieOperation"}, 1575 {102, nullptr, "UnreserveResourceForMovieOperation"},
1560 {110, nullptr, "GetMainAppletAvailableUsers"}, 1576 {110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"},
1561 {120, nullptr, "GetLaunchStorageInfoForDebug"}, 1577 {120, nullptr, "GetLaunchStorageInfoForDebug"},
1562 {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, 1578 {130, nullptr, "GetGpuErrorDetectedSystemEvent"},
1563 {140, nullptr, "SetApplicationMemoryReservation"}, 1579 {140, nullptr, "SetApplicationMemoryReservation"},
@@ -1652,6 +1668,25 @@ void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext&
1652 rb.PushRaw(applet_info); 1668 rb.PushRaw(applet_info);
1653} 1669}
1654 1670
1671void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) {
1672 const Service::Account::ProfileManager manager{};
1673 bool is_empty{true};
1674 s32 user_count{-1};
1675
1676 LOG_INFO(Service_AM, "called");
1677
1678 if (manager.GetUserCount() > 0) {
1679 is_empty = false;
1680 user_count = static_cast<s32>(manager.GetUserCount());
1681 ctx.WriteBuffer(manager.GetAllUsers());
1682 }
1683
1684 IPC::ResponseBuilder rb{ctx, 4};
1685 rb.Push(ResultSuccess);
1686 rb.Push<u8>(is_empty);
1687 rb.Push(user_count);
1688}
1689
1655void ILibraryAppletSelfAccessor::PushInShowAlbum() { 1690void ILibraryAppletSelfAccessor::PushInShowAlbum() {
1656 const Applets::CommonArguments arguments{ 1691 const Applets::CommonArguments arguments{
1657 .arguments_version = Applets::CommonArgumentVersion::Version3, 1692 .arguments_version = Applets::CommonArgumentVersion::Version3,
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 4a045cfd4..64b3f3fe2 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -124,6 +124,8 @@ public:
124private: 124private:
125 void GetCallerAppletCaptureImageEx(HLERequestContext& ctx); 125 void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
126 void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); 126 void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
127 void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
128 void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
127 void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); 129 void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
128 void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); 130 void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
129}; 131};
@@ -345,6 +347,7 @@ private:
345 void GetLibraryAppletInfo(HLERequestContext& ctx); 347 void GetLibraryAppletInfo(HLERequestContext& ctx);
346 void ExitProcessAndReturn(HLERequestContext& ctx); 348 void ExitProcessAndReturn(HLERequestContext& ctx);
347 void GetCallerAppletIdentityInfo(HLERequestContext& ctx); 349 void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
350 void GetMainAppletAvailableUsers(HLERequestContext& ctx);
348 351
349 void PushInShowAlbum(); 352 void PushInShowAlbum();
350 void PushInShowCabinetData(); 353 void PushInShowCabinetData();
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp
index 808b21069..77db60e92 100644
--- a/src/input_common/drivers/udp_client.cpp
+++ b/src/input_common/drivers/udp_client.cpp
@@ -338,6 +338,7 @@ void UDPClient::StartCommunication(std::size_t client, const std::string& host,
338 for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) { 338 for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
339 const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index); 339 const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index);
340 PreSetController(identifier); 340 PreSetController(identifier);
341 PreSetMotion(identifier, 0);
341 } 342 }
342} 343}
343 344
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index c4f6e8d12..eed267361 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -62,7 +62,11 @@ using BufferId = SlotId;
62using VideoCore::Surface::PixelFormat; 62using VideoCore::Surface::PixelFormat;
63using namespace Common::Literals; 63using namespace Common::Literals;
64 64
65#ifdef __APPLE__
66constexpr u32 NUM_VERTEX_BUFFERS = 16;
67#else
65constexpr u32 NUM_VERTEX_BUFFERS = 32; 68constexpr u32 NUM_VERTEX_BUFFERS = 32;
69#endif
66constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4; 70constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4;
67constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; 71constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18;
68constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; 72constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8;
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index f34090791..d77ff455b 100644
--- a/src/video_core/engines/draw_manager.cpp
+++ b/src/video_core/engines/draw_manager.cpp
@@ -48,8 +48,14 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
48 SetInlineIndexBuffer(regs.inline_index_4x8.index3); 48 SetInlineIndexBuffer(regs.inline_index_4x8.index3);
49 break; 49 break;
50 case MAXWELL3D_REG_INDEX(vertex_array_instance_first): 50 case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
51 DrawArrayInstanced(regs.vertex_array_instance_first.topology.Value(),
52 regs.vertex_array_instance_first.start.Value(),
53 regs.vertex_array_instance_first.count.Value(), false);
54 break;
51 case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): { 55 case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): {
52 LOG_WARNING(HW_GPU, "(STUBBED) called"); 56 DrawArrayInstanced(regs.vertex_array_instance_subsequent.topology.Value(),
57 regs.vertex_array_instance_subsequent.start.Value(),
58 regs.vertex_array_instance_subsequent.count.Value(), true);
53 break; 59 break;
54 } 60 }
55 case MAXWELL3D_REG_INDEX(draw_texture.src_y0): { 61 case MAXWELL3D_REG_INDEX(draw_texture.src_y0): {
@@ -84,6 +90,22 @@ void DrawManager::DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 ve
84 ProcessDraw(false, num_instances); 90 ProcessDraw(false, num_instances);
85} 91}
86 92
93void DrawManager::DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
94 bool subsequent) {
95 draw_state.topology = topology;
96 draw_state.vertex_buffer.first = vertex_first;
97 draw_state.vertex_buffer.count = vertex_count;
98
99 if (!subsequent) {
100 draw_state.instance_count = 1;
101 }
102
103 draw_state.base_instance = draw_state.instance_count - 1;
104 draw_state.draw_mode = DrawMode::Instance;
105 draw_state.instance_count++;
106 ProcessDraw(false, 1);
107}
108
87void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, 109void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count,
88 u32 base_index, u32 base_instance, u32 num_instances) { 110 u32 base_index, u32 base_instance, u32 num_instances) {
89 const auto& regs{maxwell3d->regs}; 111 const auto& regs{maxwell3d->regs};
diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h
index 18d959143..cfc8127fc 100644
--- a/src/video_core/engines/draw_manager.h
+++ b/src/video_core/engines/draw_manager.h
@@ -66,6 +66,8 @@ public:
66 66
67 void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, 67 void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
68 u32 base_instance, u32 num_instances); 68 u32 base_instance, u32 num_instances);
69 void DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
70 bool subsequent);
69 71
70 void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, 72 void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
71 u32 base_instance, u32 num_instances); 73 u32 base_instance, u32 num_instances);
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp
index 8d7da50fc..dbcf508e5 100644
--- a/src/video_core/host1x/codecs/codec.cpp
+++ b/src/video_core/host1x/codecs/codec.cpp
@@ -137,16 +137,6 @@ bool Codec::CreateGpuAvDevice() {
137 break; 137 break;
138 } 138 }
139 if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { 139 if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
140#if defined(__unix__)
141 // Some linux decoding backends are reported to crash with this config method
142 // TODO(ameerj): Properly support this method
143 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) {
144 // skip zero-copy decoders, we don't currently support them
145 LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.",
146 av_hwdevice_get_type_name(type), config->methods);
147 continue;
148 }
149#endif
150 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); 140 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
151 av_codec_ctx->pix_fmt = config->pix_fmt; 141 av_codec_ctx->pix_fmt = config->pix_fmt;
152 return true; 142 return true;
diff --git a/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag
index d33131d7c..b81a54056 100644
--- a/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag
+++ b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag
@@ -3,16 +3,16 @@
3 3
4#version 450 4#version 450
5 5
6precision mediump int;
7precision highp float;
8
6layout(binding = 0) uniform sampler2D depth_tex; 9layout(binding = 0) uniform sampler2D depth_tex;
7layout(binding = 1) uniform isampler2D stencil_tex; 10layout(binding = 1) uniform usampler2D stencil_tex;
8 11
9layout(location = 0) out vec4 color; 12layout(location = 0) out vec4 color;
10 13
11void main() { 14void main() {
12 ivec2 coord = ivec2(gl_FragCoord.xy); 15 ivec2 coord = ivec2(gl_FragCoord.xy);
13 uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
14 uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
15
16 highp uint depth_val = 16 highp uint depth_val =
17 uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0)); 17 uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
18 lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r; 18 lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
diff --git a/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
index 31db7d426..6a457981d 100644
--- a/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
+++ b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
@@ -3,16 +3,16 @@
3 3
4#version 450 4#version 450
5 5
6precision mediump int;
7precision highp float;
8
6layout(binding = 0) uniform sampler2D depth_tex; 9layout(binding = 0) uniform sampler2D depth_tex;
7layout(binding = 1) uniform isampler2D stencil_tex; 10layout(binding = 1) uniform usampler2D stencil_tex;
8 11
9layout(location = 0) out vec4 color; 12layout(location = 0) out vec4 color;
10 13
11void main() { 14void main() {
12 ivec2 coord = ivec2(gl_FragCoord.xy); 15 ivec2 coord = ivec2(gl_FragCoord.xy);
13 uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
14 uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
15
16 highp uint depth_val = 16 highp uint depth_val =
17 uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0)); 17 uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
18 lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r; 18 lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 2edaafa7e..66c03bf17 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -1436,6 +1436,7 @@ void QueryCacheRuntime::Barriers(bool is_prebarrier) {
1436 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, 1436 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
1437 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, 1437 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
1438 }; 1438 };
1439 impl->scheduler.RequestOutsideRenderPassOperationContext();
1439 if (is_prebarrier) { 1440 if (is_prebarrier) {
1440 impl->scheduler.Record([](vk::CommandBuffer cmdbuf) { 1441 impl->scheduler.Record([](vk::CommandBuffer cmdbuf) {
1441 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 1442 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 83f2b6045..61d03daae 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -975,6 +975,19 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
975 if (!state_tracker.TouchScissors()) { 975 if (!state_tracker.TouchScissors()) {
976 return; 976 return;
977 } 977 }
978 if (!regs.viewport_scale_offset_enabled) {
979 const auto x = static_cast<float>(regs.surface_clip.x);
980 const auto y = static_cast<float>(regs.surface_clip.y);
981 const auto width = static_cast<float>(regs.surface_clip.width);
982 const auto height = static_cast<float>(regs.surface_clip.height);
983 VkRect2D scissor;
984 scissor.offset.x = static_cast<u32>(x);
985 scissor.offset.y = static_cast<u32>(y);
986 scissor.extent.width = static_cast<u32>(width != 0.0f ? width : 1.0f);
987 scissor.extent.height = static_cast<u32>(height != 0.0f ? height : 1.0f);
988 scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissor); });
989 return;
990 }
978 u32 up_scale = 1; 991 u32 up_scale = 1;
979 u32 down_shift = 0; 992 u32 down_shift = 0;
980 if (texture_cache.IsRescaling()) { 993 if (texture_cache.IsRescaling()) {
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
index ae9f1de64..7746a88d3 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
@@ -19,7 +19,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat
19 VkSampleCountFlagBits samples) { 19 VkSampleCountFlagBits samples) {
20 using MaxwellToVK::SurfaceFormat; 20 using MaxwellToVK::SurfaceFormat;
21 return { 21 return {
22 .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, 22 .flags = {},
23 .format = SurfaceFormat(device, FormatType::Optimal, true, format).format, 23 .format = SurfaceFormat(device, FormatType::Optimal, true, format).format,
24 .samples = samples, 24 .samples = samples,
25 .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, 25 .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp
index 6279d8e9e..2b7e0df72 100644
--- a/src/video_core/texture_cache/formatter.cpp
+++ b/src/video_core/texture_cache/formatter.cpp
@@ -10,19 +10,23 @@
10#include "video_core/texture_cache/image_info.h" 10#include "video_core/texture_cache/image_info.h"
11#include "video_core/texture_cache/image_view_base.h" 11#include "video_core/texture_cache/image_view_base.h"
12#include "video_core/texture_cache/render_targets.h" 12#include "video_core/texture_cache/render_targets.h"
13#include "video_core/texture_cache/samples_helper.h"
13 14
14namespace VideoCommon { 15namespace VideoCommon {
15 16
16std::string Name(const ImageBase& image) { 17std::string Name(const ImageBase& image) {
17 const GPUVAddr gpu_addr = image.gpu_addr; 18 const GPUVAddr gpu_addr = image.gpu_addr;
18 const ImageInfo& info = image.info; 19 const ImageInfo& info = image.info;
19 const u32 width = info.size.width; 20 u32 width = info.size.width;
20 const u32 height = info.size.height; 21 u32 height = info.size.height;
21 const u32 depth = info.size.depth; 22 const u32 depth = info.size.depth;
22 const u32 num_layers = image.info.resources.layers; 23 const u32 num_layers = image.info.resources.layers;
23 const u32 num_levels = image.info.resources.levels; 24 const u32 num_levels = image.info.resources.levels;
24 std::string resource; 25 std::string resource;
25 if (image.info.num_samples > 1) { 26 if (image.info.num_samples > 1) {
27 const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(image.info.num_samples);
28 width >>= samples_x;
29 height >>= samples_y;
26 resource += fmt::format(":{}xMSAA", image.info.num_samples); 30 resource += fmt::format(":{}xMSAA", image.info.num_samples);
27 } 31 }
28 if (num_layers > 1) { 32 if (num_layers > 1) {
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h
index 203ac1b11..2ee2f8312 100644
--- a/src/video_core/texture_cache/samples_helper.h
+++ b/src/video_core/texture_cache/samples_helper.h
@@ -24,7 +24,7 @@ namespace VideoCommon {
24 return {2, 2}; 24 return {2, 2};
25 } 25 }
26 ASSERT_MSG(false, "Invalid number of samples={}", num_samples); 26 ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
27 return {1, 1}; 27 return {0, 0};
28} 28}
29 29
30[[nodiscard]] inline int NumSamples(Tegra::Texture::MsaaMode msaa_mode) { 30[[nodiscard]] inline int NumSamples(Tegra::Texture::MsaaMode msaa_mode) {
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 8151cabf0..15596c925 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -167,6 +167,13 @@ template <u32 GOB_EXTENT>
167} 167}
168 168
169[[nodiscard]] constexpr Extent3D TileShift(const LevelInfo& info, u32 level) { 169[[nodiscard]] constexpr Extent3D TileShift(const LevelInfo& info, u32 level) {
170 if (level == 0 && info.num_levels == 1) {
171 return Extent3D{
172 .width = info.block.width,
173 .height = info.block.height,
174 .depth = info.block.depth,
175 };
176 }
170 const Extent3D blocks = NumLevelBlocks(info, level); 177 const Extent3D blocks = NumLevelBlocks(info, level);
171 return Extent3D{ 178 return Extent3D{
172 .width = AdjustTileSize(info.block.width, GOB_SIZE_X, blocks.width), 179 .width = AdjustTileSize(info.block.width, GOB_SIZE_X, blocks.width),
@@ -1293,9 +1300,9 @@ u32 MapSizeBytes(const ImageBase& image) {
1293 1300
1294static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0, 1}, 0) == 1301static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0, 1}, 0) ==
1295 0x7f8000); 1302 0x7f8000);
1296static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0, 1}, 0) == 0x4000); 1303static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0, 1}, 0) == 0x40000);
1297 1304
1298static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0, 1}, 0) == 0x4000); 1305static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0, 1}, 0) == 0x40000);
1299 1306
1300static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) == 1307static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) ==
1301 0x2afc00); 1308 0x2afc00);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 9ebece907..34208ed74 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -384,7 +384,7 @@ if (USE_DISCORD_PRESENCE)
384 discord_impl.cpp 384 discord_impl.cpp
385 discord_impl.h 385 discord_impl.h
386 ) 386 )
387 target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib) 387 target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib Qt${QT_MAJOR_VERSION}::Network)
388 target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) 388 target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE)
389endif() 389endif()
390 390
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 1de093447..d5157c502 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -128,8 +128,8 @@ const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
128 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}}, 128 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
129 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}}, 129 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
130 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, 130 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
131 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}}, 131 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}},
132 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}}, 132 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}},
133 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, 133 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
134 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, 134 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
135 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, 135 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index e8f9ebfd8..5a48e388b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -115,17 +115,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
115 for (std::size_t i = 0; i < player_tabs.size(); ++i) { 115 for (std::size_t i = 0; i < player_tabs.size(); ++i) {
116 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); 116 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
117 player_tabs[i]->layout()->addWidget(player_controllers[i]); 117 player_tabs[i]->layout()->addWidget(player_controllers[i]);
118 connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) { 118 connect(player_connected[i], &QCheckBox::clicked, [this, i](int checked) {
119 // Ensures that the controllers are always connected in sequential order 119 // Ensures that the controllers are always connected in sequential order
120 if (is_connected) { 120 this->propagateMouseClickOnPlayers(i, checked, true);
121 for (std::size_t index = 0; index <= i; ++index) {
122 player_connected[index]->setChecked(is_connected);
123 }
124 } else {
125 for (std::size_t index = i; index < player_tabs.size(); ++index) {
126 player_connected[index]->setChecked(is_connected);
127 }
128 }
129 }); 121 });
130 connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, 122 connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this,
131 &ConfigureInput::UpdateAllInputDevices); 123 &ConfigureInput::UpdateAllInputDevices);
@@ -183,6 +175,30 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
183 LoadConfiguration(); 175 LoadConfiguration();
184} 176}
185 177
178void ConfigureInput::propagateMouseClickOnPlayers(size_t player_index, bool checked, bool origin) {
179 // Origin has already been toggled
180 if (!origin) {
181 player_connected[player_index]->setChecked(checked);
182 }
183
184 if (checked) {
185 // Check all previous buttons when checked
186 if (player_index > 0) {
187 propagateMouseClickOnPlayers(player_index - 1, checked, false);
188 }
189 } else {
190 // Unchecked all following buttons when unchecked
191 if (player_index < player_tabs.size() - 1) {
192 // Reconnect current player if it was the last one checked
193 // (player number was reduced by more than one)
194 if (origin && player_connected[player_index + 1]->checkState() == Qt::Checked) {
195 player_connected[player_index]->setCheckState(Qt::Checked);
196 }
197 propagateMouseClickOnPlayers(player_index + 1, checked, false);
198 }
199 }
200}
201
186QList<QWidget*> ConfigureInput::GetSubTabs() const { 202QList<QWidget*> ConfigureInput::GetSubTabs() const {
187 return { 203 return {
188 ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5, 204 ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index c89189c36..abb7f7089 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -56,6 +56,7 @@ private:
56 void UpdateDockedState(bool is_handheld); 56 void UpdateDockedState(bool is_handheld);
57 void UpdateAllInputDevices(); 57 void UpdateAllInputDevices();
58 void UpdateAllInputProfiles(std::size_t player_index); 58 void UpdateAllInputProfiles(std::size_t player_index);
59 void propagateMouseClickOnPlayers(size_t player_index, bool origin, bool checked);
59 60
60 /// Load configuration settings. 61 /// Load configuration settings.
61 void LoadConfiguration(); 62 void LoadConfiguration();
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index a4e8af1b4..3fe448f27 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -157,6 +157,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
157 INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); 157 INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", "");
158 INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); 158 INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", "");
159 INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); 159 INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", "");
160 INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", "");
160 INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); 161 INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", "");
161 INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); 162 INSERT(UISettings, controller_applet_disabled, "Disable controller applet", "");
162 163
@@ -383,6 +384,13 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
383 translations->insert( 384 translations->insert(
384 {Settings::EnumMetadata<Settings::ConsoleMode>::Index(), 385 {Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
385 {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); 386 {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
387 translations->insert(
388 {Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
389 {
390 PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"),
391 PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"),
392 PAIR(ConfirmStop, Ask_Never, "Never ask"),
393 }});
386 394
387#undef PAIR 395#undef PAIR
388#undef CTX_PAIR 396#undef CTX_PAIR
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 1a6b63856..1431cf2fe 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -211,7 +211,7 @@ void GMainWindow::ShowTelemetryCallout() {
211 tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous " 211 tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous "
212 "data is collected</a> to help improve yuzu. " 212 "data is collected</a> to help improve yuzu. "
213 "<br/><br/>Would you like to share your usage data with us?"); 213 "<br/><br/>Would you like to share your usage data with us?");
214 if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) { 214 if (!question(this, tr("Telemetry"), telemetry_message)) {
215 Settings::values.enable_telemetry = false; 215 Settings::values.enable_telemetry = false;
216 system->ApplySettings(); 216 system->ApplySettings();
217 } 217 }
@@ -2420,9 +2420,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
2420 } 2420 }
2421 }(); 2421 }();
2422 2422
2423 if (QMessageBox::question(this, tr("Remove Entry"), entry_question, 2423 if (!question(this, tr("Remove Entry"), entry_question, QMessageBox::Yes | QMessageBox::No,
2424 QMessageBox::Yes | QMessageBox::No, 2424 QMessageBox::No)) {
2425 QMessageBox::No) != QMessageBox::Yes) {
2426 return; 2425 return;
2427 } 2426 }
2428 2427
@@ -2521,8 +2520,8 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
2521 } 2520 }
2522 }(); 2521 }();
2523 2522
2524 if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No, 2523 if (!GMainWindow::question(this, tr("Remove File"), question,
2525 QMessageBox::No) != QMessageBox::Yes) { 2524 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) {
2526 return; 2525 return;
2527 } 2526 }
2528 2527
@@ -3409,10 +3408,13 @@ void GMainWindow::OnRestartGame() {
3409 if (!system->IsPoweredOn()) { 3408 if (!system->IsPoweredOn()) {
3410 return; 3409 return;
3411 } 3410 }
3412 // Make a copy since ShutdownGame edits game_path 3411
3413 const auto current_game = QString(current_game_path); 3412 if (ConfirmShutdownGame()) {
3414 ShutdownGame(); 3413 // Make a copy since ShutdownGame edits game_path
3415 BootGame(current_game); 3414 const auto current_game = QString(current_game_path);
3415 ShutdownGame();
3416 BootGame(current_game);
3417 }
3416} 3418}
3417 3419
3418void GMainWindow::OnPauseGame() { 3420void GMainWindow::OnPauseGame() {
@@ -3434,18 +3436,39 @@ void GMainWindow::OnPauseContinueGame() {
3434} 3436}
3435 3437
3436void GMainWindow::OnStopGame() { 3438void GMainWindow::OnStopGame() {
3437 if (system->GetExitLocked() && !ConfirmForceLockedExit()) { 3439 if (ConfirmShutdownGame()) {
3438 return; 3440 play_time_manager->Stop();
3441 // Update game list to show new play time
3442 game_list->PopulateAsync(UISettings::values.game_dirs);
3443 if (OnShutdownBegin()) {
3444 OnShutdownBeginDialog();
3445 } else {
3446 OnEmulationStopped();
3447 }
3439 } 3448 }
3449}
3440 3450
3441 play_time_manager->Stop(); 3451bool GMainWindow::ConfirmShutdownGame() {
3442 // Update game list to show new play time 3452 if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Always) {
3443 game_list->PopulateAsync(UISettings::values.game_dirs); 3453 if (system->GetExitLocked()) {
3444 if (OnShutdownBegin()) { 3454 if (!ConfirmForceLockedExit()) {
3445 OnShutdownBeginDialog(); 3455 return false;
3456 }
3457 } else {
3458 if (!ConfirmChangeGame()) {
3459 return false;
3460 }
3461 }
3446 } else { 3462 } else {
3447 OnEmulationStopped(); 3463 if (UISettings::values.confirm_before_stopping.GetValue() ==
3464 ConfirmStop::Ask_Based_On_Game &&
3465 system->GetExitLocked()) {
3466 if (!ConfirmForceLockedExit()) {
3467 return false;
3468 }
3469 }
3448 } 3470 }
3471 return true;
3449} 3472}
3450 3473
3451void GMainWindow::OnLoadComplete() { 3474void GMainWindow::OnLoadComplete() {
@@ -3825,22 +3848,11 @@ void GMainWindow::OnTasRecord() {
3825 const bool is_recording = input_subsystem->GetTas()->Record(); 3848 const bool is_recording = input_subsystem->GetTas()->Record();
3826 if (!is_recording) { 3849 if (!is_recording) {
3827 is_tas_recording_dialog_active = true; 3850 is_tas_recording_dialog_active = true;
3828 ControllerNavigation* controller_navigation = 3851
3829 new ControllerNavigation(system->HIDCore(), this); 3852 bool answer = question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"),
3830 // Use QMessageBox instead of question so we can link controller navigation 3853 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
3831 QMessageBox* box_dialog = new QMessageBox(); 3854
3832 box_dialog->setWindowTitle(tr("TAS Recording")); 3855 input_subsystem->GetTas()->SaveRecording(answer);
3833 box_dialog->setText(tr("Overwrite file of player 1?"));
3834 box_dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
3835 box_dialog->setDefaultButton(QMessageBox::Yes);
3836 connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
3837 [box_dialog](Qt::Key key) {
3838 QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
3839 QCoreApplication::postEvent(box_dialog, event);
3840 });
3841 int res = box_dialog->exec();
3842 controller_navigation->UnloadController();
3843 input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
3844 is_tas_recording_dialog_active = false; 3856 is_tas_recording_dialog_active = false;
3845 } 3857 }
3846 OnTasStateChanged(); 3858 OnTasStateChanged();
@@ -4081,6 +4093,29 @@ void GMainWindow::OnLoadAmiibo() {
4081 LoadAmiibo(filename); 4093 LoadAmiibo(filename);
4082} 4094}
4083 4095
4096bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text,
4097 QMessageBox::StandardButtons buttons,
4098 QMessageBox::StandardButton defaultButton) {
4099
4100 QMessageBox* box_dialog = new QMessageBox(parent);
4101 box_dialog->setWindowTitle(title);
4102 box_dialog->setText(text);
4103 box_dialog->setStandardButtons(buttons);
4104 box_dialog->setDefaultButton(defaultButton);
4105
4106 ControllerNavigation* controller_navigation =
4107 new ControllerNavigation(system->HIDCore(), box_dialog);
4108 connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
4109 [box_dialog](Qt::Key key) {
4110 QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
4111 QCoreApplication::postEvent(box_dialog, event);
4112 });
4113 int res = box_dialog->exec();
4114
4115 controller_navigation->UnloadController();
4116 return res == QMessageBox::Yes;
4117}
4118
4084void GMainWindow::LoadAmiibo(const QString& filename) { 4119void GMainWindow::LoadAmiibo(const QString& filename) {
4085 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); 4120 auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
4086 const QString title = tr("Error loading Amiibo data"); 4121 const QString title = tr("Error loading Amiibo data");
@@ -4814,8 +4849,7 @@ bool GMainWindow::ConfirmClose() {
4814 return true; 4849 return true;
4815 } 4850 }
4816 const auto text = tr("Are you sure you want to close yuzu?"); 4851 const auto text = tr("Are you sure you want to close yuzu?");
4817 const auto answer = QMessageBox::question(this, tr("yuzu"), text); 4852 return question(this, tr("yuzu"), text);
4818 return answer != QMessageBox::No;
4819} 4853}
4820 4854
4821void GMainWindow::closeEvent(QCloseEvent* event) { 4855void GMainWindow::closeEvent(QCloseEvent* event) {
@@ -4908,11 +4942,11 @@ bool GMainWindow::ConfirmChangeGame() {
4908 if (emu_thread == nullptr) 4942 if (emu_thread == nullptr)
4909 return true; 4943 return true;
4910 4944
4911 const auto answer = QMessageBox::question( 4945 // Use custom question to link controller navigation
4946 return question(
4912 this, tr("yuzu"), 4947 this, tr("yuzu"),
4913 tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), 4948 tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
4914 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 4949 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
4915 return answer != QMessageBox::No;
4916} 4950}
4917 4951
4918bool GMainWindow::ConfirmForceLockedExit() { 4952bool GMainWindow::ConfirmForceLockedExit() {
@@ -4922,8 +4956,7 @@ bool GMainWindow::ConfirmForceLockedExit() {
4922 const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" 4956 const auto text = tr("The currently running application has requested yuzu to not exit.\n\n"
4923 "Would you like to bypass this and exit anyway?"); 4957 "Would you like to bypass this and exit anyway?");
4924 4958
4925 const auto answer = QMessageBox::question(this, tr("yuzu"), text); 4959 return question(this, tr("yuzu"), text);
4926 return answer != QMessageBox::No;
4927} 4960}
4928 4961
4929void GMainWindow::RequestGameExit() { 4962void GMainWindow::RequestGameExit() {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 2346eb3bd..270a40c5f 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -7,6 +7,7 @@
7#include <optional> 7#include <optional>
8 8
9#include <QMainWindow> 9#include <QMainWindow>
10#include <QMessageBox>
10#include <QTimer> 11#include <QTimer>
11#include <QTranslator> 12#include <QTranslator>
12 13
@@ -15,6 +16,7 @@
15#include "input_common/drivers/tas_input.h" 16#include "input_common/drivers/tas_input.h"
16#include "yuzu/compatibility_list.h" 17#include "yuzu/compatibility_list.h"
17#include "yuzu/hotkeys.h" 18#include "yuzu/hotkeys.h"
19#include "yuzu/util/controller_navigation.h"
18 20
19#ifdef __unix__ 21#ifdef __unix__
20#include <QVariant> 22#include <QVariant>
@@ -424,6 +426,11 @@ private:
424 bool CheckSystemArchiveDecryption(); 426 bool CheckSystemArchiveDecryption();
425 bool CheckFirmwarePresence(); 427 bool CheckFirmwarePresence();
426 void ConfigureFilesystemProvider(const std::string& filepath); 428 void ConfigureFilesystemProvider(const std::string& filepath);
429 /**
430 * Open (or not) the right confirm dialog based on current setting and game exit lock
431 * @returns true if the player confirmed or the settings do no require it
432 */
433 bool ConfirmShutdownGame();
427 434
428 QString GetTasStateDescription() const; 435 QString GetTasStateDescription() const;
429 bool CreateShortcut(const std::string& shortcut_path, const std::string& title, 436 bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
@@ -431,6 +438,17 @@ private:
431 const std::string& command, const std::string& arguments, 438 const std::string& command, const std::string& arguments,
432 const std::string& categories, const std::string& keywords); 439 const std::string& categories, const std::string& keywords);
433 440
441 /**
442 * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
443 * The only difference is that it returns a boolean.
444 *
445 * @returns true if buttons contains QMessageBox::Yes and the user clicks on the "Yes" button.
446 */
447 bool question(QWidget* parent, const QString& title, const QString& text,
448 QMessageBox::StandardButtons buttons =
449 QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
450 QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
451
434 std::unique_ptr<Ui::MainWindow> ui; 452 std::unique_ptr<Ui::MainWindow> ui;
435 453
436 std::unique_ptr<Core::System> system; 454 std::unique_ptr<Core::System> system;
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 975008159..b62ff620c 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -16,7 +16,9 @@
16#include "common/settings_enums.h" 16#include "common/settings_enums.h"
17 17
18using Settings::Category; 18using Settings::Category;
19using Settings::ConfirmStop;
19using Settings::Setting; 20using Settings::Setting;
21using Settings::SwitchableSetting;
20 22
21#ifndef CANNOT_EXPLICITLY_INSTANTIATE 23#ifndef CANNOT_EXPLICITLY_INSTANTIATE
22namespace Settings { 24namespace Settings {
@@ -94,6 +96,15 @@ struct Values {
94 Setting<bool> confirm_before_closing{ 96 Setting<bool> confirm_before_closing{
95 linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default, 97 linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default,
96 true, true}; 98 true, true};
99
100 SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage,
101 ConfirmStop::Ask_Always,
102 "confirmStop",
103 Category::UiGeneral,
104 Settings::Specialization::Default,
105 true,
106 true};
107
97 Setting<bool> first_start{linkage, true, "firstStart", Category::Ui}; 108 Setting<bool> first_start{linkage, true, "firstStart", Category::Ui};
98 Setting<bool> pause_when_in_background{linkage, 109 Setting<bool> pause_when_in_background{linkage,
99 false, 110 false,