summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt19
-rw-r--r--src/android/app/build.gradle.kts2
-rw-r--r--src/android/app/src/main/AndroidManifest.xml2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt35
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt48
-rw-r--r--src/android/app/src/main/jni/config.cpp5
-rw-r--r--src/android/app/src/main/jni/native.cpp46
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp47
-rw-r--r--src/audio_core/sink/cubeb_sink.h7
-rw-r--r--src/audio_core/sink/sdl2_sink.cpp40
-rw-r--r--src/audio_core/sink/sdl2_sink.h7
-rw-r--r--src/audio_core/sink/sink_details.cpp45
-rw-r--r--src/common/settings.cpp5
-rw-r--r--src/common/settings.h10
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/common/swap.h5
-rw-r--r--src/core/file_sys/registered_cache.cpp37
-rw-r--r--src/core/frontend/applets/controller.cpp4
-rw-r--r--src/core/frontend/framebuffer_layout.cpp3
-rw-r--r--src/core/hle/kernel/k_process.cpp25
-rw-r--r--src/core/hle/kernel/k_process.h7
-rw-r--r--src/core/hle/service/am/am.cpp5
-rw-r--r--src/core/hle/service/apm/apm_controller.cpp4
-rw-r--r--src/core/hle/service/audio/hwopus.cpp49
-rw-r--r--src/core/hle/service/audio/hwopus.h5
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp4
-rw-r--r--src/core/hle/service/service.h4
-rw-r--r--src/core/hle/service/sockets/bsd.cpp5
-rw-r--r--src/core/hle/service/sockets/bsd.h3
-rw-r--r--src/core/hle/service/sockets/nsd.cpp11
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp12
-rw-r--r--src/core/hle/service/ssl/ssl.cpp10
-rw-r--r--src/core/hle/service/ssl/ssl_backend_openssl.cpp3
-rw-r--r--src/core/hle/service/ssl/ssl_backend_schannel.cpp25
-rw-r--r--src/core/hle/service/vi/vi.cpp2
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp2
-rw-r--r--src/core/loader/kip.cpp2
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/reporter.cpp4
-rw-r--r--src/core/telemetry_session.cpp3
-rw-r--r--src/input_common/input_poller.cpp10
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h10
-rw-r--r--src/video_core/host1x/codecs/codec.cpp3
-rw-r--r--src/video_core/macro/macro.cpp24
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp7
-rw-r--r--src/web_service/verify_user_jwt.cpp1
-rw-r--r--src/yuzu/applets/qt_controller.cpp14
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp9
-rw-r--r--src/yuzu/configuration/config.h3
-rw-r--r--src/yuzu/configuration/configure_input.cpp14
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp7
-rw-r--r--src/yuzu/configuration/configure_system.cpp5
-rw-r--r--src/yuzu/configuration/shared_translation.cpp5
-rw-r--r--src/yuzu/configuration/shared_widget.cpp69
-rw-r--r--src/yuzu/configuration/shared_widget.h6
-rw-r--r--src/yuzu/game_list_worker.cpp23
-rw-r--r--src/yuzu/main.cpp55
-rw-r--r--src/yuzu/main.h1
66 files changed, 649 insertions, 225 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7bb88c8ea..6068c7a1f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -114,16 +114,19 @@ else()
114 -Wno-attributes 114 -Wno-attributes
115 -Wno-invalid-offsetof 115 -Wno-invalid-offsetof
116 -Wno-unused-parameter 116 -Wno-unused-parameter
117
118 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
119 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
120 $<$<CXX_COMPILER_ID:Clang>:-Werror=shadow-uncaptured-local>
121 $<$<CXX_COMPILER_ID:Clang>:-Werror=implicit-fallthrough>
122 $<$<CXX_COMPILER_ID:Clang>:-Werror=type-limits>
123 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init>
124 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field>
125 ) 117 )
126 118
119 if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang
120 add_compile_options(
121 -Wno-braced-scalar-init
122 -Wno-unused-private-field
123 -Wno-nullability-completeness
124 -Werror=shadow-uncaptured-local
125 -Werror=implicit-fallthrough
126 -Werror=type-limits
127 )
128 endif()
129
127 if (ARCHITECTURE_x86_64) 130 if (ARCHITECTURE_x86_64)
128 add_compile_options("-mcx16") 131 add_compile_options("-mcx16")
129 add_compile_options("-fwrapv") 132 add_compile_options("-fwrapv")
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index a8db70511..fe79a701c 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -95,6 +95,7 @@ android {
95 // builds a release build that doesn't need signing 95 // builds a release build that doesn't need signing
96 // Attaches 'debug' suffix to version and package name, allowing installation alongside the release build. 96 // Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
97 register("relWithDebInfo") { 97 register("relWithDebInfo") {
98 isDefault = true
98 resValue("string", "app_name_suffixed", "yuzu Debug Release") 99 resValue("string", "app_name_suffixed", "yuzu Debug Release")
99 signingConfig = signingConfigs.getByName("debug") 100 signingConfig = signingConfigs.getByName("debug")
100 isMinifyEnabled = true 101 isMinifyEnabled = true
@@ -122,6 +123,7 @@ android {
122 flavorDimensions.add("version") 123 flavorDimensions.add("version")
123 productFlavors { 124 productFlavors {
124 create("mainline") { 125 create("mainline") {
126 isDefault = true
125 dimension = "version" 127 dimension = "version"
126 buildConfigField("Boolean", "PREMIUM", "false") 128 buildConfigField("Boolean", "PREMIUM", "false")
127 } 129 }
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 6184f3eb6..933244140 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -25,6 +25,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
25 android:hasFragileUserData="false" 25 android:hasFragileUserData="false"
26 android:supportsRtl="true" 26 android:supportsRtl="true"
27 android:isGame="true" 27 android:isGame="true"
28 android:appCategory="game"
28 android:localeConfig="@xml/locales_config" 29 android:localeConfig="@xml/locales_config"
29 android:banner="@drawable/tv_banner" 30 android:banner="@drawable/tv_banner"
30 android:extractNativeLibs="true" 31 android:extractNativeLibs="true"
@@ -55,7 +56,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
55 android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" 56 android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
56 android:theme="@style/Theme.Yuzu.Main" 57 android:theme="@style/Theme.Yuzu.Main"
57 android:launchMode="singleTop" 58 android:launchMode="singleTop"
58 android:screenOrientation="userLandscape"
59 android:supportsPictureInPicture="true" 59 android:supportsPictureInPicture="true"
60 android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" 60 android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
61 android:exported="true"> 61 android:exported="true">
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index a5af5a7ae..e6fffc832 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -11,7 +11,6 @@ import android.view.View
11import android.view.ViewGroup.MarginLayoutParams 11import android.view.ViewGroup.MarginLayoutParams
12import android.widget.Toast 12import android.widget.Toast
13import androidx.activity.OnBackPressedCallback 13import androidx.activity.OnBackPressedCallback
14import androidx.activity.result.ActivityResultLauncher
15import androidx.activity.viewModels 14import androidx.activity.viewModels
16import androidx.appcompat.app.AppCompatActivity 15import androidx.appcompat.app.AppCompatActivity
17import androidx.core.view.ViewCompat 16import androidx.core.view.ViewCompat
@@ -246,17 +245,5 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
246 settings.putExtra(ARG_GAME_ID, gameId) 245 settings.putExtra(ARG_GAME_ID, gameId)
247 context.startActivity(settings) 246 context.startActivity(settings)
248 } 247 }
249
250 fun launch(
251 context: Context,
252 launcher: ActivityResultLauncher<Intent>,
253 menuTag: String?,
254 gameId: String?
255 ) {
256 val settings = Intent(context, SettingsActivity::class.java)
257 settings.putExtra(ARG_MENU_TAG, menuTag)
258 settings.putExtra(ARG_GAME_ID, gameId)
259 launcher.launch(settings)
260 }
261 } 248 }
262} 249}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 25b9d4018..09e93a017 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -7,7 +7,6 @@ import android.annotation.SuppressLint
7import android.app.AlertDialog 7import android.app.AlertDialog
8import android.content.Context 8import android.content.Context
9import android.content.DialogInterface 9import android.content.DialogInterface
10import android.content.Intent
11import android.content.SharedPreferences 10import android.content.SharedPreferences
12import android.content.pm.ActivityInfo 11import android.content.pm.ActivityInfo
13import android.content.res.Configuration 12import android.content.res.Configuration
@@ -19,8 +18,6 @@ import android.util.Rational
19import android.view.* 18import android.view.*
20import android.widget.TextView 19import android.widget.TextView
21import androidx.activity.OnBackPressedCallback 20import androidx.activity.OnBackPressedCallback
22import androidx.activity.result.ActivityResultLauncher
23import androidx.activity.result.contract.ActivityResultContracts
24import androidx.appcompat.widget.PopupMenu 21import androidx.appcompat.widget.PopupMenu
25import androidx.core.content.res.ResourcesCompat 22import androidx.core.content.res.ResourcesCompat
26import androidx.core.graphics.Insets 23import androidx.core.graphics.Insets
@@ -66,8 +63,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
66 63
67 private var isInFoldableLayout = false 64 private var isInFoldableLayout = false
68 65
69 private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent>
70
71 override fun onAttach(context: Context) { 66 override fun onAttach(context: Context) {
72 super.onAttach(context) 67 super.onAttach(context)
73 if (context is EmulationActivity) { 68 if (context is EmulationActivity) {
@@ -81,11 +76,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
81 .collect { updateFoldableLayout(context, it) } 76 .collect { updateFoldableLayout(context, it) }
82 } 77 }
83 } 78 }
84
85 onReturnFromSettings = context.activityResultRegistry.register(
86 "SettingsResult",
87 ActivityResultContracts.StartActivityForResult()
88 ) { updateScreenLayout() }
89 } else { 79 } else {
90 throw IllegalStateException("EmulationFragment must have EmulationActivity parent") 80 throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
91 } 81 }
@@ -149,12 +139,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
149 } 139 }
150 140
151 R.id.menu_settings -> { 141 R.id.menu_settings -> {
152 SettingsActivity.launch( 142 SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "")
153 requireContext(),
154 onReturnFromSettings,
155 SettingsFile.FILE_NAME_CONFIG,
156 ""
157 )
158 true 143 true
159 } 144 }
160 145
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index f8e7eeca7..f71d0a098 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -11,6 +11,7 @@ import kotlinx.serialization.json.Json
11import org.yuzu.yuzu_emu.NativeLibrary 11import org.yuzu.yuzu_emu.NativeLibrary
12import org.yuzu.yuzu_emu.YuzuApplication 12import org.yuzu.yuzu_emu.YuzuApplication
13import org.yuzu.yuzu_emu.model.Game 13import org.yuzu.yuzu_emu.model.Game
14import org.yuzu.yuzu_emu.model.MinimalDocumentFile
14 15
15object GameHelper { 16object GameHelper {
16 const val KEY_GAME_PATH = "game_path" 17 const val KEY_GAME_PATH = "game_path"
@@ -29,15 +30,7 @@ object GameHelper {
29 // Ensure keys are loaded so that ROM metadata can be decrypted. 30 // Ensure keys are loaded so that ROM metadata can be decrypted.
30 NativeLibrary.reloadKeys() 31 NativeLibrary.reloadKeys()
31 32
32 val children = FileUtil.listFiles(context, gamesUri) 33 addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3)
33 for (file in children) {
34 if (!file.isDirectory) {
35 // Check that the file has an extension we care about before trying to read out of it.
36 if (Game.extensions.contains(FileUtil.getExtension(file.uri))) {
37 games.add(getGame(file.uri))
38 }
39 }
40 }
41 34
42 // Cache list of games found on disk 35 // Cache list of games found on disk
43 val serializedGames = mutableSetOf<String>() 36 val serializedGames = mutableSetOf<String>()
@@ -52,6 +45,30 @@ object GameHelper {
52 return games.toList() 45 return games.toList()
53 } 46 }
54 47
48 private fun addGamesRecursive(
49 games: MutableList<Game>,
50 files: Array<MinimalDocumentFile>,
51 depth: Int
52 ) {
53 if (depth <= 0) {
54 return
55 }
56
57 files.forEach {
58 if (it.isDirectory) {
59 addGamesRecursive(
60 games,
61 FileUtil.listFiles(YuzuApplication.appContext, it.uri),
62 depth - 1
63 )
64 } else {
65 if (Game.extensions.contains(FileUtil.getExtension(it.uri))) {
66 games.add(getGame(it.uri))
67 }
68 }
69 }
70 }
71
55 private fun getGame(uri: Uri): Game { 72 private fun getGame(uri: Uri): Game {
56 val filePath = uri.toString() 73 val filePath = uri.toString()
57 var name = NativeLibrary.getTitle(filePath) 74 var name = NativeLibrary.getTitle(filePath)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
index 685ccaa76..2f0868c63 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
@@ -7,7 +7,6 @@ import android.content.Context
7import android.util.AttributeSet 7import android.util.AttributeSet
8import android.util.Rational 8import android.util.Rational
9import android.view.SurfaceView 9import android.view.SurfaceView
10import kotlin.math.roundToInt
11 10
12class FixedRatioSurfaceView @JvmOverloads constructor( 11class FixedRatioSurfaceView @JvmOverloads constructor(
13 context: Context, 12 context: Context,
@@ -22,27 +21,44 @@ class FixedRatioSurfaceView @JvmOverloads constructor(
22 */ 21 */
23 fun setAspectRatio(ratio: Rational?) { 22 fun setAspectRatio(ratio: Rational?) {
24 aspectRatio = ratio?.toFloat() ?: 0f 23 aspectRatio = ratio?.toFloat() ?: 0f
24 requestLayout()
25 } 25 }
26 26
27 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 27 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
28 super.onMeasure(widthMeasureSpec, heightMeasureSpec) 28 val displayWidth: Float = MeasureSpec.getSize(widthMeasureSpec).toFloat()
29 val width = MeasureSpec.getSize(widthMeasureSpec) 29 val displayHeight: Float = MeasureSpec.getSize(heightMeasureSpec).toFloat()
30 val height = MeasureSpec.getSize(heightMeasureSpec)
31 if (aspectRatio != 0f) { 30 if (aspectRatio != 0f) {
32 val newWidth: Int 31 val displayAspect = displayWidth / displayHeight
33 val newHeight: Int 32 if (displayAspect < aspectRatio) {
34 if (height * aspectRatio < width) { 33 // Max out width
35 newWidth = (height * aspectRatio).roundToInt() 34 val halfHeight = displayHeight / 2
36 newHeight = height 35 val surfaceHeight = displayWidth / aspectRatio
36 val newTop: Float = halfHeight - (surfaceHeight / 2)
37 val newBottom: Float = halfHeight + (surfaceHeight / 2)
38 super.onMeasure(
39 widthMeasureSpec,
40 MeasureSpec.makeMeasureSpec(
41 newBottom.toInt() - newTop.toInt(),
42 MeasureSpec.EXACTLY
43 )
44 )
45 return
37 } else { 46 } else {
38 newWidth = width 47 // Max out height
39 newHeight = (width / aspectRatio).roundToInt() 48 val halfWidth = displayWidth / 2
49 val surfaceWidth = displayHeight * aspectRatio
50 val newLeft: Float = halfWidth - (surfaceWidth / 2)
51 val newRight: Float = halfWidth + (surfaceWidth / 2)
52 super.onMeasure(
53 MeasureSpec.makeMeasureSpec(
54 newRight.toInt() - newLeft.toInt(),
55 MeasureSpec.EXACTLY
56 ),
57 heightMeasureSpec
58 )
59 return
40 } 60 }
41 val left = (width - newWidth) / 2
42 val top = (height - newHeight) / 2
43 setLeftTopRightBottom(left, top, left + newWidth, top + newHeight)
44 } else {
45 setLeftTopRightBottom(0, 0, width, height)
46 } 61 }
62 super.onMeasure(widthMeasureSpec, heightMeasureSpec)
47 } 63 }
48} 64}
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 5e1f10f99..9de9bd93e 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -11,6 +11,7 @@
11#include "common/fs/path_util.h" 11#include "common/fs/path_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/settings.h" 13#include "common/settings.h"
14#include "common/settings_enums.h"
14#include "core/hle/service/acc/profile_manager.h" 15#include "core/hle/service/acc/profile_manager.h"
15#include "input_common/main.h" 16#include "input_common/main.h"
16#include "jni/config.h" 17#include "jni/config.h"
@@ -144,7 +145,9 @@ void Config::ReadValues() {
144 Service::Account::MAX_USERS - 1); 145 Service::Account::MAX_USERS - 1);
145 146
146 // Disable docked mode by default on Android 147 // Disable docked mode by default on Android
147 Settings::values.use_docked_mode = config->GetBoolean("System", "use_docked_mode", false); 148 Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
149 ? Settings::ConsoleMode::Docked
150 : Settings::ConsoleMode::Handheld);
148 151
149 const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false); 152 const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
150 if (rng_seed_enabled) { 153 if (rng_seed_enabled) {
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index c23b2f19e..7e17833a0 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -30,6 +30,7 @@
30#include "core/cpu_manager.h" 30#include "core/cpu_manager.h"
31#include "core/crypto/key_manager.h" 31#include "core/crypto/key_manager.h"
32#include "core/file_sys/card_image.h" 32#include "core/file_sys/card_image.h"
33#include "core/file_sys/content_archive.h"
33#include "core/file_sys/registered_cache.h" 34#include "core/file_sys/registered_cache.h"
34#include "core/file_sys/submission_package.h" 35#include "core/file_sys/submission_package.h"
35#include "core/file_sys/vfs.h" 36#include "core/file_sys/vfs.h"
@@ -224,6 +225,42 @@ public:
224 m_system.Renderer().NotifySurfaceChanged(); 225 m_system.Renderer().NotifySurfaceChanged();
225 } 226 }
226 227
228 void ConfigureFilesystemProvider(const std::string& filepath) {
229 const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
230 if (!file) {
231 return;
232 }
233
234 auto loader = Loader::GetLoader(m_system, file);
235 if (!loader) {
236 return;
237 }
238
239 const auto file_type = loader->GetFileType();
240 if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
241 return;
242 }
243
244 u64 program_id = 0;
245 const auto res2 = loader->ReadProgramId(program_id);
246 if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
247 m_manual_provider->AddEntry(FileSys::TitleType::Application,
248 FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
249 program_id, file);
250 } else if (res2 == Loader::ResultStatus::Success &&
251 (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
252 const auto nsp = file_type == Loader::FileType::NSP
253 ? std::make_shared<FileSys::NSP>(file)
254 : FileSys::XCI{file}.GetSecurePartitionNSP();
255 for (const auto& title : nsp->GetNCAs()) {
256 for (const auto& entry : title.second) {
257 m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
258 entry.second->GetBaseFile());
259 }
260 }
261 }
262 }
263
227 Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { 264 Core::SystemResultStatus InitializeEmulation(const std::string& filepath) {
228 std::scoped_lock lock(m_mutex); 265 std::scoped_lock lock(m_mutex);
229 266
@@ -254,8 +291,14 @@ public:
254 std::move(android_keyboard), // Software Keyboard 291 std::move(android_keyboard), // Software Keyboard
255 nullptr, // Web Browser 292 nullptr, // Web Browser
256 }); 293 });
294
295 // Initialize filesystem.
296 m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
257 m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 297 m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
298 m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
299 m_manual_provider.get());
258 m_system.GetFileSystemController().CreateFactories(*m_vfs); 300 m_system.GetFileSystemController().CreateFactories(*m_vfs);
301 ConfigureFilesystemProvider(filepath);
259 302
260 // Initialize account manager 303 // Initialize account manager
261 m_profile_manager = std::make_unique<Service::Account::ProfileManager>(); 304 m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
@@ -377,7 +420,7 @@ public:
377 return false; 420 return false;
378 } 421 }
379 422
380 return !Settings::values.use_docked_mode.GetValue(); 423 return !Settings::IsDockedMode();
381 } 424 }
382 425
383 void SetDeviceType([[maybe_unused]] int index, int type) { 426 void SetDeviceType([[maybe_unused]] int index, int type) {
@@ -489,6 +532,7 @@ private:
489 bool m_is_paused{}; 532 bool m_is_paused{};
490 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; 533 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
491 std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; 534 std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
535 std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
492 536
493 // GPU driver parameters 537 // GPU driver parameters
494 std::shared_ptr<Common::DynamicLibrary> m_vulkan_library; 538 std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index 9a0801888..bbb598bc5 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -8,6 +8,7 @@
8#include "audio_core/sink/cubeb_sink.h" 8#include "audio_core/sink/cubeb_sink.h"
9#include "audio_core/sink/sink_stream.h" 9#include "audio_core/sink/sink_stream.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/scope_exit.h"
11#include "core/core.h" 12#include "core/core.h"
12 13
13#ifdef _WIN32 14#ifdef _WIN32
@@ -332,25 +333,38 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
332 return device_list; 333 return device_list;
333} 334}
334 335
335u32 GetCubebLatency() { 336namespace {
336 cubeb* ctx; 337static long TmpDataCallback(cubeb_stream*, void*, const void*, void*, long) {
338 return TargetSampleCount;
339}
340static void TmpStateCallback(cubeb_stream*, void*, cubeb_state) {}
341} // namespace
342
343bool IsCubebSuitable() {
344#if !defined(HAVE_CUBEB)
345 return false;
346#else
347 cubeb* ctx{nullptr};
337 348
338#ifdef _WIN32 349#ifdef _WIN32
339 auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); 350 auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
340#endif 351#endif
341 352
353 // Init cubeb
342 if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { 354 if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
343 LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); 355 LOG_ERROR(Audio_Sink, "Cubeb failed to init, it is not suitable.");
344 // Return a large latency so we choose SDL instead. 356 return false;
345 return 10000u;
346 } 357 }
347 358
359 SCOPE_EXIT({ cubeb_destroy(ctx); });
360
348#ifdef _WIN32 361#ifdef _WIN32
349 if (SUCCEEDED(com_init_result)) { 362 if (SUCCEEDED(com_init_result)) {
350 CoUninitialize(); 363 CoUninitialize();
351 } 364 }
352#endif 365#endif
353 366
367 // Get min latency
354 cubeb_stream_params params{}; 368 cubeb_stream_params params{};
355 params.rate = TargetSampleRate; 369 params.rate = TargetSampleRate;
356 params.channels = 2; 370 params.channels = 2;
@@ -361,12 +375,27 @@ u32 GetCubebLatency() {
361 u32 latency{0}; 375 u32 latency{0};
362 const auto latency_error = cubeb_get_min_latency(ctx, &params, &latency); 376 const auto latency_error = cubeb_get_min_latency(ctx, &params, &latency);
363 if (latency_error != CUBEB_OK) { 377 if (latency_error != CUBEB_OK) {
364 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); 378 LOG_ERROR(Audio_Sink, "Cubeb could not get min latency, it is not suitable.");
365 latency = TargetSampleCount * 2; 379 return false;
366 } 380 }
367 latency = std::max(latency, TargetSampleCount * 2); 381 latency = std::max(latency, TargetSampleCount * 2);
368 cubeb_destroy(ctx); 382
369 return latency; 383 // Test opening a device with standard parameters
384 cubeb_devid output_device{0};
385 cubeb_devid input_device{0};
386 std::string name{"Yuzu test"};
387 cubeb_stream* stream{nullptr};
388
389 if (cubeb_stream_init(ctx, &stream, name.c_str(), input_device, nullptr, output_device, &params,
390 latency, &TmpDataCallback, &TmpStateCallback, nullptr) != CUBEB_OK) {
391 LOG_CRITICAL(Audio_Sink, "Cubeb could not open a device, it is not suitable.");
392 return false;
393 }
394
395 cubeb_stream_stop(stream);
396 cubeb_stream_destroy(stream);
397 return true;
398#endif
370} 399}
371 400
372} // namespace AudioCore::Sink 401} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h
index 3302cb98d..f49a6fdaa 100644
--- a/src/audio_core/sink/cubeb_sink.h
+++ b/src/audio_core/sink/cubeb_sink.h
@@ -97,10 +97,11 @@ private:
97std::vector<std::string> ListCubebSinkDevices(bool capture); 97std::vector<std::string> ListCubebSinkDevices(bool capture);
98 98
99/** 99/**
100 * Get the reported latency for this sink. 100 * Check if this backend is suitable for use.
101 * Checks if enabled, its latency, whether it opens successfully, etc.
101 * 102 *
102 * @return Minimum latency for this sink. 103 * @return True is this backend is suitable, false otherwise.
103 */ 104 */
104u32 GetCubebLatency(); 105bool IsCubebSuitable();
105 106
106} // namespace AudioCore::Sink 107} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp
index c1529d1f9..7b89151de 100644
--- a/src/audio_core/sink/sdl2_sink.cpp
+++ b/src/audio_core/sink/sdl2_sink.cpp
@@ -9,6 +9,7 @@
9#include "audio_core/sink/sdl2_sink.h" 9#include "audio_core/sink/sdl2_sink.h"
10#include "audio_core/sink/sink_stream.h" 10#include "audio_core/sink/sink_stream.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/scope_exit.h"
12#include "core/core.h" 13#include "core/core.h"
13 14
14namespace AudioCore::Sink { 15namespace AudioCore::Sink {
@@ -84,6 +85,7 @@ public:
84 } 85 }
85 86
86 Stop(); 87 Stop();
88 SDL_ClearQueuedAudio(device);
87 SDL_CloseAudioDevice(device); 89 SDL_CloseAudioDevice(device);
88 } 90 }
89 91
@@ -227,8 +229,42 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
227 return device_list; 229 return device_list;
228} 230}
229 231
230u32 GetSDLLatency() { 232bool IsSDLSuitable() {
231 return TargetSampleCount * 2; 233#if !defined(HAVE_SDL2)
234 return false;
235#else
236 // Check SDL can init
237 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
238 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
239 LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}",
240 SDL_GetError());
241 return false;
242 }
243 }
244
245 // We can set any latency frequency we want with SDL, so no need to check that.
246
247 // Check we can open a device with standard parameters
248 SDL_AudioSpec spec;
249 spec.freq = TargetSampleRate;
250 spec.channels = 2u;
251 spec.format = AUDIO_S16SYS;
252 spec.samples = TargetSampleCount * 2;
253 spec.callback = nullptr;
254 spec.userdata = nullptr;
255
256 SDL_AudioSpec obtained;
257 auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false);
258
259 if (device == 0) {
260 LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}",
261 SDL_GetError());
262 return false;
263 }
264
265 SDL_CloseAudioDevice(device);
266 return true;
267#endif
232} 268}
233 269
234} // namespace AudioCore::Sink 270} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h
index 27ed1ab94..9211d2e97 100644
--- a/src/audio_core/sink/sdl2_sink.h
+++ b/src/audio_core/sink/sdl2_sink.h
@@ -88,10 +88,11 @@ private:
88std::vector<std::string> ListSDLSinkDevices(bool capture); 88std::vector<std::string> ListSDLSinkDevices(bool capture);
89 89
90/** 90/**
91 * Get the reported latency for this sink. 91 * Check if this backend is suitable for use.
92 * Checks if enabled, its latency, whether it opens successfully, etc.
92 * 93 *
93 * @return Minimum latency for this sink. 94 * @return True is this backend is suitable, false otherwise.
94 */ 95 */
95u32 GetSDLLatency(); 96bool IsSDLSuitable();
96 97
97} // namespace AudioCore::Sink 98} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp
index 027bfa517..7c9a4e3ac 100644
--- a/src/audio_core/sink/sink_details.cpp
+++ b/src/audio_core/sink/sink_details.cpp
@@ -22,7 +22,7 @@ namespace {
22struct SinkDetails { 22struct SinkDetails {
23 using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); 23 using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
24 using ListDevicesFn = std::vector<std::string> (*)(bool); 24 using ListDevicesFn = std::vector<std::string> (*)(bool);
25 using LatencyFn = u32 (*)(); 25 using SuitableFn = bool (*)();
26 26
27 /// Name for this sink. 27 /// Name for this sink.
28 Settings::AudioEngine id; 28 Settings::AudioEngine id;
@@ -30,8 +30,8 @@ struct SinkDetails {
30 FactoryFn factory; 30 FactoryFn factory;
31 /// A method to call to list available devices. 31 /// A method to call to list available devices.
32 ListDevicesFn list_devices; 32 ListDevicesFn list_devices;
33 /// Method to get the latency of this backend. 33 /// Check whether this backend is suitable to be used.
34 LatencyFn latency; 34 SuitableFn is_suitable;
35}; 35};
36 36
37// sink_details is ordered in terms of desirability, with the best choice at the top. 37// sink_details is ordered in terms of desirability, with the best choice at the top.
@@ -43,7 +43,7 @@ constexpr SinkDetails sink_details[] = {
43 return std::make_unique<CubebSink>(device_id); 43 return std::make_unique<CubebSink>(device_id);
44 }, 44 },
45 &ListCubebSinkDevices, 45 &ListCubebSinkDevices,
46 &GetCubebLatency, 46 &IsCubebSuitable,
47 }, 47 },
48#endif 48#endif
49#ifdef HAVE_SDL2 49#ifdef HAVE_SDL2
@@ -53,14 +53,17 @@ constexpr SinkDetails sink_details[] = {
53 return std::make_unique<SDLSink>(device_id); 53 return std::make_unique<SDLSink>(device_id);
54 }, 54 },
55 &ListSDLSinkDevices, 55 &ListSDLSinkDevices,
56 &GetSDLLatency, 56 &IsSDLSuitable,
57 }, 57 },
58#endif 58#endif
59 SinkDetails{Settings::AudioEngine::Null, 59 SinkDetails{
60 [](std::string_view device_id) -> std::unique_ptr<Sink> { 60 Settings::AudioEngine::Null,
61 return std::make_unique<NullSink>(device_id); 61 [](std::string_view device_id) -> std::unique_ptr<Sink> {
62 }, 62 return std::make_unique<NullSink>(device_id);
63 [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }}, 63 },
64 [](bool capture) { return std::vector<std::string>{"null"}; },
65 []() { return true; },
66 },
64}; 67};
65 68
66const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) { 69const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
@@ -72,18 +75,22 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
72 auto iter = find_backend(sink_id); 75 auto iter = find_backend(sink_id);
73 76
74 if (sink_id == Settings::AudioEngine::Auto) { 77 if (sink_id == Settings::AudioEngine::Auto) {
75 // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which 78 // Auto-select a backend. Use the sink details ordering, preferring cubeb first, checking
76 // causes audio issues, in that case go with SDL. 79 // that the backend is available and suitable to use.
77#if defined(HAVE_CUBEB) && defined(HAVE_SDL2) 80 for (auto& details : sink_details) {
78 iter = find_backend(Settings::AudioEngine::Cubeb); 81 if (details.is_suitable()) {
79 if (iter->latency() > TargetSampleCount * 3) { 82 iter = &details;
80 iter = find_backend(Settings::AudioEngine::Sdl2); 83 break;
84 }
81 } 85 }
82#else
83 iter = std::begin(sink_details);
84#endif
85 LOG_INFO(Service_Audio, "Auto-selecting the {} backend", 86 LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
86 Settings::CanonicalizeEnum(iter->id)); 87 Settings::CanonicalizeEnum(iter->id));
88 } else {
89 if (iter != std::end(sink_details) && !iter->is_suitable()) {
90 LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null",
91 Settings::CanonicalizeEnum(iter->id));
92 iter = find_backend(Settings::AudioEngine::Null);
93 }
87 } 94 }
88 95
89 if (iter == std::end(sink_details)) { 96 if (iter == std::end(sink_details)) {
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 16a58a750..524056841 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <version> 4#include <version>
5#include "common/settings_enums.h"
5#if __cpp_lib_chrono >= 201907L 6#if __cpp_lib_chrono >= 201907L
6#include <chrono> 7#include <chrono>
7#include <exception> 8#include <exception>
@@ -145,6 +146,10 @@ bool IsFastmemEnabled() {
145 return true; 146 return true;
146} 147}
147 148
149bool IsDockedMode() {
150 return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
151}
152
148float Volume() { 153float Volume() {
149 if (values.audio_muted) { 154 if (values.audio_muted) {
150 return 0.0f; 155 return 0.0f;
diff --git a/src/common/settings.h b/src/common/settings.h
index 4407c1e6d..b15213bd7 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -379,7 +379,13 @@ struct Values {
379 379
380 Setting<s32> current_user{linkage, 0, "current_user", Category::System}; 380 Setting<s32> current_user{linkage, 0, "current_user", Category::System};
381 381
382 SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System}; 382 SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
383 ConsoleMode::Docked,
384 "use_docked_mode",
385 Category::System,
386 Specialization::Radio,
387 true,
388 true};
383 389
384 // Controls 390 // Controls
385 InputSetting<std::array<PlayerInput, 10>> players; 391 InputSetting<std::array<PlayerInput, 10>> players;
@@ -519,6 +525,8 @@ bool IsGPULevelHigh();
519 525
520bool IsFastmemEnabled(); 526bool IsFastmemEnabled();
521 527
528bool IsDockedMode();
529
522float Volume(); 530float Volume();
523 531
524std::string GetTimeZoneString(TimeZone time_zone); 532std::string GetTimeZoneString(TimeZone time_zone);
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 2efb329b0..3082e0ce1 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -56,6 +56,7 @@ enum Specialization : u8 {
56 Scalar = 5, // Values are continuous 56 Scalar = 5, // Values are continuous
57 Countable = 6, // Can be stepped through 57 Countable = 6, // Can be stepped through
58 Paired = 7, // Another setting is associated with this setting 58 Paired = 7, // Another setting is associated with this setting
59 Radio = 8, // Setting should be presented in a radio group
59 60
60 Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage 61 Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage
61}; 62};
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index e7cb59ea5..815cafe15 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -146,6 +146,8 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
146 146
147ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); 147ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
148 148
149ENUM(ConsoleMode, Handheld, Docked);
150
149template <typename Type> 151template <typename Type>
150inline std::string CanonicalizeEnum(Type id) { 152inline std::string CanonicalizeEnum(Type id) {
151 const auto group = EnumMetadata<Type>::Canonicalizations(); 153 const auto group = EnumMetadata<Type>::Canonicalizations();
diff --git a/src/common/swap.h b/src/common/swap.h
index 085baaf9a..fde343e45 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -460,11 +460,6 @@ S operator&(const S& i, const swap_struct_t<T, F> v) {
460 return i & v.swap(); 460 return i & v.swap();
461} 461}
462 462
463template <typename S, typename T, typename F>
464S operator&(const swap_struct_t<T, F> v, const S& i) {
465 return static_cast<S>(v.swap() & i);
466}
467
468// Comparison 463// Comparison
469template <typename S, typename T, typename F> 464template <typename S, typename T, typename F>
470bool operator<(const S& p, const swap_struct_t<T, F> v) { 465bool operator<(const S& p, const swap_struct_t<T, F> v) {
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index a28af3594..f70adab82 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -606,9 +606,9 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
606 const auto result = RemoveExistingEntry(title_id); 606 const auto result = RemoveExistingEntry(title_id);
607 607
608 // Install Metadata File 608 // Install Metadata File
609 const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id_data); 609 const auto meta_result = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id_data);
610 if (res != InstallResult::Success) { 610 if (meta_result != InstallResult::Success) {
611 return res; 611 return meta_result;
612 } 612 }
613 613
614 // Install all the other NCAs 614 // Install all the other NCAs
@@ -621,9 +621,19 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
621 if (nca == nullptr) { 621 if (nca == nullptr) {
622 return InstallResult::ErrorCopyFailed; 622 return InstallResult::ErrorCopyFailed;
623 } 623 }
624 const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id); 624 if (nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
625 if (res2 != InstallResult::Success) { 625 nca->GetTitleId() != title_id) {
626 return res2; 626 // Create fake cnmt for patch to multiprogram application
627 const auto sub_nca_result =
628 InstallEntry(*nca, TitleType::Update, overwrite_if_exists, copy);
629 if (sub_nca_result != InstallResult::Success) {
630 return sub_nca_result;
631 }
632 continue;
633 }
634 const auto nca_result = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
635 if (nca_result != InstallResult::Success) {
636 return nca_result;
627 } 637 }
628 } 638 }
629 639
@@ -663,6 +673,8 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
663} 673}
664 674
665bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { 675bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
676 bool removed_data = false;
677
666 const auto delete_nca = [this](const NcaID& id) { 678 const auto delete_nca = [this](const NcaID& id) {
667 const auto path = GetRelativePathFromNcaID(id, false, true, false); 679 const auto path = GetRelativePathFromNcaID(id, false, true, false);
668 680
@@ -706,11 +718,18 @@ bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
706 const auto deleted_html = delete_nca(html_id); 718 const auto deleted_html = delete_nca(html_id);
707 const auto deleted_legal = delete_nca(legal_id); 719 const auto deleted_legal = delete_nca(legal_id);
708 720
709 return deleted_meta && (deleted_meta || deleted_program || deleted_data || 721 removed_data |= (deleted_meta || deleted_program || deleted_data || deleted_control ||
710 deleted_control || deleted_html || deleted_legal); 722 deleted_html || deleted_legal);
711 } 723 }
712 724
713 return false; 725 // If patch entries for any program exist in yuzu meta, remove them
726 for (u8 i = 0; i < 0x10; i++) {
727 const auto meta_dir = dir->CreateDirectoryRelative("yuzu_meta");
728 const auto filename = GetCNMTName(TitleType::Update, title_id + i);
729 removed_data |= meta_dir->DeleteFile(filename);
730 }
731
732 return removed_data;
714} 733}
715 734
716InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, 735InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 3300d4f79..27755cb58 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -3,6 +3,8 @@
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/settings.h"
7#include "common/settings_enums.h"
6#include "core/frontend/applets/controller.h" 8#include "core/frontend/applets/controller.h"
7#include "core/hid/emulated_controller.h" 9#include "core/hid/emulated_controller.h"
8#include "core/hid/hid_core.h" 10#include "core/hid/hid_core.h"
@@ -62,7 +64,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
62 controller->Connect(true); 64 controller->Connect(true);
63 } 65 }
64 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && 66 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
65 !Settings::values.use_docked_mode.GetValue()) { 67 !Settings::IsDockedMode()) {
66 // We should *never* reach here under any normal circumstances. 68 // We should *never* reach here under any normal circumstances.
67 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); 69 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
68 controller->Connect(true); 70 controller->Connect(true);
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index b4081fc39..2590b20da 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "common/settings_enums.h"
8#include "core/frontend/framebuffer_layout.h" 9#include "core/frontend/framebuffer_layout.h"
9 10
10namespace Layout { 11namespace Layout {
@@ -49,7 +50,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
49} 50}
50 51
51FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale) { 52FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale) {
52 const bool is_docked = Settings::values.use_docked_mode.GetValue(); 53 const bool is_docked = Settings::IsDockedMode();
53 const u32 screen_width = is_docked ? ScreenDocked::Width : ScreenUndocked::Width; 54 const u32 screen_width = is_docked ? ScreenDocked::Width : ScreenUndocked::Width;
54 const u32 screen_height = is_docked ? ScreenDocked::Height : ScreenUndocked::Height; 55 const u32 screen_height = is_docked ? ScreenDocked::Height : ScreenUndocked::Height;
55 56
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index e573e2a57..703049ede 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -38,7 +38,7 @@ namespace {
38 */ 38 */
39void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, 39void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority,
40 KProcessAddress stack_top) { 40 KProcessAddress stack_top) {
41 const KProcessAddress entry_point = owner_process.GetPageTable().GetCodeRegionStart(); 41 const KProcessAddress entry_point = owner_process.GetEntryPoint();
42 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1)); 42 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1));
43 43
44 KThread* thread = KThread::Create(system.Kernel()); 44 KThread* thread = KThread::Create(system.Kernel());
@@ -358,6 +358,21 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
358 m_system_resource_size = metadata.GetSystemResourceSize(); 358 m_system_resource_size = metadata.GetSystemResourceSize();
359 m_image_size = code_size; 359 m_image_size = code_size;
360 360
361 if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit) {
362 // For 39-bit processes, the ASLR region starts at 0x800'0000 and is ~512GiB large.
363 // However, some (buggy) programs/libraries like skyline incorrectly depend on the
364 // existence of ASLR pages before the entry point, so we will adjust the load address
365 // to point to about 2GiB into the ASLR region.
366 m_code_address = 0x8000'0000;
367 } else {
368 // All other processes can be mapped at the beginning of the code region.
369 if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is36Bit) {
370 m_code_address = 0x800'0000;
371 } else {
372 m_code_address = 0x20'0000;
373 }
374 }
375
361 KScopedResourceReservation memory_reservation( 376 KScopedResourceReservation memory_reservation(
362 m_resource_limit, LimitableResource::PhysicalMemoryMax, code_size + m_system_resource_size); 377 m_resource_limit, LimitableResource::PhysicalMemoryMax, code_size + m_system_resource_size);
363 if (!memory_reservation.Succeeded()) { 378 if (!memory_reservation.Succeeded()) {
@@ -368,15 +383,15 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
368 // Initialize process address space 383 // Initialize process address space
369 if (const Result result{m_page_table.InitializeForProcess( 384 if (const Result result{m_page_table.InitializeForProcess(
370 metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application, 385 metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application,
371 0x8000000, code_size, std::addressof(m_kernel.GetAppSystemResource()), m_resource_limit, 386 this->GetEntryPoint(), code_size, std::addressof(m_kernel.GetAppSystemResource()),
372 m_kernel.System().ApplicationMemory())}; 387 m_resource_limit, m_kernel.System().ApplicationMemory())};
373 result.IsError()) { 388 result.IsError()) {
374 R_RETURN(result); 389 R_RETURN(result);
375 } 390 }
376 391
377 // Map process code region 392 // Map process code region
378 if (const Result result{m_page_table.MapProcessCode(m_page_table.GetCodeRegionStart(), 393 if (const Result result{m_page_table.MapProcessCode(this->GetEntryPoint(), code_size / PageSize,
379 code_size / PageSize, KMemoryState::Code, 394 KMemoryState::Code,
380 KMemoryPermission::None)}; 395 KMemoryPermission::None)};
381 result.IsError()) { 396 result.IsError()) {
382 R_RETURN(result); 397 R_RETURN(result);
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index c9b37e138..4fdeaf11a 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -177,6 +177,10 @@ public:
177 return m_program_id; 177 return m_program_id;
178 } 178 }
179 179
180 KProcessAddress GetEntryPoint() const {
181 return m_code_address;
182 }
183
180 /// Gets the resource limit descriptor for this process 184 /// Gets the resource limit descriptor for this process
181 KResourceLimit* GetResourceLimit() const; 185 KResourceLimit* GetResourceLimit() const;
182 186
@@ -485,6 +489,9 @@ private:
485 /// Address indicating the location of the process' dedicated TLS region. 489 /// Address indicating the location of the process' dedicated TLS region.
486 KProcessAddress m_plr_address = 0; 490 KProcessAddress m_plr_address = 0;
487 491
492 /// Address indicating the location of the process's entry point.
493 KProcessAddress m_code_address = 0;
494
488 /// Random values for svcGetInfo RandomEntropy 495 /// Random values for svcGetInfo RandomEntropy
489 std::array<u64, RANDOM_ENTROPY_SIZE> m_random_entropy{}; 496 std::array<u64, RANDOM_ENTROPY_SIZE> m_random_entropy{};
490 497
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 8d057b3a8..da33f0e44 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -6,6 +6,7 @@
6#include <cinttypes> 6#include <cinttypes>
7#include <cstring> 7#include <cstring>
8#include "common/settings.h" 8#include "common/settings.h"
9#include "common/settings_enums.h"
9#include "core/core.h" 10#include "core/core.h"
10#include "core/file_sys/control_metadata.h" 11#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/patch_manager.h" 12#include "core/file_sys/patch_manager.h"
@@ -833,7 +834,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) {
833 IPC::ResponseBuilder rb{ctx, 4}; 834 IPC::ResponseBuilder rb{ctx, 4};
834 rb.Push(ResultSuccess); 835 rb.Push(ResultSuccess);
835 836
836 if (Settings::values.use_docked_mode.GetValue()) { 837 if (Settings::IsDockedMode()) {
837 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 838 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
838 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); 839 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
839 } else { 840 } else {
@@ -921,7 +922,7 @@ void IStorage::Open(HLERequestContext& ctx) {
921} 922}
922 923
923void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) { 924void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) {
924 const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()}; 925 const bool use_docked_mode{Settings::IsDockedMode()};
925 LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); 926 LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
926 927
927 IPC::ResponseBuilder rb{ctx, 3}; 928 IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp
index 227fdd0cf..4f1aa5cc2 100644
--- a/src/core/hle/service/apm/apm_controller.cpp
+++ b/src/core/hle/service/apm/apm_controller.cpp
@@ -7,6 +7,7 @@
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h" 9#include "common/settings.h"
10#include "common/settings_enums.h"
10#include "core/core_timing.h" 11#include "core/core_timing.h"
11#include "core/hle/service/apm/apm_controller.h" 12#include "core/hle/service/apm/apm_controller.h"
12 13
@@ -67,8 +68,7 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
67} 68}
68 69
69PerformanceMode Controller::GetCurrentPerformanceMode() const { 70PerformanceMode Controller::GetCurrentPerformanceMode() const {
70 return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost 71 return Settings::IsDockedMode() ? PerformanceMode::Boost : PerformanceMode::Normal;
71 : PerformanceMode::Normal;
72} 72}
73 73
74PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { 74PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index fa77007f3..4a8276ed1 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -174,7 +174,7 @@ public:
174 {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"}, 174 {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"},
175 {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, 175 {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
176 {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, 176 {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
177 {9, nullptr, "DecodeInterleavedForMultiStream"}, 177 {9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"},
178 }; 178 };
179 // clang-format on 179 // clang-format on
180 180
@@ -206,6 +206,16 @@ private:
206 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); 206 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
207 } 207 }
208 208
209 void DecodeInterleavedForMultiStream(HLERequestContext& ctx) {
210 LOG_DEBUG(Audio, "called");
211
212 IPC::RequestParser rp{ctx};
213 const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
214 : OpusDecoderState::ExtraBehavior::None;
215
216 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
217 }
218
209 OpusDecoderState decoder_state; 219 OpusDecoderState decoder_state;
210}; 220};
211 221
@@ -354,6 +364,40 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
354 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); 364 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
355} 365}
356 366
367void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
368 OpusMultiStreamParametersEx params;
369 std::memcpy(&params, ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
370
371 const auto& sample_rate = params.sample_rate;
372 const auto& channel_count = params.channel_count;
373
374 LOG_INFO(
375 Audio,
376 "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}",
377 sample_rate, channel_count, params.number_streams, params.number_stereo_streams);
378
379 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
380 sample_rate == 12000 || sample_rate == 8000,
381 "Invalid sample rate");
382
383 int error = 0;
384 OpusDecoderPtr decoder{opus_multistream_decoder_create(
385 sample_rate, static_cast<int>(channel_count), params.number_streams,
386 params.number_stereo_streams, params.channel_mappings.data(), &error)};
387 if (error != OPUS_OK || decoder == nullptr) {
388 LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
389 IPC::ResponseBuilder rb{ctx, 2};
390 // TODO(ogniK): Use correct error code
391 rb.Push(ResultUnknown);
392 return;
393 }
394
395 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
396 rb.Push(ResultSuccess);
397 rb.PushIpcInterface<IHardwareOpusDecoderManager>(
398 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
399}
400
357HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { 401HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
358 static const FunctionInfo functions[] = { 402 static const FunctionInfo functions[] = {
359 {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, 403 {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"},
@@ -362,7 +406,8 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
362 {3, nullptr, "GetWorkBufferSizeForMultiStream"}, 406 {3, nullptr, "GetWorkBufferSizeForMultiStream"},
363 {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, 407 {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
364 {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, 408 {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
365 {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"}, 409 {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx,
410 "OpenHardwareOpusDecoderForMultiStreamEx"},
366 {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, 411 {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},
367 {8, nullptr, "GetWorkBufferSizeExEx"}, 412 {8, nullptr, "GetWorkBufferSizeExEx"},
368 {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"}, 413 {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"},
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h
index ece65c02c..91d9998ac 100644
--- a/src/core/hle/service/audio/hwopus.h
+++ b/src/core/hle/service/audio/hwopus.h
@@ -18,8 +18,10 @@ struct OpusMultiStreamParametersEx {
18 u32 number_stereo_streams; 18 u32 number_stereo_streams;
19 u32 use_large_frame_size; 19 u32 use_large_frame_size;
20 u32 padding; 20 u32 padding;
21 std::array<u32, 64> channel_mappings; 21 std::array<u8, 0x100> channel_mappings;
22}; 22};
23static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
24 "OpusMultiStreamParametersEx has incorrect size");
23 25
24class HwOpus final : public ServiceFramework<HwOpus> { 26class HwOpus final : public ServiceFramework<HwOpus> {
25public: 27public:
@@ -29,6 +31,7 @@ public:
29private: 31private:
30 void OpenHardwareOpusDecoder(HLERequestContext& ctx); 32 void OpenHardwareOpusDecoder(HLERequestContext& ctx);
31 void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); 33 void OpenHardwareOpusDecoderEx(HLERequestContext& ctx);
34 void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx);
32 void GetWorkBufferSize(HLERequestContext& ctx); 35 void GetWorkBufferSize(HLERequestContext& ctx);
33 void GetWorkBufferSizeEx(HLERequestContext& ctx); 36 void GetWorkBufferSizeEx(HLERequestContext& ctx);
34 void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); 37 void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx);
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 4c1ea1a5b..508db7360 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -4,6 +4,7 @@
4#include <utility> 4#include <utility>
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/fs/fs.h"
7#include "common/fs/path_util.h" 8#include "common/fs/path_util.h"
8#include "common/settings.h" 9#include "common/settings.h"
9#include "core/core.h" 10#include "core/core.h"
@@ -154,10 +155,18 @@ Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
154 std::string src_path(Common::FS::SanitizePath(src_path_)); 155 std::string src_path(Common::FS::SanitizePath(src_path_));
155 std::string dest_path(Common::FS::SanitizePath(dest_path_)); 156 std::string dest_path(Common::FS::SanitizePath(dest_path_));
156 auto src = backing->GetFileRelative(src_path); 157 auto src = backing->GetFileRelative(src_path);
158 auto dst = backing->GetFileRelative(dest_path);
157 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) { 159 if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
158 // Use more-optimized vfs implementation rename. 160 // Use more-optimized vfs implementation rename.
159 if (src == nullptr) 161 if (src == nullptr) {
160 return FileSys::ERROR_PATH_NOT_FOUND; 162 return FileSys::ERROR_PATH_NOT_FOUND;
163 }
164
165 if (dst && Common::FS::Exists(dst->GetFullPath())) {
166 LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath());
167 return FileSys::ERROR_PATH_ALREADY_EXISTS;
168 }
169
161 if (!src->Rename(Common::FS::GetFilename(dest_path))) { 170 if (!src->Rename(Common::FS::GetFilename(dest_path))) {
162 // TODO(DarkLordZach): Find a better error code for this 171 // TODO(DarkLordZach): Find a better error code for this
163 return ResultUnknown; 172 return ResultUnknown;
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 03432f7cb..63eecd42b 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -331,7 +331,7 @@ Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties()
331 }; 331 };
332 332
333 // Hack: There is no touch in docked but games still allow it 333 // Hack: There is no touch in docked but games still allow it
334 if (Settings::values.use_docked_mode.GetValue()) { 334 if (Settings::IsDockedMode()) {
335 gesture.points[id] = { 335 gesture.points[id] = {
336 .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), 336 .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
337 .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), 337 .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 28818c813..3b349b4c4 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -1518,7 +1518,7 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
1518 return false; 1518 return false;
1519 } 1519 }
1520 // Handheld shouldn't be supported in docked mode 1520 // Handheld shouldn't be supported in docked mode
1521 if (Settings::values.use_docked_mode.GetValue()) { 1521 if (Settings::IsDockedMode()) {
1522 return false; 1522 return false;
1523 } 1523 }
1524 1524
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 07e570a9f..7d7bb8687 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -204,9 +204,11 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
204 if (!mapping->fixed) { 204 if (!mapping->fixed) {
205 auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; 205 auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
206 u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; 206 u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
207 u32 page_size{mapping->big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
208 u64 aligned_size{Common::AlignUp(mapping->size, page_size)};
207 209
208 allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits), 210 allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
209 static_cast<u32>(mapping->size >> page_size_bits)); 211 static_cast<u32>(aligned_size >> page_size_bits));
210 } 212 }
211 213
212 // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state 214 // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 45b2c43b7..d539ed0f4 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -79,8 +79,8 @@ protected:
79 using HandlerFnP = void (Self::*)(HLERequestContext&); 79 using HandlerFnP = void (Self::*)(HLERequestContext&);
80 80
81 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. 81 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
82 [[nodiscard]] std::scoped_lock<std::mutex> LockService() { 82 [[nodiscard]] virtual std::unique_lock<std::mutex> LockService() {
83 return std::scoped_lock{lock_service}; 83 return std::unique_lock{lock_service};
84 } 84 }
85 85
86 /// System context that the service operates under. 86 /// System context that the service operates under.
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 11f8efbac..d8509c1dd 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -1029,6 +1029,11 @@ BSD::~BSD() {
1029 } 1029 }
1030} 1030}
1031 1031
1032std::unique_lock<std::mutex> BSD::LockService() {
1033 // Do not lock socket IClient instances.
1034 return {};
1035}
1036
1032BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} { 1037BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} {
1033 // clang-format off 1038 // clang-format off
1034 static const FunctionInfo functions[] = { 1039 static const FunctionInfo functions[] = {
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 430edb97c..161f22b9b 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -186,6 +186,9 @@ private:
186 186
187 // Callback identifier for the OnProxyPacketReceived event. 187 // Callback identifier for the OnProxyPacketReceived event.
188 Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received; 188 Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
189
190protected:
191 virtual std::unique_lock<std::mutex> LockService() override;
189}; 192};
190 193
191class BSDCFG final : public ServiceFramework<BSDCFG> { 194class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp
index bac21752a..491b76d48 100644
--- a/src/core/hle/service/sockets/nsd.cpp
+++ b/src/core/hle/service/sockets/nsd.cpp
@@ -19,6 +19,12 @@ enum class ServerEnvironmentType : u8 {
19 Dp, 19 Dp,
20}; 20};
21 21
22// This is nn::nsd::EnvironmentIdentifier
23struct EnvironmentIdentifier {
24 std::array<u8, 8> identifier;
25};
26static_assert(sizeof(EnvironmentIdentifier) == 0x8);
27
22NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { 28NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
23 // clang-format off 29 // clang-format off
24 static const FunctionInfo functions[] = { 30 static const FunctionInfo functions[] = {
@@ -101,8 +107,9 @@ void NSD::ResolveEx(HLERequestContext& ctx) {
101} 107}
102 108
103void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) { 109void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) {
104 const std::string environment_identifier = "lp1"; 110 constexpr EnvironmentIdentifier lp1 = {
105 ctx.WriteBuffer(environment_identifier); 111 .identifier = {'l', 'p', '1', '\0', '\0', '\0', '\0', '\0'}};
112 ctx.WriteBuffer(lp1);
106 113
107 IPC::ResponseBuilder rb{ctx, 2}; 114 IPC::ResponseBuilder rb{ctx, 2};
108 rb.Push(ResultSuccess); 115 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index 22e4a6f49..c657c4efd 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -150,6 +150,12 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
150 const std::string host = Common::StringFromBuffer(host_buffer); 150 const std::string host = Common::StringFromBuffer(host_buffer);
151 // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions. 151 // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions.
152 152
153 // Prevent resolution of Nintendo servers
154 if (host.find("srv.nintendo.net") != std::string::npos) {
155 LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
156 return {0, GetAddrInfoError::AGAIN};
157 }
158
153 auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt); 159 auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt);
154 if (!res.has_value()) { 160 if (!res.has_value()) {
155 return {0, Translate(res.error())}; 161 return {0, Translate(res.error())};
@@ -261,6 +267,12 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
261 const auto host_buffer = ctx.ReadBuffer(0); 267 const auto host_buffer = ctx.ReadBuffer(0);
262 const std::string host = Common::StringFromBuffer(host_buffer); 268 const std::string host = Common::StringFromBuffer(host_buffer);
263 269
270 // Prevent resolution of Nintendo servers
271 if (host.find("srv.nintendo.net") != std::string::npos) {
272 LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
273 return {0, GetAddrInfoError::AGAIN};
274 }
275
264 std::optional<std::string> service = std::nullopt; 276 std::optional<std::string> service = std::nullopt;
265 if (ctx.CanReadBuffer(1)) { 277 if (ctx.CanReadBuffer(1)) {
266 const std::span<const u8> service_buffer = ctx.ReadBuffer(1); 278 const std::span<const u8> service_buffer = ctx.ReadBuffer(1);
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index 2cba9e5c9..6c8427b0d 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -139,7 +139,6 @@ private:
139 bool do_not_close_socket = false; 139 bool do_not_close_socket = false;
140 bool get_server_cert_chain = false; 140 bool get_server_cert_chain = false;
141 std::shared_ptr<Network::SocketBase> socket; 141 std::shared_ptr<Network::SocketBase> socket;
142 bool did_set_host_name = false;
143 bool did_handshake = false; 142 bool did_handshake = false;
144 143
145 Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) { 144 Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
@@ -174,11 +173,7 @@ private:
174 Result SetHostNameImpl(const std::string& hostname) { 173 Result SetHostNameImpl(const std::string& hostname) {
175 LOG_DEBUG(Service_SSL, "called. hostname={}", hostname); 174 LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
176 ASSERT(!did_handshake); 175 ASSERT(!did_handshake);
177 Result res = backend->SetHostName(hostname); 176 return backend->SetHostName(hostname);
178 if (res == ResultSuccess) {
179 did_set_host_name = true;
180 }
181 return res;
182 } 177 }
183 178
184 Result SetVerifyOptionImpl(u32 option) { 179 Result SetVerifyOptionImpl(u32 option) {
@@ -208,9 +203,6 @@ private:
208 203
209 Result DoHandshakeImpl() { 204 Result DoHandshakeImpl() {
210 ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; }); 205 ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
211 ASSERT_OR_EXECUTE_MSG(
212 did_set_host_name, { return ResultInternalError; },
213 "Expected SetHostName before DoHandshake");
214 Result res = backend->DoHandshake(); 206 Result res = backend->DoHandshake();
215 did_handshake = res.IsSuccess(); 207 did_handshake = res.IsSuccess();
216 return res; 208 return res;
diff --git a/src/core/hle/service/ssl/ssl_backend_openssl.cpp b/src/core/hle/service/ssl/ssl_backend_openssl.cpp
index b2dd37cd4..5714e6f3c 100644
--- a/src/core/hle/service/ssl/ssl_backend_openssl.cpp
+++ b/src/core/hle/service/ssl/ssl_backend_openssl.cpp
@@ -167,9 +167,8 @@ public:
167 } 167 }
168 168
169 ~SSLConnectionBackendOpenSSL() { 169 ~SSLConnectionBackendOpenSSL() {
170 // these are null-tolerant: 170 // this is null-tolerant:
171 SSL_free(ssl); 171 SSL_free(ssl);
172 BIO_free(bio);
173 } 172 }
174 173
175 static void KeyLogCallback(const SSL* ssl, const char* line) { 174 static void KeyLogCallback(const SSL* ssl, const char* line) {
diff --git a/src/core/hle/service/ssl/ssl_backend_schannel.cpp b/src/core/hle/service/ssl/ssl_backend_schannel.cpp
index bda12b761..d834a0c1f 100644
--- a/src/core/hle/service/ssl/ssl_backend_schannel.cpp
+++ b/src/core/hle/service/ssl/ssl_backend_schannel.cpp
@@ -31,9 +31,9 @@ CredHandle cred_handle;
31static void OneTimeInit() { 31static void OneTimeInit() {
32 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; 32 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
33 schannel_cred.dwFlags = 33 schannel_cred.dwFlags =
34 SCH_USE_STRONG_CRYPTO | // don't allow insecure protocols 34 SCH_USE_STRONG_CRYPTO | // don't allow insecure protocols
35 SCH_CRED_AUTO_CRED_VALIDATION | // validate certs 35 SCH_CRED_NO_SERVERNAME_CHECK | // don't validate server names
36 SCH_CRED_NO_DEFAULT_CREDS; // don't automatically present a client certificate 36 SCH_CRED_NO_DEFAULT_CREDS; // don't automatically present a client certificate
37 // ^ I'm assuming that nobody would want to connect Yuzu to a 37 // ^ I'm assuming that nobody would want to connect Yuzu to a
38 // service that requires some OS-provided corporate client 38 // service that requires some OS-provided corporate client
39 // certificate, and presenting one to some arbitrary server 39 // certificate, and presenting one to some arbitrary server
@@ -227,16 +227,15 @@ public:
227 ciphertext_read_buf.size()); 227 ciphertext_read_buf.size());
228 } 228 }
229 229
230 const SECURITY_STATUS ret = 230 char* hostname_ptr = hostname ? const_cast<char*>(hostname->c_str()) : nullptr;
231 InitializeSecurityContextA(&cred_handle, initial_call_done ? &ctxt : nullptr, 231 const SECURITY_STATUS ret = InitializeSecurityContextA(
232 // Caller ensured we have set a hostname: 232 &cred_handle, initial_call_done ? &ctxt : nullptr, hostname_ptr, req,
233 const_cast<char*>(hostname.value().c_str()), req, 233 0, // Reserved1
234 0, // Reserved1 234 0, // TargetDataRep not used with Schannel
235 0, // TargetDataRep not used with Schannel 235 initial_call_done ? &input_desc : nullptr,
236 initial_call_done ? &input_desc : nullptr, 236 0, // Reserved2
237 0, // Reserved2 237 initial_call_done ? nullptr : &ctxt, &output_desc, &attr,
238 initial_call_done ? nullptr : &ctxt, &output_desc, &attr, 238 nullptr); // ptsExpiry
239 nullptr); // ptsExpiry
240 239
241 if (output_buffers[0].pvBuffer) { 240 if (output_buffers[0].pvBuffer) {
242 const std::span span(static_cast<u8*>(output_buffers[0].pvBuffer), 241 const std::span span(static_cast<u8*>(output_buffers[0].pvBuffer),
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 6bb02393c..2eb978379 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -217,7 +217,7 @@ private:
217 IPC::ResponseBuilder rb{ctx, 6}; 217 IPC::ResponseBuilder rb{ctx, 6};
218 rb.Push(ResultSuccess); 218 rb.Push(ResultSuccess);
219 219
220 if (Settings::values.use_docked_mode.GetValue()) { 220 if (Settings::IsDockedMode()) {
221 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 221 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
222 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); 222 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
223 } else { 223 } else {
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index e04ad19db..f4eaf3331 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -153,7 +153,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
153 153
154 // Load NSO modules 154 // Load NSO modules
155 modules.clear(); 155 modules.clear();
156 const VAddr base_address{GetInteger(process.GetPageTable().GetCodeRegionStart())}; 156 const VAddr base_address{GetInteger(process.GetEntryPoint())};
157 VAddr next_load_addr{base_address}; 157 VAddr next_load_addr{base_address};
158 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), 158 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
159 system.GetContentProvider()}; 159 system.GetContentProvider()};
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index ffe976b94..d722459c6 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -96,7 +96,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
96 } 96 }
97 97
98 codeset.memory = std::move(program_image); 98 codeset.memory = std::move(program_image);
99 const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart()); 99 const VAddr base_address = GetInteger(process.GetEntryPoint());
100 process.LoadModule(std::move(codeset), base_address); 100 process.LoadModule(std::move(codeset), base_address);
101 101
102 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address); 102 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 506808b5d..d7562b4bc 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -203,7 +203,7 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
203 203
204 // Load codeset for current process 204 // Load codeset for current process
205 codeset.memory = std::move(program_image); 205 codeset.memory = std::move(program_image);
206 process.LoadModule(std::move(codeset), process.GetPageTable().GetCodeRegionStart()); 206 process.LoadModule(std::move(codeset), process.GetEntryPoint());
207 207
208 return true; 208 return true;
209} 209}
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 74cc9579f..549822506 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -167,7 +167,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S
167 modules.clear(); 167 modules.clear();
168 168
169 // Load module 169 // Load module
170 const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart()); 170 const VAddr base_address = GetInteger(process.GetEntryPoint());
171 if (!LoadModule(process, system, *file, base_address, true, true)) { 171 if (!LoadModule(process, system, *file, base_address, true, true)) {
172 return {ResultStatus::ErrorLoadingNSO, {}}; 172 return {ResultStatus::ErrorLoadingNSO, {}};
173 } 173 }
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index b5b3e7eda..ed875d444 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -117,8 +117,8 @@ json GetProcessorStateDataAuto(Core::System& system) {
117 arm.SaveContext(context); 117 arm.SaveContext(context);
118 118
119 return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", 119 return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
120 GetInteger(process->GetPageTable().GetCodeRegionStart()), 120 GetInteger(process->GetEntryPoint()), context.sp, context.pc,
121 context.sp, context.pc, context.pstate, context.cpu_registers); 121 context.pstate, context.cpu_registers);
122} 122}
123 123
124json GetBacktraceData(Core::System& system) { 124json GetBacktraceData(Core::System& system) {
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 62b3f6636..c26179e03 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -14,6 +14,7 @@
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15 15
16#include "common/settings.h" 16#include "common/settings.h"
17#include "common/settings_enums.h"
17#include "core/file_sys/control_metadata.h" 18#include "core/file_sys/control_metadata.h"
18#include "core/file_sys/patch_manager.h" 19#include "core/file_sys/patch_manager.h"
19#include "core/loader/loader.h" 20#include "core/loader/loader.h"
@@ -275,7 +276,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
275 static_cast<u32>(Settings::values.shader_backend.GetValue())); 276 static_cast<u32>(Settings::values.shader_backend.GetValue()));
276 AddField(field_type, "Renderer_UseAsynchronousShaders", 277 AddField(field_type, "Renderer_UseAsynchronousShaders",
277 Settings::values.use_asynchronous_shaders.GetValue()); 278 Settings::values.use_asynchronous_shaders.GetValue());
278 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue()); 279 AddField(field_type, "System_UseDockedMode", Settings::IsDockedMode());
279} 280}
280 281
281bool TelemetrySession::SubmitTestcase() { 282bool TelemetrySession::SubmitTestcase() {
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 870e76ab0..188e862d7 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -835,15 +835,15 @@ public:
835 return input_engine->SupportsNfc(identifier); 835 return input_engine->SupportsNfc(identifier);
836 } 836 }
837 837
838 Common::Input::NfcState StartNfcPolling() { 838 Common::Input::NfcState StartNfcPolling() override {
839 return input_engine->StartNfcPolling(identifier); 839 return input_engine->StartNfcPolling(identifier);
840 } 840 }
841 841
842 Common::Input::NfcState StopNfcPolling() { 842 Common::Input::NfcState StopNfcPolling() override {
843 return input_engine->StopNfcPolling(identifier); 843 return input_engine->StopNfcPolling(identifier);
844 } 844 }
845 845
846 Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) { 846 Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) override {
847 return input_engine->ReadAmiiboData(identifier, out_data); 847 return input_engine->ReadAmiiboData(identifier, out_data);
848 } 848 }
849 849
@@ -852,11 +852,11 @@ public:
852 } 852 }
853 853
854 Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request, 854 Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request,
855 Common::Input::MifareRequest& out_data) { 855 Common::Input::MifareRequest& out_data) override {
856 return input_engine->ReadMifareData(identifier, request, out_data); 856 return input_engine->ReadMifareData(identifier, request, out_data);
857 } 857 }
858 858
859 Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) { 859 Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) override {
860 return input_engine->WriteMifareData(identifier, request); 860 return input_engine->WriteMifareData(identifier, request);
861 } 861 }
862 862
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index f0f450edb..8be7bd594 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -289,8 +289,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_ad
289 MarkWrittenBuffer(buffer_id, *cpu_addr, size); 289 MarkWrittenBuffer(buffer_id, *cpu_addr, size);
290 break; 290 break;
291 case ObtainBufferOperation::DiscardWrite: { 291 case ObtainBufferOperation::DiscardWrite: {
292 IntervalType interval{*cpu_addr, size}; 292 VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
293 VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
294 IntervalType interval{cpu_addr_start, cpu_addr_end};
293 ClearDownload(interval); 295 ClearDownload(interval);
296 common_ranges.subtract(interval);
294 break; 297 break;
295 } 298 }
296 default: 299 default:
@@ -1159,6 +1162,11 @@ void BufferCache<P>::UpdateDrawIndirect() {
1159 .size = static_cast<u32>(size), 1162 .size = static_cast<u32>(size),
1160 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), 1163 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
1161 }; 1164 };
1165 VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
1166 VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
1167 IntervalType interval{cpu_addr_start, cpu_addr_end};
1168 ClearDownload(interval);
1169 common_ranges.subtract(interval);
1162 }; 1170 };
1163 if (current_draw_indirect->include_count) { 1171 if (current_draw_indirect->include_count) {
1164 update(current_draw_indirect->count_start_address, sizeof(u32), 1172 update(current_draw_indirect->count_start_address, sizeof(u32),
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp
index 220cce28a..8d7da50fc 100644
--- a/src/video_core/host1x/codecs/codec.cpp
+++ b/src/video_core/host1x/codecs/codec.cpp
@@ -319,6 +319,7 @@ void Codec::Decode() {
319 LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); 319 LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
320 return; 320 return;
321 } 321 }
322 bool is_interlaced = initial_frame->interlaced_frame != 0;
322 if (av_codec_ctx->hw_device_ctx) { 323 if (av_codec_ctx->hw_device_ctx) {
323 final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; 324 final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
324 ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); 325 ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
@@ -334,7 +335,7 @@ void Codec::Decode() {
334 UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); 335 UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
335 return; 336 return;
336 } 337 }
337 if (!final_frame->interlaced_frame) { 338 if (!is_interlaced) {
338 av_frames.push(std::move(final_frame)); 339 av_frames.push(std::move(final_frame));
339 } else { 340 } else {
340 if (!filters_initialized) { 341 if (!filters_initialized) {
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index 905505ca1..5d0bb9cc4 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -27,14 +27,24 @@ MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192))
27 27
28namespace Tegra { 28namespace Tegra {
29 29
30static void Dump(u64 hash, std::span<const u32> code) { 30static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
31 const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)}; 31 const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
32 const auto macro_dir{base_dir / "macros"}; 32 const auto macro_dir{base_dir / "macros"};
33 if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) { 33 if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
34 LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories"); 34 LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
35 return; 35 return;
36 } 36 }
37 const auto name{macro_dir / fmt::format("{:016x}.macro", hash)}; 37 auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
38
39 if (decompiled) {
40 auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)};
41 if (Common::FS::Exists(name)) {
42 (void)Common::FS::RenameFile(name, new_name);
43 return;
44 }
45 name = new_name;
46 }
47
38 std::fstream macro_file(name, std::ios::out | std::ios::binary); 48 std::fstream macro_file(name, std::ios::out | std::ios::binary);
39 if (!macro_file) { 49 if (!macro_file) {
40 LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}", 50 LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}",
@@ -90,9 +100,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
90 if (!mid_method.has_value()) { 100 if (!mid_method.has_value()) {
91 cache_info.lle_program = Compile(macro_code->second); 101 cache_info.lle_program = Compile(macro_code->second);
92 cache_info.hash = Common::HashValue(macro_code->second); 102 cache_info.hash = Common::HashValue(macro_code->second);
93 if (Settings::values.dump_macros) {
94 Dump(cache_info.hash, macro_code->second);
95 }
96 } else { 103 } else {
97 const auto& macro_cached = uploaded_macro_code[mid_method.value()]; 104 const auto& macro_cached = uploaded_macro_code[mid_method.value()];
98 const auto rebased_method = method - mid_method.value(); 105 const auto rebased_method = method - mid_method.value();
@@ -102,9 +109,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
102 code.size() * sizeof(u32)); 109 code.size() * sizeof(u32));
103 cache_info.hash = Common::HashValue(code); 110 cache_info.hash = Common::HashValue(code);
104 cache_info.lle_program = Compile(code); 111 cache_info.lle_program = Compile(code);
105 if (Settings::values.dump_macros) {
106 Dump(cache_info.hash, code);
107 }
108 } 112 }
109 113
110 auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); 114 auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
@@ -117,6 +121,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
117 MICROPROFILE_SCOPE(MacroHLE); 121 MICROPROFILE_SCOPE(MacroHLE);
118 cache_info.hle_program->Execute(parameters, method); 122 cache_info.hle_program->Execute(parameters, method);
119 } 123 }
124
125 if (Settings::values.dump_macros) {
126 Dump(cache_info.hash, macro_code->second, cache_info.has_hle_program);
127 }
120 } 128 }
121} 129}
122 130
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index c1314ca99..fe432dfe1 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -611,9 +611,6 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
611 611
612 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; 612 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
613 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); 613 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
614 if (Settings::values.dump_shaders) {
615 env.Dump(hash, key.unique_hashes[index]);
616 }
617 if (!uses_vertex_a || index != 1) { 614 if (!uses_vertex_a || index != 1) {
618 // Normal path 615 // Normal path
619 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); 616 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -624,6 +621,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
624 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); 621 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
625 } 622 }
626 623
624 if (Settings::values.dump_shaders) {
625 env.Dump(hash, key.unique_hashes[index]);
626 }
627
627 if (programs[index].info.requires_layer_emulation) { 628 if (programs[index].info.requires_layer_emulation) {
628 layer_source_program = &programs[index]; 629 layer_source_program = &programs[index];
629 } 630 }
diff --git a/src/web_service/verify_user_jwt.cpp b/src/web_service/verify_user_jwt.cpp
index 129eb1968..f88f67620 100644
--- a/src/web_service/verify_user_jwt.cpp
+++ b/src/web_service/verify_user_jwt.cpp
@@ -4,6 +4,7 @@
4#if defined(__GNUC__) || defined(__clang__) 4#if defined(__GNUC__) || defined(__clang__)
5#pragma GCC diagnostic push 5#pragma GCC diagnostic push
6#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" 6#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
7#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // for deprecated OpenSSL functions
7#endif 8#endif
8#include <jwt/jwt.hpp> 9#include <jwt/jwt.hpp>
9#if defined(__GNUC__) || defined(__clang__) 10#if defined(__GNUC__) || defined(__clang__)
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 00aafb8f8..d15559518 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -5,6 +5,8 @@
5#include <thread> 5#include <thread>
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/settings.h"
9#include "common/settings_enums.h"
8#include "common/string_util.h" 10#include "common/string_util.h"
9#include "core/core.h" 11#include "core/core.h"
10#include "core/hid/emulated_controller.h" 12#include "core/hid/emulated_controller.h"
@@ -226,9 +228,11 @@ int QtControllerSelectorDialog::exec() {
226} 228}
227 229
228void QtControllerSelectorDialog::ApplyConfiguration() { 230void QtControllerSelectorDialog::ApplyConfiguration() {
229 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); 231 const bool pre_docked_mode = Settings::IsDockedMode();
230 Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); 232 const bool docked_mode_selected = ui->radioDocked->isChecked();
231 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system); 233 Settings::values.use_docked_mode.SetValue(
234 docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld);
235 OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system);
232 236
233 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); 237 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
234 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); 238 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
@@ -616,8 +620,8 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
616 ui->radioDocked->setEnabled(!is_handheld); 620 ui->radioDocked->setEnabled(!is_handheld);
617 ui->radioUndocked->setEnabled(!is_handheld); 621 ui->radioUndocked->setEnabled(!is_handheld);
618 622
619 ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); 623 ui->radioDocked->setChecked(Settings::IsDockedMode());
620 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); 624 ui->radioUndocked->setChecked(!Settings::IsDockedMode());
621 625
622 // Also force into undocked mode if the controller type is handheld. 626 // Also force into undocked mode if the controller type is handheld.
623 if (is_handheld) { 627 if (is_handheld) {
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 407988b8f..2afa72140 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -928,8 +928,8 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
928 const Layout::FramebufferLayout layout{[]() { 928 const Layout::FramebufferLayout layout{[]() {
929 u32 height = UISettings::values.screenshot_height.GetValue(); 929 u32 height = UISettings::values.screenshot_height.GetValue();
930 if (height == 0) { 930 if (height == 0) {
931 height = Settings::values.use_docked_mode.GetValue() ? Layout::ScreenDocked::Height 931 height = Settings::IsDockedMode() ? Layout::ScreenDocked::Height
932 : Layout::ScreenUndocked::Height; 932 : Layout::ScreenUndocked::Height;
933 height *= Settings::values.resolution_info.up_factor; 933 height *= Settings::values.resolution_info.up_factor;
934 } 934 }
935 const u32 width = 935 const u32 width =
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index b22c83303..1de093447 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -9,6 +9,7 @@
9#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
10#include "common/settings.h" 10#include "common/settings.h"
11#include "common/settings_common.h" 11#include "common/settings_common.h"
12#include "common/settings_enums.h"
12#include "core/core.h" 13#include "core/core.h"
13#include "core/hle/service/acc/profile_manager.h" 14#include "core/hle/service/acc/profile_manager.h"
14#include "core/hle/service/hid/controllers/npad.h" 15#include "core/hle/service/hid/controllers/npad.h"
@@ -85,9 +86,9 @@ const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_ma
85 {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, 86 {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
86}; 87};
87 88
88const std::map<bool, QString> Config::use_docked_mode_texts_map = { 89const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = {
89 {true, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, 90 {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
90 {false, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, 91 {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
91}; 92};
92 93
93const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = { 94const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = {
@@ -376,7 +377,7 @@ void Config::ReadControlValues() {
376 const auto controller_type = Settings::values.players.GetValue()[0].controller_type; 377 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
377 if (controller_type == Settings::ControllerType::Handheld) { 378 if (controller_type == Settings::ControllerType::Handheld) {
378 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); 379 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
379 Settings::values.use_docked_mode.SetValue(false); 380 Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
380 } 381 }
381 382
382 if (IsCustomConfig()) { 383 if (IsCustomConfig()) {
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 0ac74c8e7..727feebfb 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -9,6 +9,7 @@
9#include <QMetaType> 9#include <QMetaType>
10#include <QVariant> 10#include <QVariant>
11#include "common/settings.h" 11#include "common/settings.h"
12#include "common/settings_enums.h"
12#include "yuzu/uisettings.h" 13#include "yuzu/uisettings.h"
13 14
14class QSettings; 15class QSettings;
@@ -51,7 +52,7 @@ public:
51 52
52 static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map; 53 static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map;
53 static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map; 54 static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map;
54 static const std::map<bool, QString> use_docked_mode_texts_map; 55 static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map;
55 static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map; 56 static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map;
56 static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; 57 static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
57 static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; 58 static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 7fce85bca..e8f9ebfd8 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -4,6 +4,8 @@
4#include <memory> 4#include <memory>
5#include <thread> 5#include <thread>
6 6
7#include "common/settings.h"
8#include "common/settings_enums.h"
7#include "core/core.h" 9#include "core/core.h"
8#include "core/hid/emulated_controller.h" 10#include "core/hid/emulated_controller.h"
9#include "core/hid/hid_core.h" 11#include "core/hid/hid_core.h"
@@ -197,9 +199,11 @@ void ConfigureInput::ApplyConfiguration() {
197 199
198 advanced->ApplyConfiguration(); 200 advanced->ApplyConfiguration();
199 201
200 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); 202 const bool pre_docked_mode = Settings::IsDockedMode();
201 Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); 203 const bool docked_mode_selected = ui->radioDocked->isChecked();
202 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system); 204 Settings::values.use_docked_mode.SetValue(
205 docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld);
206 OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system);
203 207
204 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); 208 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
205 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); 209 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
@@ -267,8 +271,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
267 ui->radioDocked->setEnabled(!is_handheld); 271 ui->radioDocked->setEnabled(!is_handheld);
268 ui->radioUndocked->setEnabled(!is_handheld); 272 ui->radioUndocked->setEnabled(!is_handheld);
269 273
270 ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); 274 ui->radioDocked->setChecked(Settings::IsDockedMode());
271 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); 275 ui->radioUndocked->setChecked(!Settings::IsDockedMode());
272 276
273 // Also force into undocked mode if the controller type is handheld. 277 // Also force into undocked mode if the controller type is handheld.
274 if (is_handheld) { 278 if (is_handheld) {
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 4f9e8db08..b91d6ad4a 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -18,6 +18,7 @@
18 18
19#include "common/fs/fs_util.h" 19#include "common/fs/fs_util.h"
20#include "common/settings_enums.h" 20#include "common/settings_enums.h"
21#include "common/settings_input.h"
21#include "configuration/shared_widget.h" 22#include "configuration/shared_widget.h"
22#include "core/core.h" 23#include "core/core.h"
23#include "core/file_sys/control_metadata.h" 24#include "core/file_sys/control_metadata.h"
@@ -98,6 +99,12 @@ void ConfigurePerGame::ApplyConfiguration() {
98 addons_tab->ApplyConfiguration(); 99 addons_tab->ApplyConfiguration();
99 input_tab->ApplyConfiguration(); 100 input_tab->ApplyConfiguration();
100 101
102 if (Settings::IsDockedMode() && Settings::values.players.GetValue()[0].controller_type ==
103 Settings::ControllerType::Handheld) {
104 Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
105 Settings::values.use_docked_mode.SetGlobal(true);
106 }
107
101 system.ApplySettings(); 108 system.ApplySettings();
102 Settings::LogSettings(); 109 Settings::LogSettings();
103 110
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index c4833f4e7..0c8e5c8b4 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -106,6 +106,11 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
106 push(Settings::values.linkage.by_category[Settings::Category::System]); 106 push(Settings::values.linkage.by_category[Settings::Category::System]);
107 107
108 for (auto setting : settings) { 108 for (auto setting : settings) {
109 if (setting->Id() == Settings::values.use_docked_mode.Id() &&
110 Settings::IsConfiguringGlobal()) {
111 continue;
112 }
113
109 ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs); 114 ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
110 115
111 if (widget == nullptr) { 116 if (widget == nullptr) {
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index 335810788..276bdbaba 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -135,7 +135,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
135 INSERT(Settings, region_index, "Region:", ""); 135 INSERT(Settings, region_index, "Region:", "");
136 INSERT(Settings, time_zone_index, "Time Zone:", ""); 136 INSERT(Settings, time_zone_index, "Time Zone:", "");
137 INSERT(Settings, sound_index, "Sound Output Mode:", ""); 137 INSERT(Settings, sound_index, "Sound Output Mode:", "");
138 INSERT(Settings, use_docked_mode, "", ""); 138 INSERT(Settings, use_docked_mode, "Console Mode:", "");
139 INSERT(Settings, current_user, "", ""); 139 INSERT(Settings, current_user, "", "");
140 140
141 // Controls 141 // Controls
@@ -379,6 +379,9 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
379 PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), 379 PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"),
380 PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), 380 PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"),
381 }}); 381 }});
382 translations->insert(
383 {Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
384 {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
382 385
383#undef PAIR 386#undef PAIR
384#undef CTX_PAIR 387#undef CTX_PAIR
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
index bdb38c8ea..7721e58f9 100644
--- a/src/yuzu/configuration/shared_widget.cpp
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -23,6 +23,7 @@
23#include <QLineEdit> 23#include <QLineEdit>
24#include <QObject> 24#include <QObject>
25#include <QPushButton> 25#include <QPushButton>
26#include <QRadioButton>
26#include <QRegularExpression> 27#include <QRegularExpression>
27#include <QSizePolicy> 28#include <QSizePolicy>
28#include <QSlider> 29#include <QSlider>
@@ -171,6 +172,65 @@ QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer,
171 return combobox; 172 return combobox;
172} 173}
173 174
175QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
176 std::function<void()>& restore_func,
177 const std::function<void()>& touch) {
178 const auto type = setting.EnumIndex();
179
180 QWidget* group = new QWidget(this);
181 QHBoxLayout* layout = new QHBoxLayout(group);
182 layout->setContentsMargins(0, 0, 0, 0);
183 group->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
184
185 const ComboboxTranslations* enumeration{nullptr};
186 if (combobox_enumerations.contains(type)) {
187 enumeration = &combobox_enumerations.at(type);
188 for (const auto& [id, name] : *enumeration) {
189 QRadioButton* radio_button = new QRadioButton(name, group);
190 layout->addWidget(radio_button);
191 radio_buttons.push_back({id, radio_button});
192 }
193 } else {
194 return group;
195 }
196
197 const auto get_selected = [=]() -> u32 {
198 for (const auto& [id, button] : radio_buttons) {
199 if (button->isChecked()) {
200 return id;
201 }
202 }
203 return -1;
204 };
205
206 const auto set_index = [=](u32 value) {
207 for (const auto& [id, button] : radio_buttons) {
208 button->setChecked(id == value);
209 }
210 };
211
212 const u32 setting_value = std::stoi(setting.ToString());
213 set_index(setting_value);
214
215 serializer = [get_selected]() {
216 int current = get_selected();
217 return std::to_string(current);
218 };
219
220 restore_func = [this, set_index]() {
221 const u32 global_value = std::stoi(RelevantDefault(setting));
222 set_index(global_value);
223 };
224
225 if (!Settings::IsConfiguringGlobal()) {
226 for (const auto& [id, button] : radio_buttons) {
227 QObject::connect(button, &QAbstractButton::clicked, [touch]() { touch(); });
228 }
229 }
230
231 return group;
232}
233
174QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer, 234QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer,
175 std::function<void()>& restore_func, 235 std::function<void()>& restore_func,
176 const std::function<void()>& touch, bool managed) { 236 const std::function<void()>& touch, bool managed) {
@@ -235,6 +295,7 @@ QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& gi
235 slider->setValue(std::stoi(setting.ToString())); 295 slider->setValue(std::stoi(setting.ToString()));
236 296
237 slider->setInvertedAppearance(reversed); 297 slider->setInvertedAppearance(reversed);
298 slider->setInvertedControls(reversed);
238 299
239 serializer = [this]() { return std::to_string(slider->value()); }; 300 serializer = [this]() { return std::to_string(slider->value()); };
240 restore_func = [this]() { slider->setValue(std::stoi(RelevantDefault(setting))); }; 301 restore_func = [this]() { slider->setValue(std::stoi(RelevantDefault(setting))); };
@@ -410,6 +471,8 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
410 return RequestType::Slider; 471 return RequestType::Slider;
411 case Settings::Specialization::Countable: 472 case Settings::Specialization::Countable:
412 return RequestType::SpinBox; 473 return RequestType::SpinBox;
474 case Settings::Specialization::Radio:
475 return RequestType::RadioGroup;
413 default: 476 default:
414 break; 477 break;
415 } 478 }
@@ -438,7 +501,11 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
438 if (setting.TypeId() == typeid(bool)) { 501 if (setting.TypeId() == typeid(bool)) {
439 data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch); 502 data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch);
440 } else if (setting.IsEnum()) { 503 } else if (setting.IsEnum()) {
441 data_component = CreateCombobox(serializer, restore_func, touch); 504 if (request == RequestType::RadioGroup) {
505 data_component = CreateRadioGroup(serializer, restore_func, touch);
506 } else {
507 data_component = CreateCombobox(serializer, restore_func, touch);
508 }
442 } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) || 509 } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) ||
443 type == typeid(s64) || type == typeid(u8)) { 510 type == typeid(s64) || type == typeid(u8)) {
444 switch (request) { 511 switch (request) {
diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h
index e64693bab..5303dd898 100644
--- a/src/yuzu/configuration/shared_widget.h
+++ b/src/yuzu/configuration/shared_widget.h
@@ -22,6 +22,7 @@ class QObject;
22class QPushButton; 22class QPushButton;
23class QSlider; 23class QSlider;
24class QSpinBox; 24class QSpinBox;
25class QRadioButton;
25 26
26namespace Settings { 27namespace Settings {
27class BasicSetting; 28class BasicSetting;
@@ -38,6 +39,7 @@ enum class RequestType {
38 LineEdit, 39 LineEdit,
39 HexEdit, 40 HexEdit,
40 DateTimeEdit, 41 DateTimeEdit,
42 RadioGroup,
41 MaxEnum, 43 MaxEnum,
42}; 44};
43 45
@@ -91,6 +93,7 @@ public:
91 QSlider* slider{}; 93 QSlider* slider{};
92 QComboBox* combobox{}; 94 QComboBox* combobox{};
93 QDateTimeEdit* date_time_edit{}; 95 QDateTimeEdit* date_time_edit{};
96 std::vector<std::pair<u32, QRadioButton*>> radio_buttons{};
94 97
95private: 98private:
96 void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed, 99 void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
@@ -106,6 +109,9 @@ private:
106 QWidget* CreateCombobox(std::function<std::string()>& serializer, 109 QWidget* CreateCombobox(std::function<std::string()>& serializer,
107 std::function<void()>& restore_func, 110 std::function<void()>& restore_func,
108 const std::function<void()>& touch); 111 const std::function<void()>& touch);
112 QWidget* CreateRadioGroup(std::function<std::string()>& serializer,
113 std::function<void()>& restore_func,
114 const std::function<void()>& touch);
109 QWidget* CreateLineEdit(std::function<std::string()>& serializer, 115 QWidget* CreateLineEdit(std::function<std::string()>& serializer,
110 std::function<void()>& restore_func, const std::function<void()>& touch, 116 std::function<void()>& restore_func, const std::function<void()>& touch,
111 bool managed = true); 117 bool managed = true);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 9404365b4..e7fb8a282 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -191,8 +191,9 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
191} 191}
192 192
193QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::string& name, 193QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::string& name,
194 const std::vector<u8>& icon, Loader::AppLoader& loader, 194 const std::size_t size, const std::vector<u8>& icon,
195 u64 program_id, const CompatibilityList& compatibility_list, 195 Loader::AppLoader& loader, u64 program_id,
196 const CompatibilityList& compatibility_list,
196 const FileSys::PatchManager& patch) { 197 const FileSys::PatchManager& patch) {
197 const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 198 const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
198 199
@@ -210,7 +211,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
210 file_type_string, program_id), 211 file_type_string, program_id),
211 new GameListItemCompat(compatibility), 212 new GameListItemCompat(compatibility),
212 new GameListItem(file_type_string), 213 new GameListItem(file_type_string),
213 new GameListItemSize(Common::FS::GetSize(path)), 214 new GameListItemSize(size),
214 }; 215 };
215 216
216 const auto patch_versions = GetGameListCachedObject( 217 const auto patch_versions = GetGameListCachedObject(
@@ -278,8 +279,8 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
278 GetMetadataFromControlNCA(patch, *control, icon, name); 279 GetMetadataFromControlNCA(patch, *control, icon, name);
279 } 280 }
280 281
281 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, 282 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader,
282 compatibility_list, patch), 283 program_id, compatibility_list, patch),
283 parent_dir); 284 parent_dir);
284 } 285 }
285} 286}
@@ -354,8 +355,9 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
354 const FileSys::PatchManager patch{id, system.GetFileSystemController(), 355 const FileSys::PatchManager patch{id, system.GetFileSystemController(),
355 system.GetContentProvider()}; 356 system.GetContentProvider()};
356 357
357 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, id, 358 emit EntryReady(MakeGameListEntry(physical_name, name,
358 compatibility_list, patch), 359 Common::FS::GetSize(physical_name), icon,
360 *loader, id, compatibility_list, patch),
359 parent_dir); 361 parent_dir);
360 } 362 }
361 } else { 363 } else {
@@ -368,9 +370,10 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
368 const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), 370 const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
369 system.GetContentProvider()}; 371 system.GetContentProvider()};
370 372
371 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, 373 emit EntryReady(
372 program_id, compatibility_list, patch), 374 MakeGameListEntry(physical_name, name, Common::FS::GetSize(physical_name),
373 parent_dir); 375 icon, *loader, program_id, compatibility_list, patch),
376 parent_dir);
374 } 377 }
375 } 378 }
376 } else if (is_dir) { 379 } else if (is_dir) {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index a9d035f3d..33c9fd0af 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1158,9 +1158,9 @@ void GMainWindow::InitializeWidgets() {
1158 [this](const QPoint& menu_location) { 1158 [this](const QPoint& menu_location) {
1159 QMenu context_menu; 1159 QMenu context_menu;
1160 1160
1161 for (auto const& docked_mode_pair : Config::use_docked_mode_texts_map) { 1161 for (auto const& pair : Config::use_docked_mode_texts_map) {
1162 context_menu.addAction(docked_mode_pair.second, [this, docked_mode_pair] { 1162 context_menu.addAction(pair.second, [this, &pair] {
1163 if (docked_mode_pair.first != Settings::values.use_docked_mode.GetValue()) { 1163 if (pair.first != Settings::values.use_docked_mode.GetValue()) {
1164 OnToggleDockedMode(); 1164 OnToggleDockedMode();
1165 } 1165 }
1166 }); 1166 });
@@ -1811,6 +1811,43 @@ bool GMainWindow::SelectAndSetCurrentUser(
1811 return true; 1811 return true;
1812} 1812}
1813 1813
1814void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
1815 // Ensure all NCAs are registered before launching the game
1816 const auto file = vfs->OpenFile(filepath, FileSys::Mode::Read);
1817 if (!file) {
1818 return;
1819 }
1820
1821 auto loader = Loader::GetLoader(*system, file);
1822 if (!loader) {
1823 return;
1824 }
1825
1826 const auto file_type = loader->GetFileType();
1827 if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
1828 return;
1829 }
1830
1831 u64 program_id = 0;
1832 const auto res2 = loader->ReadProgramId(program_id);
1833 if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
1834 provider->AddEntry(FileSys::TitleType::Application,
1835 FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id,
1836 file);
1837 } else if (res2 == Loader::ResultStatus::Success &&
1838 (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
1839 const auto nsp = file_type == Loader::FileType::NSP
1840 ? std::make_shared<FileSys::NSP>(file)
1841 : FileSys::XCI{file}.GetSecurePartitionNSP();
1842 for (const auto& title : nsp->GetNCAs()) {
1843 for (const auto& entry : title.second) {
1844 provider->AddEntry(entry.first.first, entry.first.second, title.first,
1845 entry.second->GetBaseFile());
1846 }
1847 }
1848 }
1849}
1850
1814void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, 1851void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
1815 StartGameType type) { 1852 StartGameType type) {
1816 LOG_INFO(Frontend, "yuzu starting..."); 1853 LOG_INFO(Frontend, "yuzu starting...");
@@ -1825,6 +1862,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1825 1862
1826 last_filename_booted = filename; 1863 last_filename_booted = filename;
1827 1864
1865 ConfigureFilesystemProvider(filename.toStdString());
1828 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); 1866 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1829 const auto loader = Loader::GetLoader(*system, v_file, program_id, program_index); 1867 const auto loader = Loader::GetLoader(*system, v_file, program_id, program_index);
1830 1868
@@ -3636,7 +3674,7 @@ void GMainWindow::OnTasReset() {
3636} 3674}
3637 3675
3638void GMainWindow::OnToggleDockedMode() { 3676void GMainWindow::OnToggleDockedMode() {
3639 const bool is_docked = Settings::values.use_docked_mode.GetValue(); 3677 const bool is_docked = Settings::IsDockedMode();
3640 auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 3678 auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
3641 auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); 3679 auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
3642 3680
@@ -3650,7 +3688,8 @@ void GMainWindow::OnToggleDockedMode() {
3650 controller_dialog->refreshConfiguration(); 3688 controller_dialog->refreshConfiguration();
3651 } 3689 }
3652 3690
3653 Settings::values.use_docked_mode.SetValue(!is_docked); 3691 Settings::values.use_docked_mode.SetValue(is_docked ? Settings::ConsoleMode::Handheld
3692 : Settings::ConsoleMode::Docked);
3654 UpdateDockedButton(); 3693 UpdateDockedButton();
3655 OnDockedModeChanged(is_docked, !is_docked, *system); 3694 OnDockedModeChanged(is_docked, !is_docked, *system);
3656} 3695}
@@ -4080,10 +4119,10 @@ void GMainWindow::UpdateGPUAccuracyButton() {
4080} 4119}
4081 4120
4082void GMainWindow::UpdateDockedButton() { 4121void GMainWindow::UpdateDockedButton() {
4083 const bool is_docked = Settings::values.use_docked_mode.GetValue(); 4122 const auto console_mode = Settings::values.use_docked_mode.GetValue();
4084 dock_status_button->setChecked(is_docked); 4123 dock_status_button->setChecked(Settings::IsDockedMode());
4085 dock_status_button->setText( 4124 dock_status_button->setText(
4086 Config::use_docked_mode_texts_map.find(is_docked)->second.toUpper()); 4125 Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
4087} 4126}
4088 4127
4089void GMainWindow::UpdateAPIText() { 4128void GMainWindow::UpdateAPIText() {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 2cfb96257..1b7055122 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -399,6 +399,7 @@ private:
399 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); 399 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
400 bool CheckDarkMode(); 400 bool CheckDarkMode();
401 bool CheckSystemArchiveDecryption(); 401 bool CheckSystemArchiveDecryption();
402 void ConfigureFilesystemProvider(const std::string& filepath);
402 403
403 QString GetTasStateDescription() const; 404 QString GetTasStateDescription() const;
404 bool CreateShortcut(const std::string& shortcut_path, const std::string& title, 405 bool CreateShortcut(const std::string& shortcut_path, const std::string& title,