summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/yuzu-mainline-step2.yml1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt44
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt (renamed from src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt)44
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Addon.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt42
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Patch.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/PatchType.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt101
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt80
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt5
-rw-r--r--src/android/app/src/main/jni/android_common/android_common.cpp16
-rw-r--r--src/android/app/src/main/jni/android_common/android_common.h7
-rw-r--r--src/android/app/src/main/jni/android_config.cpp2
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp101
-rw-r--r--src/android/app/src/main/jni/id_cache.h17
-rw-r--r--src/android/app/src/main/jni/native.cpp128
-rw-r--r--src/android/app/src/main/jni/native.h2
-rw-r--r--src/android/app/src/main/res/layout/card_home_option.xml6
-rw-r--r--src/android/app/src/main/res/layout/dialog_progress_bar.xml30
-rw-r--r--src/android/app/src/main/res/layout/list_item_addon.xml32
-rw-r--r--src/android/app/src/main/res/values/strings.xml3
-rw-r--r--src/common/fs/file.h2
-rw-r--r--src/core/CMakeLists.txt17
-rw-r--r--src/core/file_sys/patch_manager.cpp43
-rw-r--r--src/core/file_sys/patch_manager.h17
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp17
-rw-r--r--src/core/hle/service/acc/profile_manager.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp3
-rw-r--r--src/core/hle/service/hid/hid_server.cpp334
-rw-r--r--src/core/hle/service/hid/hid_server.h1
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp98
-rw-r--r--src/core/hle/service/hid/hid_system_server.h6
-rw-r--r--src/core/hle/service/nfc/common/device.cpp5
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp16
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h4
-rw-r--r--src/core/hle/service/set/private_settings.h72
-rw-r--r--src/core/hle/service/set/setting_formats/appln_settings.cpp (renamed from src/core/hle/service/set/appln_settings.cpp)8
-rw-r--r--src/core/hle/service/set/setting_formats/appln_settings.h (renamed from src/core/hle/service/set/appln_settings.h)17
-rw-r--r--src/core/hle/service/set/setting_formats/device_settings.cpp (renamed from src/core/hle/service/set/device_settings.cpp)2
-rw-r--r--src/core/hle/service/set/setting_formats/device_settings.h (renamed from src/core/hle/service/set/device_settings.h)16
-rw-r--r--src/core/hle/service/set/setting_formats/private_settings.cpp (renamed from src/core/hle/service/set/private_settings.cpp)2
-rw-r--r--src/core/hle/service/set/setting_formats/private_settings.h39
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.cpp (renamed from src/core/hle/service/set/system_settings.cpp)8
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.h (renamed from src/core/hle/service/set/system_settings.h)496
-rw-r--r--src/core/hle/service/set/settings_server.h61
-rw-r--r--src/core/hle/service/set/settings_types.h451
-rw-r--r--src/core/hle/service/set/system_settings_server.cpp215
-rw-r--r--src/core/hle/service/set/system_settings_server.h48
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp11
-rw-r--r--src/core/hle/service/vi/display/vi_display.h3
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h9
-rw-r--r--src/core/hle/service/vi/vi.cpp14
-rw-r--r--src/frontend_common/CMakeLists.txt1
-rw-r--r--src/frontend_common/config.cpp34
-rw-r--r--src/frontend_common/config.h1
-rw-r--r--src/frontend_common/content_manager.h238
-rw-r--r--src/hid_core/frontend/emulated_controller.cpp41
-rw-r--r--src/hid_core/frontend/emulated_controller.h20
-rw-r--r--src/hid_core/resource_manager.cpp123
-rw-r--r--src/hid_core/resource_manager.h25
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp2
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h6
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad.cpp13
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad.h6
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp42
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h12
-rw-r--r--src/hid_core/resources/applet_resource.cpp19
-rw-r--r--src/hid_core/resources/applet_resource.h1
-rw-r--r--src/hid_core/resources/hid_firmware_settings.cpp35
-rw-r--r--src/hid_core/resources/hid_firmware_settings.h12
-rw-r--r--src/hid_core/resources/npad/npad.cpp299
-rw-r--r--src/hid_core/resources/npad/npad.h59
-rw-r--r--src/hid_core/resources/npad/npad_types.h6
-rw-r--r--src/hid_core/resources/npad/npad_vibration.cpp4
-rw-r--r--src/hid_core/resources/npad/npad_vibration.h2
-rw-r--r--src/hid_core/resources/vibration/gc_vibration_device.cpp56
-rw-r--r--src/hid_core/resources/vibration/gc_vibration_device.h10
-rw-r--r--src/hid_core/resources/vibration/n64_vibration_device.cpp57
-rw-r--r--src/hid_core/resources/vibration/n64_vibration_device.h8
-rw-r--r--src/hid_core/resources/vibration/vibration_base.cpp4
-rw-r--r--src/hid_core/resources/vibration/vibration_base.h10
-rw-r--r--src/hid_core/resources/vibration/vibration_device.cpp72
-rw-r--r--src/hid_core/resources/vibration/vibration_device.h16
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/drivers/android.cpp48
-rw-r--r--src/input_common/drivers/android.h54
-rw-r--r--src/input_common/input_mapping.cpp3
-rw-r--r--src/input_common/main.cpp22
-rw-r--r--src/input_common/main.h7
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp76
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h2
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp16
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp7
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp8
-rw-r--r--src/yuzu/configuration/input_profiles.cpp3
-rw-r--r--src/yuzu/configuration/qt_config.cpp7
-rw-r--r--src/yuzu/game_list_worker.cpp11
-rw-r--r--src/yuzu/main.cpp190
-rw-r--r--src/yuzu/main.h13
-rw-r--r--src/yuzu_cmd/sdl_config.cpp4
111 files changed, 3018 insertions, 1593 deletions
diff --git a/.ci/yuzu-mainline-step2.yml b/.ci/yuzu-mainline-step2.yml
index 8bb0572f5..825be121a 100644
--- a/.ci/yuzu-mainline-step2.yml
+++ b/.ci/yuzu-mainline-step2.yml
@@ -33,7 +33,6 @@ stages:
33 cache: 'true' 33 cache: 'true'
34 version: $(DisplayVersion) 34 version: $(DisplayVersion)
35- stage: build_win 35- stage: build_win
36 dependsOn: format
37 displayName: 'build-windows' 36 displayName: 'build-windows'
38 jobs: 37 jobs:
39 - job: build 38 - job: build
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index b7556e353..1c9fb0675 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.utils.DocumentsTree
21import org.yuzu.yuzu_emu.utils.FileUtil 21import org.yuzu.yuzu_emu.utils.FileUtil
22import org.yuzu.yuzu_emu.utils.Log 22import org.yuzu.yuzu_emu.utils.Log
23import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable 23import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
24import org.yuzu.yuzu_emu.model.InstallResult
25import org.yuzu.yuzu_emu.model.Patch
24 26
25/** 27/**
26 * Class which contains methods that interact 28 * Class which contains methods that interact
@@ -235,9 +237,12 @@ object NativeLibrary {
235 /** 237 /**
236 * Installs a nsp or xci file to nand 238 * Installs a nsp or xci file to nand
237 * @param filename String representation of file uri 239 * @param filename String representation of file uri
238 * @param extension Lowercase string representation of file extension without "." 240 * @return int representation of [InstallResult]
239 */ 241 */
240 external fun installFileToNand(filename: String, extension: String): Int 242 external fun installFileToNand(
243 filename: String,
244 callback: (max: Long, progress: Long) -> Boolean
245 ): Int
241 246
242 external fun doesUpdateMatchProgram(programId: String, updatePath: String): Boolean 247 external fun doesUpdateMatchProgram(programId: String, updatePath: String): Boolean
243 248
@@ -535,9 +540,29 @@ object NativeLibrary {
535 * 540 *
536 * @param path Path to game file. Can be a [Uri]. 541 * @param path Path to game file. Can be a [Uri].
537 * @param programId String representation of a game's program ID 542 * @param programId String representation of a game's program ID
538 * @return Array of pairs where the first value is the name of an addon and the second is the version 543 * @return Array of available patches
539 */ 544 */
540 external fun getAddonsForFile(path: String, programId: String): Array<Pair<String, String>>? 545 external fun getPatchesForFile(path: String, programId: String): Array<Patch>?
546
547 /**
548 * Removes an update for a given [programId]
549 * @param programId String representation of a game's program ID
550 */
551 external fun removeUpdate(programId: String)
552
553 /**
554 * Removes all DLC for a [programId]
555 * @param programId String representation of a game's program ID
556 */
557 external fun removeDLC(programId: String)
558
559 /**
560 * Removes a mod installed for a given [programId]
561 * @param programId String representation of a game's program ID
562 * @param name The name of a mod as given by [getPatchesForFile]. This corresponds with the name
563 * of the mod's directory in a game's load folder.
564 */
565 external fun removeMod(programId: String, name: String)
541 566
542 /** 567 /**
543 * Gets the save location for a specific game 568 * Gets the save location for a specific game
@@ -609,15 +634,4 @@ object NativeLibrary {
609 const val RELEASED = 0 634 const val RELEASED = 0
610 const val PRESSED = 1 635 const val PRESSED = 1
611 } 636 }
612
613 /**
614 * Result from installFileToNand
615 */
616 object InstallFileToNandResult {
617 const val Success = 0
618 const val SuccessFileOverwritten = 1
619 const val Error = 2
620 const val ErrorBaseGame = 3
621 const val ErrorFilenameExtension = 4
622 }
623} 637}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 93c8ce922..9b08f008d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -49,7 +49,6 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
49import org.yuzu.yuzu_emu.utils.InputHandler 49import org.yuzu.yuzu_emu.utils.InputHandler
50import org.yuzu.yuzu_emu.utils.Log 50import org.yuzu.yuzu_emu.utils.Log
51import org.yuzu.yuzu_emu.utils.MemoryUtil 51import org.yuzu.yuzu_emu.utils.MemoryUtil
52import org.yuzu.yuzu_emu.utils.NativeConfig
53import org.yuzu.yuzu_emu.utils.NfcReader 52import org.yuzu.yuzu_emu.utils.NfcReader
54import org.yuzu.yuzu_emu.utils.ThemeHelper 53import org.yuzu.yuzu_emu.utils.ThemeHelper
55import java.text.NumberFormat 54import java.text.NumberFormat
@@ -171,11 +170,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
171 stopMotionSensorListener() 170 stopMotionSensorListener()
172 } 171 }
173 172
174 override fun onStop() {
175 super.onStop()
176 NativeConfig.saveGlobalConfig()
177 }
178
179 override fun onUserLeaveHint() { 173 override fun onUserLeaveHint() {
180 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 174 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
181 if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) { 175 if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
index 94c151325..ff254d9b7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
@@ -6,27 +6,32 @@ package org.yuzu.yuzu_emu.adapters
6import android.view.LayoutInflater 6import android.view.LayoutInflater
7import android.view.ViewGroup 7import android.view.ViewGroup
8import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding 8import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
9import org.yuzu.yuzu_emu.model.Addon 9import org.yuzu.yuzu_emu.model.Patch
10import org.yuzu.yuzu_emu.model.AddonViewModel
10import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 11import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
11 12
12class AddonAdapter : AbstractDiffAdapter<Addon, AddonAdapter.AddonViewHolder>() { 13class AddonAdapter(val addonViewModel: AddonViewModel) :
14 AbstractDiffAdapter<Patch, AddonAdapter.AddonViewHolder>() {
13 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder { 15 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder {
14 ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false) 16 ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
15 .also { return AddonViewHolder(it) } 17 .also { return AddonViewHolder(it) }
16 } 18 }
17 19
18 inner class AddonViewHolder(val binding: ListItemAddonBinding) : 20 inner class AddonViewHolder(val binding: ListItemAddonBinding) :
19 AbstractViewHolder<Addon>(binding) { 21 AbstractViewHolder<Patch>(binding) {
20 override fun bind(model: Addon) { 22 override fun bind(model: Patch) {
21 binding.root.setOnClickListener { 23 binding.root.setOnClickListener {
22 binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked 24 binding.addonCheckbox.isChecked = !binding.addonCheckbox.isChecked
23 } 25 }
24 binding.title.text = model.title 26 binding.title.text = model.name
25 binding.version.text = model.version 27 binding.version.text = model.version
26 binding.addonSwitch.setOnCheckedChangeListener { _, checked -> 28 binding.addonCheckbox.setOnCheckedChangeListener { _, checked ->
27 model.enabled = checked 29 model.enabled = checked
28 } 30 }
29 binding.addonSwitch.isChecked = model.enabled 31 binding.addonCheckbox.isChecked = model.enabled
32 binding.buttonDelete.setOnClickListener {
33 addonViewModel.setAddonToDelete(model)
34 }
30 } 35 }
31 } 36 }
32} 37}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
index 816336820..adb65812c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -74,7 +74,7 @@ class AddonsFragment : Fragment() {
74 74
75 binding.listAddons.apply { 75 binding.listAddons.apply {
76 layoutManager = LinearLayoutManager(requireContext()) 76 layoutManager = LinearLayoutManager(requireContext())
77 adapter = AddonAdapter() 77 adapter = AddonAdapter(addonViewModel)
78 } 78 }
79 79
80 viewLifecycleOwner.lifecycleScope.apply { 80 viewLifecycleOwner.lifecycleScope.apply {
@@ -110,6 +110,21 @@ class AddonsFragment : Fragment() {
110 } 110 }
111 } 111 }
112 } 112 }
113 launch {
114 repeatOnLifecycle(Lifecycle.State.STARTED) {
115 addonViewModel.addonToDelete.collect {
116 if (it != null) {
117 MessageDialogFragment.newInstance(
118 requireActivity(),
119 titleId = R.string.confirm_uninstall,
120 descriptionId = R.string.confirm_uninstall_description,
121 positiveAction = { addonViewModel.onDeleteAddon(it) }
122 ).show(parentFragmentManager, MessageDialogFragment.TAG)
123 addonViewModel.setAddonToDelete(null)
124 }
125 }
126 }
127 }
113 } 128 }
114 129
115 binding.buttonInstall.setOnClickListener { 130 binding.buttonInstall.setOnClickListener {
@@ -156,22 +171,22 @@ class AddonsFragment : Fragment() {
156 descriptionId = R.string.invalid_directory_description 171 descriptionId = R.string.invalid_directory_description
157 ) 172 )
158 if (isValid) { 173 if (isValid) {
159 IndeterminateProgressDialogFragment.newInstance( 174 ProgressDialogFragment.newInstance(
160 requireActivity(), 175 requireActivity(),
161 R.string.installing_game_content, 176 R.string.installing_game_content,
162 false 177 false
163 ) { 178 ) { progressCallback, _ ->
164 val parentDirectoryName = externalAddonDirectory.name 179 val parentDirectoryName = externalAddonDirectory.name
165 val internalAddonDirectory = 180 val internalAddonDirectory =
166 File(args.game.addonDir + parentDirectoryName) 181 File(args.game.addonDir + parentDirectoryName)
167 try { 182 try {
168 externalAddonDirectory.copyFilesTo(internalAddonDirectory) 183 externalAddonDirectory.copyFilesTo(internalAddonDirectory, progressCallback)
169 } catch (_: Exception) { 184 } catch (_: Exception) {
170 return@newInstance errorMessage 185 return@newInstance errorMessage
171 } 186 }
172 addonViewModel.refreshAddons() 187 addonViewModel.refreshAddons()
173 return@newInstance getString(R.string.addon_installed_successfully) 188 return@newInstance getString(R.string.addon_installed_successfully)
174 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG) 189 }.show(parentFragmentManager, ProgressDialogFragment.TAG)
175 } else { 190 } else {
176 errorMessage.show(parentFragmentManager, MessageDialogFragment.TAG) 191 errorMessage.show(parentFragmentManager, MessageDialogFragment.TAG)
177 } 192 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index 9dabb9c41..6c758d80b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -173,11 +173,11 @@ class DriverManagerFragment : Fragment() {
173 return@registerForActivityResult 173 return@registerForActivityResult
174 } 174 }
175 175
176 IndeterminateProgressDialogFragment.newInstance( 176 ProgressDialogFragment.newInstance(
177 requireActivity(), 177 requireActivity(),
178 R.string.installing_driver, 178 R.string.installing_driver,
179 false 179 false
180 ) { 180 ) { _, _ ->
181 val driverPath = 181 val driverPath =
182 "${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}" 182 "${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
183 val driverFile = File(driverPath) 183 val driverFile = File(driverPath)
@@ -213,6 +213,6 @@ class DriverManagerFragment : Fragment() {
213 } 213 }
214 } 214 }
215 return@newInstance Any() 215 return@newInstance Any()
216 }.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG) 216 }.show(childFragmentManager, ProgressDialogFragment.TAG)
217 } 217 }
218} 218}
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 9efc1705d..47767454a 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
@@ -554,6 +554,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
554 findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean() 554 findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
555 } 555 }
556 556
557 popup.setOnDismissListener { NativeConfig.saveGlobalConfig() }
557 popup.setOnMenuItemClickListener { 558 popup.setOnMenuItemClickListener {
558 when (it.itemId) { 559 when (it.itemId) {
559 R.id.menu_toggle_fps -> { 560 R.id.menu_toggle_fps -> {
@@ -720,7 +721,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
720 MaterialAlertDialogBuilder(requireContext()) 721 MaterialAlertDialogBuilder(requireContext())
721 .setTitle(R.string.emulation_control_adjust) 722 .setTitle(R.string.emulation_control_adjust)
722 .setView(adjustBinding.root) 723 .setView(adjustBinding.root)
723 .setPositiveButton(android.R.string.ok, null) 724 .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
725 NativeConfig.saveGlobalConfig()
726 }
724 .setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int -> 727 .setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int ->
725 setControlScale(50) 728 setControlScale(50)
726 setControlOpacity(100) 729 setControlOpacity(100)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index b04d1208f..83a845434 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -44,7 +44,6 @@ import org.yuzu.yuzu_emu.utils.FileUtil
44import org.yuzu.yuzu_emu.utils.GameIconUtils 44import org.yuzu.yuzu_emu.utils.GameIconUtils
45import org.yuzu.yuzu_emu.utils.GpuDriverHelper 45import org.yuzu.yuzu_emu.utils.GpuDriverHelper
46import org.yuzu.yuzu_emu.utils.MemoryUtil 46import org.yuzu.yuzu_emu.utils.MemoryUtil
47import java.io.BufferedInputStream
48import java.io.BufferedOutputStream 47import java.io.BufferedOutputStream
49import java.io.File 48import java.io.File
50 49
@@ -357,27 +356,17 @@ class GamePropertiesFragment : Fragment() {
357 return@registerForActivityResult 356 return@registerForActivityResult
358 } 357 }
359 358
360 val inputZip = requireContext().contentResolver.openInputStream(result)
361 val savesFolder = File(args.game.saveDir) 359 val savesFolder = File(args.game.saveDir)
362 val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/") 360 val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
363 cacheSaveDir.mkdir() 361 cacheSaveDir.mkdir()
364 362
365 if (inputZip == null) { 363 ProgressDialogFragment.newInstance(
366 Toast.makeText(
367 YuzuApplication.appContext,
368 getString(R.string.fatal_error),
369 Toast.LENGTH_LONG
370 ).show()
371 return@registerForActivityResult
372 }
373
374 IndeterminateProgressDialogFragment.newInstance(
375 requireActivity(), 364 requireActivity(),
376 R.string.save_files_importing, 365 R.string.save_files_importing,
377 false 366 false
378 ) { 367 ) { _, _ ->
379 try { 368 try {
380 FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir) 369 FileUtil.unzipToInternalStorage(result.toString(), cacheSaveDir)
381 val files = cacheSaveDir.listFiles() 370 val files = cacheSaveDir.listFiles()
382 var savesFolderFile: File? = null 371 var savesFolderFile: File? = null
383 if (files != null) { 372 if (files != null) {
@@ -422,7 +411,7 @@ class GamePropertiesFragment : Fragment() {
422 Toast.LENGTH_LONG 411 Toast.LENGTH_LONG
423 ).show() 412 ).show()
424 } 413 }
425 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG) 414 }.show(parentFragmentManager, ProgressDialogFragment.TAG)
426 } 415 }
427 416
428 /** 417 /**
@@ -436,11 +425,11 @@ class GamePropertiesFragment : Fragment() {
436 return@registerForActivityResult 425 return@registerForActivityResult
437 } 426 }
438 427
439 IndeterminateProgressDialogFragment.newInstance( 428 ProgressDialogFragment.newInstance(
440 requireActivity(), 429 requireActivity(),
441 R.string.save_files_exporting, 430 R.string.save_files_exporting,
442 false 431 false
443 ) { 432 ) { _, _ ->
444 val saveLocation = args.game.saveDir 433 val saveLocation = args.game.saveDir
445 val zipResult = FileUtil.zipFromInternalStorage( 434 val zipResult = FileUtil.zipFromInternalStorage(
446 File(saveLocation), 435 File(saveLocation),
@@ -452,6 +441,6 @@ class GamePropertiesFragment : Fragment() {
452 TaskState.Completed -> getString(R.string.export_success) 441 TaskState.Completed -> getString(R.string.export_success)
453 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed) 442 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
454 } 443 }
455 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG) 444 }.show(parentFragmentManager, ProgressDialogFragment.TAG)
456 } 445 }
457} 446}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index 5b4bf2c9f..7df8e6bf4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -34,7 +34,6 @@ import org.yuzu.yuzu_emu.model.TaskState
34import org.yuzu.yuzu_emu.ui.main.MainActivity 34import org.yuzu.yuzu_emu.ui.main.MainActivity
35import org.yuzu.yuzu_emu.utils.DirectoryInitialization 35import org.yuzu.yuzu_emu.utils.DirectoryInitialization
36import org.yuzu.yuzu_emu.utils.FileUtil 36import org.yuzu.yuzu_emu.utils.FileUtil
37import java.io.BufferedInputStream
38import java.io.BufferedOutputStream 37import java.io.BufferedOutputStream
39import java.io.File 38import java.io.File
40import java.math.BigInteger 39import java.math.BigInteger
@@ -195,26 +194,20 @@ class InstallableFragment : Fragment() {
195 return@registerForActivityResult 194 return@registerForActivityResult
196 } 195 }
197 196
198 val inputZip = requireContext().contentResolver.openInputStream(result)
199 val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/") 197 val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
200 cacheSaveDir.mkdir() 198 cacheSaveDir.mkdir()
201 199
202 if (inputZip == null) { 200 ProgressDialogFragment.newInstance(
203 Toast.makeText(
204 YuzuApplication.appContext,
205 getString(R.string.fatal_error),
206 Toast.LENGTH_LONG
207 ).show()
208 return@registerForActivityResult
209 }
210
211 IndeterminateProgressDialogFragment.newInstance(
212 requireActivity(), 201 requireActivity(),
213 R.string.save_files_importing, 202 R.string.save_files_importing,
214 false 203 false
215 ) { 204 ) { progressCallback, _ ->
216 try { 205 try {
217 FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir) 206 FileUtil.unzipToInternalStorage(
207 result.toString(),
208 cacheSaveDir,
209 progressCallback
210 )
218 val files = cacheSaveDir.listFiles() 211 val files = cacheSaveDir.listFiles()
219 var successfulImports = 0 212 var successfulImports = 0
220 var failedImports = 0 213 var failedImports = 0
@@ -287,7 +280,7 @@ class InstallableFragment : Fragment() {
287 Toast.LENGTH_LONG 280 Toast.LENGTH_LONG
288 ).show() 281 ).show()
289 } 282 }
290 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG) 283 }.show(parentFragmentManager, ProgressDialogFragment.TAG)
291 } 284 }
292 285
293 private val exportSaves = registerForActivityResult( 286 private val exportSaves = registerForActivityResult(
@@ -297,11 +290,11 @@ class InstallableFragment : Fragment() {
297 return@registerForActivityResult 290 return@registerForActivityResult
298 } 291 }
299 292
300 IndeterminateProgressDialogFragment.newInstance( 293 ProgressDialogFragment.newInstance(
301 requireActivity(), 294 requireActivity(),
302 R.string.save_files_exporting, 295 R.string.save_files_exporting,
303 false 296 false
304 ) { 297 ) { _, _ ->
305 val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/") 298 val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
306 cacheSaveDir.mkdir() 299 cacheSaveDir.mkdir()
307 300
@@ -338,6 +331,6 @@ class InstallableFragment : Fragment() {
338 TaskState.Completed -> getString(R.string.export_success) 331 TaskState.Completed -> getString(R.string.export_success)
339 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed) 332 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
340 } 333 }
341 }.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG) 334 }.show(parentFragmentManager, ProgressDialogFragment.TAG)
342 } 335 }
343} 336}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
index 8847e5531..d201cb80c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
@@ -23,11 +23,13 @@ import org.yuzu.yuzu_emu.R
23import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 23import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
24import org.yuzu.yuzu_emu.model.TaskViewModel 24import org.yuzu.yuzu_emu.model.TaskViewModel
25 25
26class IndeterminateProgressDialogFragment : DialogFragment() { 26class ProgressDialogFragment : DialogFragment() {
27 private val taskViewModel: TaskViewModel by activityViewModels() 27 private val taskViewModel: TaskViewModel by activityViewModels()
28 28
29 private lateinit var binding: DialogProgressBarBinding 29 private lateinit var binding: DialogProgressBarBinding
30 30
31 private val PROGRESS_BAR_RESOLUTION = 1000
32
31 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 33 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
32 val titleId = requireArguments().getInt(TITLE) 34 val titleId = requireArguments().getInt(TITLE)
33 val cancellable = requireArguments().getBoolean(CANCELLABLE) 35 val cancellable = requireArguments().getBoolean(CANCELLABLE)
@@ -61,6 +63,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
61 63
62 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 64 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
63 super.onViewCreated(view, savedInstanceState) 65 super.onViewCreated(view, savedInstanceState)
66 binding.message.isSelected = true
64 viewLifecycleOwner.lifecycleScope.apply { 67 viewLifecycleOwner.lifecycleScope.apply {
65 launch { 68 launch {
66 repeatOnLifecycle(Lifecycle.State.CREATED) { 69 repeatOnLifecycle(Lifecycle.State.CREATED) {
@@ -97,6 +100,35 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
97 } 100 }
98 } 101 }
99 } 102 }
103 launch {
104 repeatOnLifecycle(Lifecycle.State.CREATED) {
105 taskViewModel.progress.collect {
106 if (it != 0.0) {
107 binding.progressBar.apply {
108 isIndeterminate = false
109 progress = (
110 (it / taskViewModel.maxProgress.value) *
111 PROGRESS_BAR_RESOLUTION
112 ).toInt()
113 min = 0
114 max = PROGRESS_BAR_RESOLUTION
115 }
116 }
117 }
118 }
119 }
120 launch {
121 repeatOnLifecycle(Lifecycle.State.CREATED) {
122 taskViewModel.message.collect {
123 if (it.isEmpty()) {
124 binding.message.visibility = View.GONE
125 } else {
126 binding.message.visibility = View.VISIBLE
127 binding.message.text = it
128 }
129 }
130 }
131 }
100 } 132 }
101 } 133 }
102 134
@@ -108,6 +140,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
108 val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE) 140 val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE)
109 negativeButton.setOnClickListener { 141 negativeButton.setOnClickListener {
110 alertDialog.setTitle(getString(R.string.cancelling)) 142 alertDialog.setTitle(getString(R.string.cancelling))
143 binding.progressBar.isIndeterminate = true
111 taskViewModel.setCancelled(true) 144 taskViewModel.setCancelled(true)
112 } 145 }
113 } 146 }
@@ -122,9 +155,12 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
122 activity: FragmentActivity, 155 activity: FragmentActivity,
123 titleId: Int, 156 titleId: Int,
124 cancellable: Boolean = false, 157 cancellable: Boolean = false,
125 task: suspend () -> Any 158 task: suspend (
126 ): IndeterminateProgressDialogFragment { 159 progressCallback: (max: Long, progress: Long) -> Boolean,
127 val dialog = IndeterminateProgressDialogFragment() 160 messageCallback: (message: String) -> Unit
161 ) -> Any
162 ): ProgressDialogFragment {
163 val dialog = ProgressDialogFragment()
128 val args = Bundle() 164 val args = Bundle()
129 ViewModelProvider(activity)[TaskViewModel::class.java].task = task 165 ViewModelProvider(activity)[TaskViewModel::class.java].task = task
130 args.putInt(TITLE, titleId) 166 args.putInt(TITLE, titleId)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index 64b295fbd..20b10b1a0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -136,14 +136,14 @@ class SearchFragment : Fragment() {
136 baseList.filter { 136 baseList.filter {
137 val lastPlayedTime = preferences.getLong(it.keyLastPlayedTime, 0L) 137 val lastPlayedTime = preferences.getLong(it.keyLastPlayedTime, 0L)
138 lastPlayedTime > (System.currentTimeMillis() - 24 * 60 * 60 * 1000) 138 lastPlayedTime > (System.currentTimeMillis() - 24 * 60 * 60 * 1000)
139 } 139 }.sortedByDescending { preferences.getLong(it.keyLastPlayedTime, 0L) }
140 } 140 }
141 141
142 R.id.chip_recently_added -> { 142 R.id.chip_recently_added -> {
143 baseList.filter { 143 baseList.filter {
144 val addedTime = preferences.getLong(it.keyAddedToLibraryTime, 0L) 144 val addedTime = preferences.getLong(it.keyAddedToLibraryTime, 0L)
145 addedTime > (System.currentTimeMillis() - 24 * 60 * 60 * 1000) 145 addedTime > (System.currentTimeMillis() - 24 * 60 * 60 * 1000)
146 } 146 }.sortedByDescending { preferences.getLong(it.keyAddedToLibraryTime, 0L) }
147 } 147 }
148 148
149 R.id.chip_homebrew -> baseList.filter { it.isHomebrew } 149 R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Addon.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Addon.kt
deleted file mode 100644
index ed79a8b02..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Addon.kt
+++ /dev/null
@@ -1,10 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6data class Addon(
7 var enabled: Boolean,
8 val title: String,
9 val version: String
10)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt
index 075252f5b..b9c8e49ca 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/AddonViewModel.kt
@@ -15,8 +15,8 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
15import java.util.concurrent.atomic.AtomicBoolean 15import java.util.concurrent.atomic.AtomicBoolean
16 16
17class AddonViewModel : ViewModel() { 17class AddonViewModel : ViewModel() {
18 private val _addonList = MutableStateFlow(mutableListOf<Addon>()) 18 private val _patchList = MutableStateFlow(mutableListOf<Patch>())
19 val addonList get() = _addonList.asStateFlow() 19 val addonList get() = _patchList.asStateFlow()
20 20
21 private val _showModInstallPicker = MutableStateFlow(false) 21 private val _showModInstallPicker = MutableStateFlow(false)
22 val showModInstallPicker get() = _showModInstallPicker.asStateFlow() 22 val showModInstallPicker get() = _showModInstallPicker.asStateFlow()
@@ -24,6 +24,9 @@ class AddonViewModel : ViewModel() {
24 private val _showModNoticeDialog = MutableStateFlow(false) 24 private val _showModNoticeDialog = MutableStateFlow(false)
25 val showModNoticeDialog get() = _showModNoticeDialog.asStateFlow() 25 val showModNoticeDialog get() = _showModNoticeDialog.asStateFlow()
26 26
27 private val _addonToDelete = MutableStateFlow<Patch?>(null)
28 val addonToDelete = _addonToDelete.asStateFlow()
29
27 var game: Game? = null 30 var game: Game? = null
28 31
29 private val isRefreshing = AtomicBoolean(false) 32 private val isRefreshing = AtomicBoolean(false)
@@ -40,36 +43,47 @@ class AddonViewModel : ViewModel() {
40 isRefreshing.set(true) 43 isRefreshing.set(true)
41 viewModelScope.launch { 44 viewModelScope.launch {
42 withContext(Dispatchers.IO) { 45 withContext(Dispatchers.IO) {
43 val addonList = mutableListOf<Addon>() 46 val patchList = (
44 val disabledAddons = NativeConfig.getDisabledAddons(game!!.programId) 47 NativeLibrary.getPatchesForFile(game!!.path, game!!.programId)
45 NativeLibrary.getAddonsForFile(game!!.path, game!!.programId)?.forEach { 48 ?: emptyArray()
46 val name = it.first.replace("[D] ", "") 49 ).toMutableList()
47 addonList.add(Addon(!disabledAddons.contains(name), name, it.second)) 50 patchList.sortBy { it.name }
48 } 51 _patchList.value = patchList
49 addonList.sortBy { it.title }
50 _addonList.value = addonList
51 isRefreshing.set(false) 52 isRefreshing.set(false)
52 } 53 }
53 } 54 }
54 } 55 }
55 56
57 fun setAddonToDelete(patch: Patch?) {
58 _addonToDelete.value = patch
59 }
60
61 fun onDeleteAddon(patch: Patch) {
62 when (PatchType.from(patch.type)) {
63 PatchType.Update -> NativeLibrary.removeUpdate(patch.programId)
64 PatchType.DLC -> NativeLibrary.removeDLC(patch.programId)
65 PatchType.Mod -> NativeLibrary.removeMod(patch.programId, patch.name)
66 }
67 refreshAddons()
68 }
69
56 fun onCloseAddons() { 70 fun onCloseAddons() {
57 if (_addonList.value.isEmpty()) { 71 if (_patchList.value.isEmpty()) {
58 return 72 return
59 } 73 }
60 74
61 NativeConfig.setDisabledAddons( 75 NativeConfig.setDisabledAddons(
62 game!!.programId, 76 game!!.programId,
63 _addonList.value.mapNotNull { 77 _patchList.value.mapNotNull {
64 if (it.enabled) { 78 if (it.enabled) {
65 null 79 null
66 } else { 80 } else {
67 it.title 81 it.name
68 } 82 }
69 }.toTypedArray() 83 }.toTypedArray()
70 ) 84 )
71 NativeConfig.saveGlobalConfig() 85 NativeConfig.saveGlobalConfig()
72 _addonList.value.clear() 86 _patchList.value.clear()
73 game = null 87 game = null
74 } 88 }
75 89
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt
new file mode 100644
index 000000000..0c3cd0521
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt
@@ -0,0 +1,15 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6enum class InstallResult(val int: Int) {
7 Success(0),
8 Overwrite(1),
9 Failure(2),
10 BaseInstallAttempted(3);
11
12 companion object {
13 fun from(int: Int): InstallResult = entries.firstOrNull { it.int == int } ?: Success
14 }
15}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Patch.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Patch.kt
new file mode 100644
index 000000000..25cb9e365
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Patch.kt
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6import androidx.annotation.Keep
7
8@Keep
9data class Patch(
10 var enabled: Boolean,
11 val name: String,
12 val version: String,
13 val type: Int,
14 val programId: String,
15 val titleId: String
16)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/PatchType.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/PatchType.kt
new file mode 100644
index 000000000..e9a54162b
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/PatchType.kt
@@ -0,0 +1,14 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6enum class PatchType(val int: Int) {
7 Update(0),
8 DLC(1),
9 Mod(2);
10
11 companion object {
12 fun from(int: Int): PatchType = entries.firstOrNull { it.int == int } ?: Update
13 }
14}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
index e59c95733..4361eb972 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt
@@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
8import kotlinx.coroutines.Dispatchers 8import kotlinx.coroutines.Dispatchers
9import kotlinx.coroutines.flow.MutableStateFlow 9import kotlinx.coroutines.flow.MutableStateFlow
10import kotlinx.coroutines.flow.StateFlow 10import kotlinx.coroutines.flow.StateFlow
11import kotlinx.coroutines.flow.asStateFlow
11import kotlinx.coroutines.launch 12import kotlinx.coroutines.launch
12 13
13class TaskViewModel : ViewModel() { 14class TaskViewModel : ViewModel() {
@@ -23,13 +24,28 @@ class TaskViewModel : ViewModel() {
23 val cancelled: StateFlow<Boolean> get() = _cancelled 24 val cancelled: StateFlow<Boolean> get() = _cancelled
24 private val _cancelled = MutableStateFlow(false) 25 private val _cancelled = MutableStateFlow(false)
25 26
26 lateinit var task: suspend () -> Any 27 private val _progress = MutableStateFlow(0.0)
28 val progress = _progress.asStateFlow()
29
30 private val _maxProgress = MutableStateFlow(0.0)
31 val maxProgress = _maxProgress.asStateFlow()
32
33 private val _message = MutableStateFlow("")
34 val message = _message.asStateFlow()
35
36 lateinit var task: suspend (
37 progressCallback: (max: Long, progress: Long) -> Boolean,
38 messageCallback: (message: String) -> Unit
39 ) -> Any
27 40
28 fun clear() { 41 fun clear() {
29 _result.value = Any() 42 _result.value = Any()
30 _isComplete.value = false 43 _isComplete.value = false
31 _isRunning.value = false 44 _isRunning.value = false
32 _cancelled.value = false 45 _cancelled.value = false
46 _progress.value = 0.0
47 _maxProgress.value = 0.0
48 _message.value = ""
33 } 49 }
34 50
35 fun setCancelled(value: Boolean) { 51 fun setCancelled(value: Boolean) {
@@ -43,7 +59,16 @@ class TaskViewModel : ViewModel() {
43 _isRunning.value = true 59 _isRunning.value = true
44 60
45 viewModelScope.launch(Dispatchers.IO) { 61 viewModelScope.launch(Dispatchers.IO) {
46 val res = task() 62 val res = task(
63 { max, progress ->
64 _maxProgress.value = max.toDouble()
65 _progress.value = progress.toDouble()
66 return@task cancelled.value
67 },
68 { message ->
69 _message.value = message
70 }
71 )
47 _result.value = res 72 _result.value = res
48 _isComplete.value = true 73 _isComplete.value = true
49 _isRunning.value = false 74 _isRunning.value = false
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 644289e25..c2cc29961 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -38,12 +38,13 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
38import org.yuzu.yuzu_emu.databinding.ActivityMainBinding 38import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
39import org.yuzu.yuzu_emu.features.settings.model.Settings 39import org.yuzu.yuzu_emu.features.settings.model.Settings
40import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment 40import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
41import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 41import org.yuzu.yuzu_emu.fragments.ProgressDialogFragment
42import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 42import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
43import org.yuzu.yuzu_emu.model.AddonViewModel 43import org.yuzu.yuzu_emu.model.AddonViewModel
44import org.yuzu.yuzu_emu.model.DriverViewModel 44import org.yuzu.yuzu_emu.model.DriverViewModel
45import org.yuzu.yuzu_emu.model.GamesViewModel 45import org.yuzu.yuzu_emu.model.GamesViewModel
46import org.yuzu.yuzu_emu.model.HomeViewModel 46import org.yuzu.yuzu_emu.model.HomeViewModel
47import org.yuzu.yuzu_emu.model.InstallResult
47import org.yuzu.yuzu_emu.model.TaskState 48import org.yuzu.yuzu_emu.model.TaskState
48import org.yuzu.yuzu_emu.model.TaskViewModel 49import org.yuzu.yuzu_emu.model.TaskViewModel
49import org.yuzu.yuzu_emu.utils.* 50import org.yuzu.yuzu_emu.utils.*
@@ -369,26 +370,23 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
369 return@registerForActivityResult 370 return@registerForActivityResult
370 } 371 }
371 372
372 val inputZip = contentResolver.openInputStream(result)
373 if (inputZip == null) {
374 Toast.makeText(
375 applicationContext,
376 getString(R.string.fatal_error),
377 Toast.LENGTH_LONG
378 ).show()
379 return@registerForActivityResult
380 }
381
382 val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") } 373 val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
383 374
384 val firmwarePath = 375 val firmwarePath =
385 File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/") 376 File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
386 val cacheFirmwareDir = File("${cacheDir.path}/registered/") 377 val cacheFirmwareDir = File("${cacheDir.path}/registered/")
387 378
388 val task: () -> Any = { 379 ProgressDialogFragment.newInstance(
380 this,
381 R.string.firmware_installing
382 ) { progressCallback, _ ->
389 var messageToShow: Any 383 var messageToShow: Any
390 try { 384 try {
391 FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheFirmwareDir) 385 FileUtil.unzipToInternalStorage(
386 result.toString(),
387 cacheFirmwareDir,
388 progressCallback
389 )
392 val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1 390 val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
393 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 391 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
394 messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) { 392 messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
@@ -404,18 +402,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
404 getString(R.string.save_file_imported_success) 402 getString(R.string.save_file_imported_success)
405 } 403 }
406 } catch (e: Exception) { 404 } catch (e: Exception) {
405 Log.error("[MainActivity] Firmware install failed - ${e.message}")
407 messageToShow = getString(R.string.fatal_error) 406 messageToShow = getString(R.string.fatal_error)
408 } finally { 407 } finally {
409 cacheFirmwareDir.deleteRecursively() 408 cacheFirmwareDir.deleteRecursively()
410 } 409 }
411 messageToShow 410 messageToShow
412 } 411 }.show(supportFragmentManager, ProgressDialogFragment.TAG)
413
414 IndeterminateProgressDialogFragment.newInstance(
415 this,
416 R.string.firmware_installing,
417 task = task
418 ).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
419 } 412 }
420 413
421 val getAmiiboKey = 414 val getAmiiboKey =
@@ -474,11 +467,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
474 return@registerForActivityResult 467 return@registerForActivityResult
475 } 468 }
476 469
477 IndeterminateProgressDialogFragment.newInstance( 470 ProgressDialogFragment.newInstance(
478 this@MainActivity, 471 this@MainActivity,
479 R.string.verifying_content, 472 R.string.verifying_content,
480 false 473 false
481 ) { 474 ) { _, _ ->
482 var updatesMatchProgram = true 475 var updatesMatchProgram = true
483 for (document in documents) { 476 for (document in documents) {
484 val valid = NativeLibrary.doesUpdateMatchProgram( 477 val valid = NativeLibrary.doesUpdateMatchProgram(
@@ -501,44 +494,42 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
501 positiveAction = { homeViewModel.setContentToInstall(documents) } 494 positiveAction = { homeViewModel.setContentToInstall(documents) }
502 ) 495 )
503 } 496 }
504 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) 497 }.show(supportFragmentManager, ProgressDialogFragment.TAG)
505 } 498 }
506 499
507 private fun installContent(documents: List<Uri>) { 500 private fun installContent(documents: List<Uri>) {
508 IndeterminateProgressDialogFragment.newInstance( 501 ProgressDialogFragment.newInstance(
509 this@MainActivity, 502 this@MainActivity,
510 R.string.installing_game_content 503 R.string.installing_game_content
511 ) { 504 ) { progressCallback, messageCallback ->
512 var installSuccess = 0 505 var installSuccess = 0
513 var installOverwrite = 0 506 var installOverwrite = 0
514 var errorBaseGame = 0 507 var errorBaseGame = 0
515 var errorExtension = 0 508 var error = 0
516 var errorOther = 0
517 documents.forEach { 509 documents.forEach {
510 messageCallback.invoke(FileUtil.getFilename(it))
518 when ( 511 when (
519 NativeLibrary.installFileToNand( 512 InstallResult.from(
520 it.toString(), 513 NativeLibrary.installFileToNand(
521 FileUtil.getExtension(it) 514 it.toString(),
515 progressCallback
516 )
522 ) 517 )
523 ) { 518 ) {
524 NativeLibrary.InstallFileToNandResult.Success -> { 519 InstallResult.Success -> {
525 installSuccess += 1 520 installSuccess += 1
526 } 521 }
527 522
528 NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> { 523 InstallResult.Overwrite -> {
529 installOverwrite += 1 524 installOverwrite += 1
530 } 525 }
531 526
532 NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> { 527 InstallResult.BaseInstallAttempted -> {
533 errorBaseGame += 1 528 errorBaseGame += 1
534 } 529 }
535 530
536 NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> { 531 InstallResult.Failure -> {
537 errorExtension += 1 532 error += 1
538 }
539
540 else -> {
541 errorOther += 1
542 } 533 }
543 } 534 }
544 } 535 }
@@ -565,7 +556,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
565 ) 556 )
566 installResult.append(separator) 557 installResult.append(separator)
567 } 558 }
568 val errorTotal: Int = errorBaseGame + errorExtension + errorOther 559 val errorTotal: Int = errorBaseGame + error
569 if (errorTotal > 0) { 560 if (errorTotal > 0) {
570 installResult.append(separator) 561 installResult.append(separator)
571 installResult.append( 562 installResult.append(
@@ -582,14 +573,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
582 ) 573 )
583 installResult.append(separator) 574 installResult.append(separator)
584 } 575 }
585 if (errorExtension > 0) { 576 if (error > 0) {
586 installResult.append(separator)
587 installResult.append(
588 getString(R.string.install_game_content_failure_file_extension)
589 )
590 installResult.append(separator)
591 }
592 if (errorOther > 0) {
593 installResult.append( 577 installResult.append(
594 getString(R.string.install_game_content_failure_description) 578 getString(R.string.install_game_content_failure_description)
595 ) 579 )
@@ -608,7 +592,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
608 descriptionString = installResult.toString().trim() 592 descriptionString = installResult.toString().trim()
609 ) 593 )
610 } 594 }
611 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) 595 }.show(supportFragmentManager, ProgressDialogFragment.TAG)
612 } 596 }
613 597
614 val exportUserData = registerForActivityResult( 598 val exportUserData = registerForActivityResult(
@@ -618,16 +602,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
618 return@registerForActivityResult 602 return@registerForActivityResult
619 } 603 }
620 604
621 IndeterminateProgressDialogFragment.newInstance( 605 ProgressDialogFragment.newInstance(
622 this, 606 this,
623 R.string.exporting_user_data, 607 R.string.exporting_user_data,
624 true 608 true
625 ) { 609 ) { progressCallback, _ ->
626 val zipResult = FileUtil.zipFromInternalStorage( 610 val zipResult = FileUtil.zipFromInternalStorage(
627 File(DirectoryInitialization.userDirectory!!), 611 File(DirectoryInitialization.userDirectory!!),
628 DirectoryInitialization.userDirectory!!, 612 DirectoryInitialization.userDirectory!!,
629 BufferedOutputStream(contentResolver.openOutputStream(result)), 613 BufferedOutputStream(contentResolver.openOutputStream(result)),
630 taskViewModel.cancelled, 614 progressCallback,
631 compression = false 615 compression = false
632 ) 616 )
633 return@newInstance when (zipResult) { 617 return@newInstance when (zipResult) {
@@ -635,7 +619,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
635 TaskState.Failed -> R.string.export_failed 619 TaskState.Failed -> R.string.export_failed
636 TaskState.Cancelled -> R.string.user_data_export_cancelled 620 TaskState.Cancelled -> R.string.user_data_export_cancelled
637 } 621 }
638 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) 622 }.show(supportFragmentManager, ProgressDialogFragment.TAG)
639 } 623 }
640 624
641 val importUserData = 625 val importUserData =
@@ -644,10 +628,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
644 return@registerForActivityResult 628 return@registerForActivityResult
645 } 629 }
646 630
647 IndeterminateProgressDialogFragment.newInstance( 631 ProgressDialogFragment.newInstance(
648 this, 632 this,
649 R.string.importing_user_data 633 R.string.importing_user_data
650 ) { 634 ) { progressCallback, _ ->
651 val checkStream = 635 val checkStream =
652 ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result))) 636 ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
653 var isYuzuBackup = false 637 var isYuzuBackup = false
@@ -676,8 +660,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
676 // Copy archive to internal storage 660 // Copy archive to internal storage
677 try { 661 try {
678 FileUtil.unzipToInternalStorage( 662 FileUtil.unzipToInternalStorage(
679 BufferedInputStream(contentResolver.openInputStream(result)), 663 result.toString(),
680 File(DirectoryInitialization.userDirectory!!) 664 File(DirectoryInitialization.userDirectory!!),
665 progressCallback
681 ) 666 )
682 } catch (e: Exception) { 667 } catch (e: Exception) {
683 return@newInstance MessageDialogFragment.newInstance( 668 return@newInstance MessageDialogFragment.newInstance(
@@ -694,6 +679,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
694 driverViewModel.reloadDriverData() 679 driverViewModel.reloadDriverData()
695 680
696 return@newInstance getString(R.string.user_data_import_success) 681 return@newInstance getString(R.string.user_data_import_success)
697 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) 682 }.show(supportFragmentManager, ProgressDialogFragment.TAG)
698 } 683 }
699} 684}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
index b54a19c65..fc2339f5a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
@@ -7,7 +7,6 @@ import android.database.Cursor
7import android.net.Uri 7import android.net.Uri
8import android.provider.DocumentsContract 8import android.provider.DocumentsContract
9import androidx.documentfile.provider.DocumentFile 9import androidx.documentfile.provider.DocumentFile
10import kotlinx.coroutines.flow.StateFlow
11import java.io.BufferedInputStream 10import java.io.BufferedInputStream
12import java.io.File 11import java.io.File
13import java.io.IOException 12import java.io.IOException
@@ -19,6 +18,7 @@ import org.yuzu.yuzu_emu.YuzuApplication
19import org.yuzu.yuzu_emu.model.MinimalDocumentFile 18import org.yuzu.yuzu_emu.model.MinimalDocumentFile
20import org.yuzu.yuzu_emu.model.TaskState 19import org.yuzu.yuzu_emu.model.TaskState
21import java.io.BufferedOutputStream 20import java.io.BufferedOutputStream
21import java.io.OutputStream
22import java.lang.NullPointerException 22import java.lang.NullPointerException
23import java.nio.charset.StandardCharsets 23import java.nio.charset.StandardCharsets
24import java.util.zip.Deflater 24import java.util.zip.Deflater
@@ -283,12 +283,34 @@ object FileUtil {
283 283
284 /** 284 /**
285 * Extracts the given zip file into the given directory. 285 * Extracts the given zip file into the given directory.
286 * @param path String representation of a [Uri] or a typical path delimited by '/'
287 * @param destDir Location to unzip the contents of [path] into
288 * @param progressCallback Lambda that is called with the total number of files and the current
289 * progress through the process. Stops execution as soon as possible if this returns true.
286 */ 290 */
287 @Throws(SecurityException::class) 291 @Throws(SecurityException::class)
288 fun unzipToInternalStorage(zipStream: BufferedInputStream, destDir: File) { 292 fun unzipToInternalStorage(
289 ZipInputStream(zipStream).use { zis -> 293 path: String,
294 destDir: File,
295 progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false }
296 ) {
297 var totalEntries = 0L
298 ZipInputStream(getInputStream(path)).use { zis ->
299 var tempEntry = zis.nextEntry
300 while (tempEntry != null) {
301 tempEntry = zis.nextEntry
302 totalEntries++
303 }
304 }
305
306 var progress = 0L
307 ZipInputStream(getInputStream(path)).use { zis ->
290 var entry: ZipEntry? = zis.nextEntry 308 var entry: ZipEntry? = zis.nextEntry
291 while (entry != null) { 309 while (entry != null) {
310 if (progressCallback.invoke(totalEntries, progress)) {
311 return@use
312 }
313
292 val newFile = File(destDir, entry.name) 314 val newFile = File(destDir, entry.name)
293 val destinationDirectory = if (entry.isDirectory) newFile else newFile.parentFile 315 val destinationDirectory = if (entry.isDirectory) newFile else newFile.parentFile
294 316
@@ -304,6 +326,7 @@ object FileUtil {
304 newFile.outputStream().use { fos -> zis.copyTo(fos) } 326 newFile.outputStream().use { fos -> zis.copyTo(fos) }
305 } 327 }
306 entry = zis.nextEntry 328 entry = zis.nextEntry
329 progress++
307 } 330 }
308 } 331 }
309 } 332 }
@@ -313,14 +336,15 @@ object FileUtil {
313 * @param inputFile File representation of the item that will be zipped 336 * @param inputFile File representation of the item that will be zipped
314 * @param rootDir Directory containing the inputFile 337 * @param rootDir Directory containing the inputFile
315 * @param outputStream Stream where the zip file will be output 338 * @param outputStream Stream where the zip file will be output
316 * @param cancelled [StateFlow] that reports whether this process has been cancelled 339 * @param progressCallback Lambda that is called with the total number of files and the current
340 * progress through the process. Stops execution as soon as possible if this returns true.
317 * @param compression Disables compression if true 341 * @param compression Disables compression if true
318 */ 342 */
319 fun zipFromInternalStorage( 343 fun zipFromInternalStorage(
320 inputFile: File, 344 inputFile: File,
321 rootDir: String, 345 rootDir: String,
322 outputStream: BufferedOutputStream, 346 outputStream: BufferedOutputStream,
323 cancelled: StateFlow<Boolean>? = null, 347 progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false },
324 compression: Boolean = true 348 compression: Boolean = true
325 ): TaskState { 349 ): TaskState {
326 try { 350 try {
@@ -330,8 +354,10 @@ object FileUtil {
330 zos.setLevel(Deflater.NO_COMPRESSION) 354 zos.setLevel(Deflater.NO_COMPRESSION)
331 } 355 }
332 356
357 var count = 0L
358 val totalFiles = inputFile.walkTopDown().count().toLong()
333 inputFile.walkTopDown().forEach { file -> 359 inputFile.walkTopDown().forEach { file ->
334 if (cancelled?.value == true) { 360 if (progressCallback.invoke(totalFiles, count)) {
335 return TaskState.Cancelled 361 return TaskState.Cancelled
336 } 362 }
337 363
@@ -343,6 +369,7 @@ object FileUtil {
343 if (file.isFile) { 369 if (file.isFile) {
344 file.inputStream().use { fis -> fis.copyTo(zos) } 370 file.inputStream().use { fis -> fis.copyTo(zos) }
345 } 371 }
372 count++
346 } 373 }
347 } 374 }
348 } 375 }
@@ -356,9 +383,14 @@ object FileUtil {
356 /** 383 /**
357 * Helper function that copies the contents of a DocumentFile folder into a [File] 384 * Helper function that copies the contents of a DocumentFile folder into a [File]
358 * @param file [File] representation of the folder to copy into 385 * @param file [File] representation of the folder to copy into
386 * @param progressCallback Lambda that is called with the total number of files and the current
387 * progress through the process. Stops execution as soon as possible if this returns true.
359 * @throws IllegalStateException Fails when trying to copy a folder into a file and vice versa 388 * @throws IllegalStateException Fails when trying to copy a folder into a file and vice versa
360 */ 389 */
361 fun DocumentFile.copyFilesTo(file: File) { 390 fun DocumentFile.copyFilesTo(
391 file: File,
392 progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false }
393 ) {
362 file.mkdirs() 394 file.mkdirs()
363 if (!this.isDirectory || !file.isDirectory) { 395 if (!this.isDirectory || !file.isDirectory) {
364 throw IllegalStateException( 396 throw IllegalStateException(
@@ -366,7 +398,13 @@ object FileUtil {
366 ) 398 )
367 } 399 }
368 400
401 var count = 0L
402 val totalFiles = this.listFiles().size.toLong()
369 this.listFiles().forEach { 403 this.listFiles().forEach {
404 if (progressCallback.invoke(totalFiles, count)) {
405 return
406 }
407
370 val newFile = File(file, it.name!!) 408 val newFile = File(file, it.name!!)
371 if (it.isDirectory) { 409 if (it.isDirectory) {
372 newFile.mkdirs() 410 newFile.mkdirs()
@@ -381,6 +419,7 @@ object FileUtil {
381 newFile.outputStream().use { os -> bos.copyTo(os) } 419 newFile.outputStream().use { os -> bos.copyTo(os) }
382 } 420 }
383 } 421 }
422 count++
384 } 423 }
385 } 424 }
386 425
@@ -427,6 +466,18 @@ object FileUtil {
427 } 466 }
428 } 467 }
429 468
469 fun getInputStream(path: String) = if (path.contains("content://")) {
470 Uri.parse(path).inputStream()
471 } else {
472 File(path).inputStream()
473 }
474
475 fun getOutputStream(path: String) = if (path.contains("content://")) {
476 Uri.parse(path).outputStream()
477 } else {
478 File(path).outputStream()
479 }
480
430 @Throws(IOException::class) 481 @Throws(IOException::class)
431 fun getStringFromFile(file: File): String = 482 fun getStringFromFile(file: File): String =
432 String(file.readBytes(), StandardCharsets.UTF_8) 483 String(file.readBytes(), StandardCharsets.UTF_8)
@@ -434,4 +485,19 @@ object FileUtil {
434 @Throws(IOException::class) 485 @Throws(IOException::class)
435 fun getStringFromInputStream(stream: InputStream): String = 486 fun getStringFromInputStream(stream: InputStream): String =
436 String(stream.readBytes(), StandardCharsets.UTF_8) 487 String(stream.readBytes(), StandardCharsets.UTF_8)
488
489 fun DocumentFile.inputStream(): InputStream =
490 YuzuApplication.appContext.contentResolver.openInputStream(uri)!!
491
492 fun DocumentFile.outputStream(): OutputStream =
493 YuzuApplication.appContext.contentResolver.openOutputStream(uri)!!
494
495 fun Uri.inputStream(): InputStream =
496 YuzuApplication.appContext.contentResolver.openInputStream(this)!!
497
498 fun Uri.outputStream(): OutputStream =
499 YuzuApplication.appContext.contentResolver.openOutputStream(this)!!
500
501 fun Uri.asDocumentFile(): DocumentFile? =
502 DocumentFile.fromSingleUri(YuzuApplication.appContext, this)
437} 503}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
index a8f9dcc34..81212cbee 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.utils
5 5
6import android.net.Uri 6import android.net.Uri
7import android.os.Build 7import android.os.Build
8import java.io.BufferedInputStream
9import java.io.File 8import java.io.File
10import java.io.IOException 9import java.io.IOException
11import org.yuzu.yuzu_emu.NativeLibrary 10import org.yuzu.yuzu_emu.NativeLibrary
@@ -123,7 +122,7 @@ object GpuDriverHelper {
123 // Unzip the driver. 122 // Unzip the driver.
124 try { 123 try {
125 FileUtil.unzipToInternalStorage( 124 FileUtil.unzipToInternalStorage(
126 BufferedInputStream(copiedFile.inputStream()), 125 copiedFile.path,
127 File(driverInstallationPath!!) 126 File(driverInstallationPath!!)
128 ) 127 )
129 } catch (e: SecurityException) { 128 } catch (e: SecurityException) {
@@ -156,7 +155,7 @@ object GpuDriverHelper {
156 // Unzip the driver to the private installation directory 155 // Unzip the driver to the private installation directory
157 try { 156 try {
158 FileUtil.unzipToInternalStorage( 157 FileUtil.unzipToInternalStorage(
159 BufferedInputStream(driver.inputStream()), 158 driver.path,
160 File(driverInstallationPath!!) 159 File(driverInstallationPath!!)
161 ) 160 )
162 } catch (e: SecurityException) { 161 } catch (e: SecurityException) {
diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/android/app/src/main/jni/android_common/android_common.cpp
index 1e884ffdd..7018a52af 100644
--- a/src/android/app/src/main/jni/android_common/android_common.cpp
+++ b/src/android/app/src/main/jni/android_common/android_common.cpp
@@ -42,3 +42,19 @@ double GetJDouble(JNIEnv* env, jobject jdouble) {
42jobject ToJDouble(JNIEnv* env, double value) { 42jobject ToJDouble(JNIEnv* env, double value) {
43 return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value); 43 return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
44} 44}
45
46s32 GetJInteger(JNIEnv* env, jobject jinteger) {
47 return env->GetIntField(jinteger, IDCache::GetIntegerValueField());
48}
49
50jobject ToJInteger(JNIEnv* env, s32 value) {
51 return env->NewObject(IDCache::GetIntegerClass(), IDCache::GetIntegerConstructor(), value);
52}
53
54bool GetJBoolean(JNIEnv* env, jobject jboolean) {
55 return env->GetBooleanField(jboolean, IDCache::GetBooleanValueField());
56}
57
58jobject ToJBoolean(JNIEnv* env, bool value) {
59 return env->NewObject(IDCache::GetBooleanClass(), IDCache::GetBooleanConstructor(), value);
60}
diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/android/app/src/main/jni/android_common/android_common.h
index 8eb803e1b..29a338c0a 100644
--- a/src/android/app/src/main/jni/android_common/android_common.h
+++ b/src/android/app/src/main/jni/android_common/android_common.h
@@ -6,6 +6,7 @@
6#include <string> 6#include <string>
7 7
8#include <jni.h> 8#include <jni.h>
9#include "common/common_types.h"
9 10
10std::string GetJString(JNIEnv* env, jstring jstr); 11std::string GetJString(JNIEnv* env, jstring jstr);
11jstring ToJString(JNIEnv* env, std::string_view str); 12jstring ToJString(JNIEnv* env, std::string_view str);
@@ -13,3 +14,9 @@ jstring ToJString(JNIEnv* env, std::u16string_view str);
13 14
14double GetJDouble(JNIEnv* env, jobject jdouble); 15double GetJDouble(JNIEnv* env, jobject jdouble);
15jobject ToJDouble(JNIEnv* env, double value); 16jobject ToJDouble(JNIEnv* env, double value);
17
18s32 GetJInteger(JNIEnv* env, jobject jinteger);
19jobject ToJInteger(JNIEnv* env, s32 value);
20
21bool GetJBoolean(JNIEnv* env, jobject jboolean);
22jobject ToJBoolean(JNIEnv* env, bool value);
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
index 08aed3216..e147560c3 100644
--- a/src/android/app/src/main/jni/android_config.cpp
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -21,7 +21,7 @@ void AndroidConfig::ReloadAllValues() {
21} 21}
22 22
23void AndroidConfig::SaveAllValues() { 23void AndroidConfig::SaveAllValues() {
24 Save(); 24 SaveValues();
25 SaveAndroidValues(); 25 SaveAndroidValues();
26} 26}
27 27
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index c79ad7d76..96f2ad3d4 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -43,10 +43,27 @@ static jfieldID s_overlay_control_data_landscape_position_field;
43static jfieldID s_overlay_control_data_portrait_position_field; 43static jfieldID s_overlay_control_data_portrait_position_field;
44static jfieldID s_overlay_control_data_foldable_position_field; 44static jfieldID s_overlay_control_data_foldable_position_field;
45 45
46static jclass s_patch_class;
47static jmethodID s_patch_constructor;
48static jfieldID s_patch_enabled_field;
49static jfieldID s_patch_name_field;
50static jfieldID s_patch_version_field;
51static jfieldID s_patch_type_field;
52static jfieldID s_patch_program_id_field;
53static jfieldID s_patch_title_id_field;
54
46static jclass s_double_class; 55static jclass s_double_class;
47static jmethodID s_double_constructor; 56static jmethodID s_double_constructor;
48static jfieldID s_double_value_field; 57static jfieldID s_double_value_field;
49 58
59static jclass s_integer_class;
60static jmethodID s_integer_constructor;
61static jfieldID s_integer_value_field;
62
63static jclass s_boolean_class;
64static jmethodID s_boolean_constructor;
65static jfieldID s_boolean_value_field;
66
50static constexpr jint JNI_VERSION = JNI_VERSION_1_6; 67static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
51 68
52namespace IDCache { 69namespace IDCache {
@@ -186,6 +203,38 @@ jfieldID GetOverlayControlDataFoldablePositionField() {
186 return s_overlay_control_data_foldable_position_field; 203 return s_overlay_control_data_foldable_position_field;
187} 204}
188 205
206jclass GetPatchClass() {
207 return s_patch_class;
208}
209
210jmethodID GetPatchConstructor() {
211 return s_patch_constructor;
212}
213
214jfieldID GetPatchEnabledField() {
215 return s_patch_enabled_field;
216}
217
218jfieldID GetPatchNameField() {
219 return s_patch_name_field;
220}
221
222jfieldID GetPatchVersionField() {
223 return s_patch_version_field;
224}
225
226jfieldID GetPatchTypeField() {
227 return s_patch_type_field;
228}
229
230jfieldID GetPatchProgramIdField() {
231 return s_patch_program_id_field;
232}
233
234jfieldID GetPatchTitleIdField() {
235 return s_patch_title_id_field;
236}
237
189jclass GetDoubleClass() { 238jclass GetDoubleClass() {
190 return s_double_class; 239 return s_double_class;
191} 240}
@@ -198,6 +247,30 @@ jfieldID GetDoubleValueField() {
198 return s_double_value_field; 247 return s_double_value_field;
199} 248}
200 249
250jclass GetIntegerClass() {
251 return s_integer_class;
252}
253
254jmethodID GetIntegerConstructor() {
255 return s_integer_constructor;
256}
257
258jfieldID GetIntegerValueField() {
259 return s_integer_value_field;
260}
261
262jclass GetBooleanClass() {
263 return s_boolean_class;
264}
265
266jmethodID GetBooleanConstructor() {
267 return s_boolean_constructor;
268}
269
270jfieldID GetBooleanValueField() {
271 return s_boolean_value_field;
272}
273
201} // namespace IDCache 274} // namespace IDCache
202 275
203#ifdef __cplusplus 276#ifdef __cplusplus
@@ -278,12 +351,37 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
278 env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;"); 351 env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
279 env->DeleteLocalRef(overlay_control_data_class); 352 env->DeleteLocalRef(overlay_control_data_class);
280 353
354 const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
355 s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
356 s_patch_constructor = env->GetMethodID(
357 patch_class, "<init>",
358 "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V");
359 s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
360 s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
361 s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
362 s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
363 s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
364 s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
365 env->DeleteLocalRef(patch_class);
366
281 const jclass double_class = env->FindClass("java/lang/Double"); 367 const jclass double_class = env->FindClass("java/lang/Double");
282 s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class)); 368 s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
283 s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V"); 369 s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
284 s_double_value_field = env->GetFieldID(double_class, "value", "D"); 370 s_double_value_field = env->GetFieldID(double_class, "value", "D");
285 env->DeleteLocalRef(double_class); 371 env->DeleteLocalRef(double_class);
286 372
373 const jclass int_class = env->FindClass("java/lang/Integer");
374 s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
375 s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
376 s_integer_value_field = env->GetFieldID(int_class, "value", "I");
377 env->DeleteLocalRef(int_class);
378
379 const jclass boolean_class = env->FindClass("java/lang/Boolean");
380 s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
381 s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
382 s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
383 env->DeleteLocalRef(boolean_class);
384
287 // Initialize Android Storage 385 // Initialize Android Storage
288 Common::FS::Android::RegisterCallbacks(env, s_native_library_class); 386 Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
289 387
@@ -309,7 +407,10 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
309 env->DeleteGlobalRef(s_string_class); 407 env->DeleteGlobalRef(s_string_class);
310 env->DeleteGlobalRef(s_pair_class); 408 env->DeleteGlobalRef(s_pair_class);
311 env->DeleteGlobalRef(s_overlay_control_data_class); 409 env->DeleteGlobalRef(s_overlay_control_data_class);
410 env->DeleteGlobalRef(s_patch_class);
312 env->DeleteGlobalRef(s_double_class); 411 env->DeleteGlobalRef(s_double_class);
412 env->DeleteGlobalRef(s_integer_class);
413 env->DeleteGlobalRef(s_boolean_class);
313 414
314 // UnInitialize applets 415 // UnInitialize applets
315 SoftwareKeyboard::CleanupJNI(env); 416 SoftwareKeyboard::CleanupJNI(env);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index 784d1412f..a002e705d 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -43,8 +43,25 @@ jfieldID GetOverlayControlDataLandscapePositionField();
43jfieldID GetOverlayControlDataPortraitPositionField(); 43jfieldID GetOverlayControlDataPortraitPositionField();
44jfieldID GetOverlayControlDataFoldablePositionField(); 44jfieldID GetOverlayControlDataFoldablePositionField();
45 45
46jclass GetPatchClass();
47jmethodID GetPatchConstructor();
48jfieldID GetPatchEnabledField();
49jfieldID GetPatchNameField();
50jfieldID GetPatchVersionField();
51jfieldID GetPatchTypeField();
52jfieldID GetPatchProgramIdField();
53jfieldID GetPatchTitleIdField();
54
46jclass GetDoubleClass(); 55jclass GetDoubleClass();
47jmethodID GetDoubleConstructor(); 56jmethodID GetDoubleConstructor();
48jfieldID GetDoubleValueField(); 57jfieldID GetDoubleValueField();
49 58
59jclass GetIntegerClass();
60jmethodID GetIntegerConstructor();
61jfieldID GetIntegerValueField();
62
63jclass GetBooleanClass();
64jmethodID GetBooleanConstructor();
65jfieldID GetBooleanValueField();
66
50} // namespace IDCache 67} // namespace IDCache
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index ed3b1353a..be0a723b1 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -17,6 +17,7 @@
17#include <core/file_sys/patch_manager.h> 17#include <core/file_sys/patch_manager.h>
18#include <core/file_sys/savedata_factory.h> 18#include <core/file_sys/savedata_factory.h>
19#include <core/loader/nro.h> 19#include <core/loader/nro.h>
20#include <frontend_common/content_manager.h>
20#include <jni.h> 21#include <jni.h>
21 22
22#include "common/detached_tasks.h" 23#include "common/detached_tasks.h"
@@ -100,67 +101,6 @@ void EmulationSession::SetNativeWindow(ANativeWindow* native_window) {
100 m_native_window = native_window; 101 m_native_window = native_window;
101} 102}
102 103
103int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) {
104 jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
105 std::size_t block_size) {
106 if (src == nullptr || dest == nullptr) {
107 return false;
108 }
109 if (!dest->Resize(src->GetSize())) {
110 return false;
111 }
112
113 using namespace Common::Literals;
114 [[maybe_unused]] std::vector<u8> buffer(1_MiB);
115
116 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
117 jconst read = src->Read(buffer.data(), buffer.size(), i);
118 dest->Write(buffer.data(), read, i);
119 }
120 return true;
121 };
122
123 enum InstallResult {
124 Success = 0,
125 SuccessFileOverwritten = 1,
126 InstallError = 2,
127 ErrorBaseGame = 3,
128 ErrorFilenameExtension = 4,
129 };
130
131 [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
132 if (file_extension == "nsp") {
133 nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
134 if (nsp->IsExtractedType()) {
135 return InstallError;
136 }
137 } else {
138 return ErrorFilenameExtension;
139 }
140
141 if (!nsp) {
142 return InstallError;
143 }
144
145 if (nsp->GetStatus() != Loader::ResultStatus::Success) {
146 return InstallError;
147 }
148
149 jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true,
150 copy_func);
151
152 switch (res) {
153 case FileSys::InstallResult::Success:
154 return Success;
155 case FileSys::InstallResult::OverwriteExisting:
156 return SuccessFileOverwritten;
157 case FileSys::InstallResult::ErrorBaseInstall:
158 return ErrorBaseGame;
159 default:
160 return InstallError;
161 }
162}
163
164void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir, 104void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir,
165 const std::string& custom_driver_dir, 105 const std::string& custom_driver_dir,
166 const std::string& custom_driver_name, 106 const std::string& custom_driver_name,
@@ -512,10 +452,20 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject
512} 452}
513 453
514int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, 454int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
515 jstring j_file, 455 jstring j_file, jobject jcallback) {
516 jstring j_file_extension) { 456 auto jlambdaClass = env->GetObjectClass(jcallback);
517 return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file), 457 auto jlambdaInvokeMethod = env->GetMethodID(
518 GetJString(env, j_file_extension)); 458 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
459 const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
460 auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
461 ToJDouble(env, max), ToJDouble(env, progress));
462 return GetJBoolean(env, jwasCancelled);
463 };
464
465 return static_cast<int>(
466 ContentManager::InstallNSP(&EmulationSession::GetInstance().System(),
467 EmulationSession::GetInstance().System().GetFilesystem().get(),
468 GetJString(env, j_file), callback));
519} 469}
520 470
521jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, 471jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
@@ -824,9 +774,9 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env,
824 return true; 774 return true;
825} 775}
826 776
827jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env, jobject jobj, 777jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj,
828 jstring jpath, 778 jstring jpath,
829 jstring jprogramId) { 779 jstring jprogramId) {
830 const auto path = GetJString(env, jpath); 780 const auto path = GetJString(env, jpath);
831 const auto vFile = 781 const auto vFile =
832 Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path); 782 Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
@@ -843,20 +793,40 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env,
843 FileSys::VirtualFile update_raw; 793 FileSys::VirtualFile update_raw;
844 loader->ReadUpdateRaw(update_raw); 794 loader->ReadUpdateRaw(update_raw);
845 795
846 auto addons = pm.GetPatchVersionNames(update_raw); 796 auto patches = pm.GetPatches(update_raw);
847 auto jemptyString = ToJString(env, ""); 797 jobjectArray jpatchArray =
848 auto jemptyStringPair = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), 798 env->NewObjectArray(patches.size(), IDCache::GetPatchClass(), nullptr);
849 jemptyString, jemptyString);
850 jobjectArray jaddonsArray =
851 env->NewObjectArray(addons.size(), IDCache::GetPairClass(), jemptyStringPair);
852 int i = 0; 799 int i = 0;
853 for (const auto& addon : addons) { 800 for (const auto& patch : patches) {
854 jobject jaddon = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), 801 jobject jpatch = env->NewObject(
855 ToJString(env, addon.first), ToJString(env, addon.second)); 802 IDCache::GetPatchClass(), IDCache::GetPatchConstructor(), patch.enabled,
856 env->SetObjectArrayElement(jaddonsArray, i, jaddon); 803 ToJString(env, patch.name), ToJString(env, patch.version),
804 static_cast<jint>(patch.type), ToJString(env, std::to_string(patch.program_id)),
805 ToJString(env, std::to_string(patch.title_id)));
806 env->SetObjectArrayElement(jpatchArray, i, jpatch);
857 ++i; 807 ++i;
858 } 808 }
859 return jaddonsArray; 809 return jpatchArray;
810}
811
812void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject jobj,
813 jstring jprogramId) {
814 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
815 ContentManager::RemoveUpdate(EmulationSession::GetInstance().System().GetFileSystemController(),
816 program_id);
817}
818
819void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
820 jstring jprogramId) {
821 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
822 ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id);
823}
824
825void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
826 jstring jname) {
827 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
828 ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(),
829 program_id, GetJString(env, jname));
860} 830}
861 831
862jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, 832jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
index 4a8049578..dadb138ad 100644
--- a/src/android/app/src/main/jni/native.h
+++ b/src/android/app/src/main/jni/native.h
@@ -7,6 +7,7 @@
7#include "core/file_sys/registered_cache.h" 7#include "core/file_sys/registered_cache.h"
8#include "core/hle/service/acc/profile_manager.h" 8#include "core/hle/service/acc/profile_manager.h"
9#include "core/perf_stats.h" 9#include "core/perf_stats.h"
10#include "frontend_common/content_manager.h"
10#include "jni/applets/software_keyboard.h" 11#include "jni/applets/software_keyboard.h"
11#include "jni/emu_window/emu_window.h" 12#include "jni/emu_window/emu_window.h"
12#include "video_core/rasterizer_interface.h" 13#include "video_core/rasterizer_interface.h"
@@ -29,7 +30,6 @@ public:
29 void SetNativeWindow(ANativeWindow* native_window); 30 void SetNativeWindow(ANativeWindow* native_window);
30 void SurfaceChanged(); 31 void SurfaceChanged();
31 32
32 int InstallFileToNand(std::string filename, std::string file_extension);
33 void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, 33 void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
34 const std::string& custom_driver_name, 34 const std::string& custom_driver_name,
35 const std::string& file_redirect_dir); 35 const std::string& file_redirect_dir);
diff --git a/src/android/app/src/main/res/layout/card_home_option.xml b/src/android/app/src/main/res/layout/card_home_option.xml
index cb667c928..224ec4d89 100644
--- a/src/android/app/src/main/res/layout/card_home_option.xml
+++ b/src/android/app/src/main/res/layout/card_home_option.xml
@@ -2,16 +2,16 @@
2<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" 2<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto" 3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools" 4 xmlns:tools="http://schemas.android.com/tools"
5 style="?attr/materialCardViewFilledStyle" 5 style="?attr/materialCardViewElevatedStyle"
6 android:id="@+id/option_card" 6 android:id="@+id/option_card"
7 android:layout_width="match_parent" 7 android:layout_width="match_parent"
8 android:layout_height="wrap_content" 8 android:layout_height="wrap_content"
9 android:layout_marginBottom="24dp" 9 android:layout_marginBottom="24dp"
10 android:layout_marginHorizontal="12dp" 10 android:layout_marginHorizontal="12dp"
11 android:background="?attr/selectableItemBackground" 11 android:background="?attr/selectableItemBackground"
12 android:backgroundTint="?attr/colorSurfaceVariant"
13 android:clickable="true" 12 android:clickable="true"
14 android:focusable="true"> 13 android:focusable="true"
14 app:cardElevation="4dp">
15 15
16 <LinearLayout 16 <LinearLayout
17 android:id="@+id/option_layout" 17 android:id="@+id/option_layout"
diff --git a/src/android/app/src/main/res/layout/dialog_progress_bar.xml b/src/android/app/src/main/res/layout/dialog_progress_bar.xml
index 0209ea082..e61aa5294 100644
--- a/src/android/app/src/main/res/layout/dialog_progress_bar.xml
+++ b/src/android/app/src/main/res/layout/dialog_progress_bar.xml
@@ -1,8 +1,30 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<com.google.android.material.progressindicator.LinearProgressIndicator xmlns:android="http://schemas.android.com/apk/res/android" 2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto" 3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 android:id="@+id/progress_bar"
5 android:layout_width="match_parent" 4 android:layout_width="match_parent"
6 android:layout_height="wrap_content" 5 android:layout_height="wrap_content"
7 android:padding="24dp" 6 android:orientation="vertical">
8 app:trackCornerRadius="4dp" /> 7
8 <com.google.android.material.textview.MaterialTextView
9 android:id="@+id/message"
10 style="@style/TextAppearance.Material3.BodyMedium"
11 android:layout_width="match_parent"
12 android:layout_height="wrap_content"
13 android:layout_marginHorizontal="24dp"
14 android:layout_marginTop="12dp"
15 android:layout_marginBottom="6dp"
16 android:ellipsize="marquee"
17 android:marqueeRepeatLimit="marquee_forever"
18 android:requiresFadingEdge="horizontal"
19 android:singleLine="true"
20 android:textAlignment="viewStart"
21 android:visibility="gone" />
22
23 <com.google.android.material.progressindicator.LinearProgressIndicator
24 android:id="@+id/progress_bar"
25 android:layout_width="match_parent"
26 android:layout_height="wrap_content"
27 android:padding="24dp"
28 app:trackCornerRadius="4dp" />
29
30</LinearLayout>
diff --git a/src/android/app/src/main/res/layout/list_item_addon.xml b/src/android/app/src/main/res/layout/list_item_addon.xml
index 74ca04ef1..3a1382fe2 100644
--- a/src/android/app/src/main/res/layout/list_item_addon.xml
+++ b/src/android/app/src/main/res/layout/list_item_addon.xml
@@ -14,12 +14,11 @@
14 android:id="@+id/text_container" 14 android:id="@+id/text_container"
15 android:layout_width="0dp" 15 android:layout_width="0dp"
16 android:layout_height="wrap_content" 16 android:layout_height="wrap_content"
17 android:layout_marginEnd="16dp"
18 android:orientation="vertical" 17 android:orientation="vertical"
19 app:layout_constraintBottom_toBottomOf="@+id/addon_switch" 18 android:layout_marginEnd="16dp"
20 app:layout_constraintEnd_toStartOf="@+id/addon_switch" 19 app:layout_constraintEnd_toStartOf="@+id/addon_checkbox"
21 app:layout_constraintStart_toStartOf="parent" 20 app:layout_constraintStart_toStartOf="parent"
22 app:layout_constraintTop_toTopOf="@+id/addon_switch"> 21 app:layout_constraintTop_toTopOf="parent">
23 22
24 <com.google.android.material.textview.MaterialTextView 23 <com.google.android.material.textview.MaterialTextView
25 android:id="@+id/title" 24 android:id="@+id/title"
@@ -42,16 +41,29 @@
42 41
43 </LinearLayout> 42 </LinearLayout>
44 43
45 <com.google.android.material.materialswitch.MaterialSwitch 44 <com.google.android.material.checkbox.MaterialCheckBox
46 android:id="@+id/addon_switch" 45 android:id="@+id/addon_checkbox"
47 android:layout_width="wrap_content" 46 android:layout_width="wrap_content"
48 android:layout_height="wrap_content" 47 android:layout_height="wrap_content"
49 android:focusable="true" 48 android:focusable="true"
50 android:gravity="center" 49 android:gravity="center"
51 android:nextFocusLeft="@id/addon_container" 50 android:layout_marginEnd="8dp"
52 app:layout_constraintBottom_toBottomOf="parent" 51 app:layout_constraintTop_toTopOf="@+id/text_container"
52 app:layout_constraintBottom_toBottomOf="@+id/text_container"
53 app:layout_constraintEnd_toStartOf="@+id/button_delete" />
54
55 <Button
56 android:id="@+id/button_delete"
57 style="@style/Widget.Material3.Button.IconButton"
58 android:layout_width="wrap_content"
59 android:layout_height="wrap_content"
60 android:layout_gravity="center_vertical"
61 android:contentDescription="@string/delete"
62 android:tooltipText="@string/delete"
63 app:icon="@drawable/ic_delete"
64 app:iconTint="?attr/colorControlNormal"
53 app:layout_constraintEnd_toEndOf="parent" 65 app:layout_constraintEnd_toEndOf="parent"
54 app:layout_constraintStart_toEndOf="@id/text_container" 66 app:layout_constraintTop_toTopOf="@+id/addon_checkbox"
55 app:layout_constraintTop_toTopOf="parent" /> 67 app:layout_constraintBottom_toBottomOf="@+id/addon_checkbox" />
56 68
57</androidx.constraintlayout.widget.ConstraintLayout> 69</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 547752bda..db5b27d38 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -286,6 +286,7 @@
286 <string name="custom">Custom</string> 286 <string name="custom">Custom</string>
287 <string name="notice">Notice</string> 287 <string name="notice">Notice</string>
288 <string name="import_complete">Import complete</string> 288 <string name="import_complete">Import complete</string>
289 <string name="more_options">More options</string>
289 290
290 <!-- GPU driver installation --> 291 <!-- GPU driver installation -->
291 <string name="select_gpu_driver">Select GPU driver</string> 292 <string name="select_gpu_driver">Select GPU driver</string>
@@ -348,6 +349,8 @@
348 <string name="verifying_content">Verifying content…</string> 349 <string name="verifying_content">Verifying content…</string>
349 <string name="content_install_notice">Content install notice</string> 350 <string name="content_install_notice">Content install notice</string>
350 <string name="content_install_notice_description">The content that you selected does not match this game.\nInstall anyway?</string> 351 <string name="content_install_notice_description">The content that you selected does not match this game.\nInstall anyway?</string>
352 <string name="confirm_uninstall">Confirm uninstall</string>
353 <string name="confirm_uninstall_description">Are you sure you want to uninstall this addon?</string>
351 354
352 <!-- ROM loading errors --> 355 <!-- ROM loading errors -->
353 <string name="loader_error_encrypted">Your ROM is encrypted</string> 356 <string name="loader_error_encrypted">Your ROM is encrypted</string>
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index 167c4d826..2e2396075 100644
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -37,7 +37,7 @@ void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path,
37template <typename FileStream, typename Path> 37template <typename FileStream, typename Path>
38void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::openmode open_mode) { 38void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::openmode open_mode) {
39 if constexpr (IsChar<typename Path::value_type>) { 39 if constexpr (IsChar<typename Path::value_type>) {
40 file_stream.open(ToU8String(path), open_mode); 40 file_stream.open(std::filesystem::path{ToU8String(path)}, open_mode);
41 } else { 41 } else {
42 file_stream.open(std::filesystem::path{path}, open_mode); 42 file_stream.open(std::filesystem::path{path}, open_mode);
43 } 43 }
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 293d9647b..16ddb5e90 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -712,22 +712,23 @@ add_library(core STATIC
712 hle/service/server_manager.h 712 hle/service/server_manager.h
713 hle/service/service.cpp 713 hle/service/service.cpp
714 hle/service/service.h 714 hle/service/service.h
715 hle/service/set/appln_settings.cpp 715 hle/service/set/setting_formats/appln_settings.cpp
716 hle/service/set/appln_settings.h 716 hle/service/set/setting_formats/appln_settings.h
717 hle/service/set/device_settings.cpp 717 hle/service/set/setting_formats/device_settings.cpp
718 hle/service/set/device_settings.h 718 hle/service/set/setting_formats/device_settings.h
719 hle/service/set/setting_formats/system_settings.cpp
720 hle/service/set/setting_formats/system_settings.h
721 hle/service/set/setting_formats/private_settings.cpp
722 hle/service/set/setting_formats/private_settings.h
719 hle/service/set/factory_settings_server.cpp 723 hle/service/set/factory_settings_server.cpp
720 hle/service/set/factory_settings_server.h 724 hle/service/set/factory_settings_server.h
721 hle/service/set/firmware_debug_settings_server.cpp 725 hle/service/set/firmware_debug_settings_server.cpp
722 hle/service/set/firmware_debug_settings_server.h 726 hle/service/set/firmware_debug_settings_server.h
723 hle/service/set/private_settings.cpp
724 hle/service/set/private_settings.h
725 hle/service/set/settings.cpp 727 hle/service/set/settings.cpp
726 hle/service/set/settings.h 728 hle/service/set/settings.h
727 hle/service/set/settings_server.cpp 729 hle/service/set/settings_server.cpp
728 hle/service/set/settings_server.h 730 hle/service/set/settings_server.h
729 hle/service/set/system_settings.cpp 731 hle/service/set/settings_types.h
730 hle/service/set/system_settings.h
731 hle/service/set/system_settings_server.cpp 732 hle/service/set/system_settings_server.cpp
732 hle/service/set/system_settings_server.h 733 hle/service/set/system_settings_server.h
733 hle/service/sm/sm.cpp 734 hle/service/sm/sm.cpp
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 4a3dbc6a3..612122224 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -466,12 +466,12 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
466 return romfs; 466 return romfs;
467} 467}
468 468
469PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const { 469std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
470 if (title_id == 0) { 470 if (title_id == 0) {
471 return {}; 471 return {};
472 } 472 }
473 473
474 std::map<std::string, std::string, std::less<>> out; 474 std::vector<Patch> out;
475 const auto& disabled = Settings::values.disabled_addons[title_id]; 475 const auto& disabled = Settings::values.disabled_addons[title_id];
476 476
477 // Game Updates 477 // Game Updates
@@ -482,20 +482,28 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
482 482
483 const auto update_disabled = 483 const auto update_disabled =
484 std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); 484 std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
485 const auto update_label = update_disabled ? "[D] Update" : "Update"; 485 Patch update_patch = {.enabled = !update_disabled,
486 .name = "Update",
487 .version = "",
488 .type = PatchType::Update,
489 .program_id = title_id,
490 .title_id = title_id};
486 491
487 if (nacp != nullptr) { 492 if (nacp != nullptr) {
488 out.insert_or_assign(update_label, nacp->GetVersionString()); 493 update_patch.version = nacp->GetVersionString();
494 out.push_back(update_patch);
489 } else { 495 } else {
490 if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) { 496 if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
491 const auto meta_ver = content_provider.GetEntryVersion(update_tid); 497 const auto meta_ver = content_provider.GetEntryVersion(update_tid);
492 if (meta_ver.value_or(0) == 0) { 498 if (meta_ver.value_or(0) == 0) {
493 out.insert_or_assign(update_label, ""); 499 out.push_back(update_patch);
494 } else { 500 } else {
495 out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver)); 501 update_patch.version = FormatTitleVersion(*meta_ver);
502 out.push_back(update_patch);
496 } 503 }
497 } else if (update_raw != nullptr) { 504 } else if (update_raw != nullptr) {
498 out.insert_or_assign(update_label, "PACKED"); 505 update_patch.version = "PACKED";
506 out.push_back(update_patch);
499 } 507 }
500 } 508 }
501 509
@@ -539,7 +547,12 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
539 547
540 const auto mod_disabled = 548 const auto mod_disabled =
541 std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end(); 549 std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end();
542 out.insert_or_assign(mod_disabled ? "[D] " + mod->GetName() : mod->GetName(), types); 550 out.push_back({.enabled = !mod_disabled,
551 .name = mod->GetName(),
552 .version = types,
553 .type = PatchType::Mod,
554 .program_id = title_id,
555 .title_id = title_id});
543 } 556 }
544 } 557 }
545 558
@@ -557,7 +570,12 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
557 if (!types.empty()) { 570 if (!types.empty()) {
558 const auto mod_disabled = 571 const auto mod_disabled =
559 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); 572 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
560 out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types); 573 out.push_back({.enabled = !mod_disabled,
574 .name = "SDMC",
575 .version = types,
576 .type = PatchType::Mod,
577 .program_id = title_id,
578 .title_id = title_id});
561 } 579 }
562 } 580 }
563 581
@@ -584,7 +602,12 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
584 602
585 const auto dlc_disabled = 603 const auto dlc_disabled =
586 std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end(); 604 std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end();
587 out.insert_or_assign(dlc_disabled ? "[D] DLC" : "DLC", std::move(list)); 605 out.push_back({.enabled = !dlc_disabled,
606 .name = "DLC",
607 .version = std::move(list),
608 .type = PatchType::DLC,
609 .program_id = title_id,
610 .title_id = dlc_match.back().title_id});
588 } 611 }
589 612
590 return out; 613 return out;
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 03e9c7301..2601b8217 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -26,12 +26,22 @@ class ContentProvider;
26class NCA; 26class NCA;
27class NACP; 27class NACP;
28 28
29enum class PatchType { Update, DLC, Mod };
30
31struct Patch {
32 bool enabled;
33 std::string name;
34 std::string version;
35 PatchType type;
36 u64 program_id;
37 u64 title_id;
38};
39
29// A centralized class to manage patches to games. 40// A centralized class to manage patches to games.
30class PatchManager { 41class PatchManager {
31public: 42public:
32 using BuildID = std::array<u8, 0x20>; 43 using BuildID = std::array<u8, 0x20>;
33 using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>; 44 using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
34 using PatchVersionNames = std::map<std::string, std::string, std::less<>>;
35 45
36 explicit PatchManager(u64 title_id_, 46 explicit PatchManager(u64 title_id_,
37 const Service::FileSystem::FileSystemController& fs_controller_, 47 const Service::FileSystem::FileSystemController& fs_controller_,
@@ -66,9 +76,8 @@ public:
66 VirtualFile packed_update_raw = nullptr, 76 VirtualFile packed_update_raw = nullptr,
67 bool apply_layeredfs = true) const; 77 bool apply_layeredfs = true) const;
68 78
69 // Returns a vector of pairs between patch names and patch versions. 79 // Returns a vector of patches
70 // i.e. Update 3.2.2 will return {"Update", "3.2.2"} 80 [[nodiscard]] std::vector<Patch> GetPatches(VirtualFile update_raw = nullptr) const;
71 [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const;
72 81
73 // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, 82 // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
74 // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be 83 // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 683f44e27..29a10ad13 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -11,6 +11,7 @@
11#include "common/fs/path_util.h" 11#include "common/fs/path_util.h"
12#include "common/polyfill_ranges.h" 12#include "common/polyfill_ranges.h"
13#include "common/settings.h" 13#include "common/settings.h"
14#include "common/string_util.h"
14#include "core/hle/service/acc/profile_manager.h" 15#include "core/hle/service/acc/profile_manager.h"
15 16
16namespace Service::Account { 17namespace Service::Account {
@@ -164,6 +165,22 @@ std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user)
164 return GetUserIndex(user.user_uuid); 165 return GetUserIndex(user.user_uuid);
165} 166}
166 167
168/// Returns the first user profile seen based on username (which does not enforce uniqueness)
169std::optional<std::size_t> ProfileManager::GetUserIndex(const std::string& username) const {
170 const auto iter =
171 std::find_if(profiles.begin(), profiles.end(), [&username](const ProfileInfo& p) {
172 const std::string profile_username = Common::StringFromFixedZeroTerminatedBuffer(
173 reinterpret_cast<const char*>(p.username.data()), p.username.size());
174
175 return username.compare(profile_username) == 0;
176 });
177 if (iter == profiles.end()) {
178 return std::nullopt;
179 }
180
181 return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
182}
183
167/// Returns the data structure used by the switch when GetProfileBase is called on acc:* 184/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
168bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const { 185bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
169 if (!index || index >= MAX_USERS) { 186 if (!index || index >= MAX_USERS) {
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index e21863e64..f94157300 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -70,6 +70,7 @@ public:
70 std::optional<Common::UUID> GetUser(std::size_t index) const; 70 std::optional<Common::UUID> GetUser(std::size_t index) const;
71 std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const; 71 std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const;
72 std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; 72 std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
73 std::optional<std::size_t> GetUserIndex(const std::string& username) const;
73 bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; 74 bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
74 bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const; 75 bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
75 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; 76 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 4ce0a9834..595a3372e 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -20,12 +20,13 @@ void LoopProcess(Core::System& system) {
20 auto server_manager = std::make_unique<ServerManager>(system); 20 auto server_manager = std::make_unique<ServerManager>(system);
21 std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system); 21 std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
22 std::shared_ptr<HidFirmwareSettings> firmware_settings = 22 std::shared_ptr<HidFirmwareSettings> firmware_settings =
23 std::make_shared<HidFirmwareSettings>(); 23 std::make_shared<HidFirmwareSettings>(system);
24 24
25 // TODO: Remove this hack when am is emulated properly. 25 // TODO: Remove this hack when am is emulated properly.
26 resource_manager->Initialize(); 26 resource_manager->Initialize();
27 resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(), 27 resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
28 true); 28 true);
29 resource_manager->SetAruidValidForVibration(system.ApplicationProcess()->GetProcessId(), true);
29 30
30 server_manager->RegisterNamedService( 31 server_manager->RegisterNamedService(
31 "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings)); 32 "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index 1951da33b..30afed812 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -22,12 +22,16 @@
22#include "hid_core/resources/mouse/mouse.h" 22#include "hid_core/resources/mouse/mouse.h"
23#include "hid_core/resources/npad/npad.h" 23#include "hid_core/resources/npad/npad.h"
24#include "hid_core/resources/npad/npad_types.h" 24#include "hid_core/resources/npad/npad_types.h"
25#include "hid_core/resources/npad/npad_vibration.h"
25#include "hid_core/resources/palma/palma.h" 26#include "hid_core/resources/palma/palma.h"
26#include "hid_core/resources/six_axis/console_six_axis.h" 27#include "hid_core/resources/six_axis/console_six_axis.h"
27#include "hid_core/resources/six_axis/seven_six_axis.h" 28#include "hid_core/resources/six_axis/seven_six_axis.h"
28#include "hid_core/resources/six_axis/six_axis.h" 29#include "hid_core/resources/six_axis/six_axis.h"
29#include "hid_core/resources/touch_screen/gesture.h" 30#include "hid_core/resources/touch_screen/gesture.h"
30#include "hid_core/resources/touch_screen/touch_screen.h" 31#include "hid_core/resources/touch_screen/touch_screen.h"
32#include "hid_core/resources/vibration/gc_vibration_device.h"
33#include "hid_core/resources/vibration/n64_vibration_device.h"
34#include "hid_core/resources/vibration/vibration_device.h"
31 35
32namespace Service::HID { 36namespace Service::HID {
33 37
@@ -38,7 +42,7 @@ public:
38 : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) { 42 : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) {
39 // clang-format off 43 // clang-format off
40 static const FunctionInfo functions[] = { 44 static const FunctionInfo functions[] = {
41 {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, 45 {0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"},
42 }; 46 };
43 // clang-format on 47 // clang-format on
44 48
@@ -46,22 +50,49 @@ public:
46 } 50 }
47 51
48private: 52private:
49 void InitializeVibrationDevice(HLERequestContext& ctx) { 53 void ActivateVibrationDevice(HLERequestContext& ctx) {
50 IPC::RequestParser rp{ctx}; 54 IPC::RequestParser rp{ctx};
51 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; 55 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
52 56
53 if (resource_manager != nullptr && resource_manager->GetNpad()) {
54 resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
55 }
56
57 LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", 57 LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
58 vibration_device_handle.npad_type, vibration_device_handle.npad_id, 58 vibration_device_handle.npad_type, vibration_device_handle.npad_id,
59 vibration_device_handle.device_index); 59 vibration_device_handle.device_index);
60 60
61 const auto result = ActivateVibrationDeviceImpl(vibration_device_handle);
62
61 IPC::ResponseBuilder rb{ctx, 2}; 63 IPC::ResponseBuilder rb{ctx, 2};
62 rb.Push(ResultSuccess); 64 rb.Push(result);
63 } 65 }
64 66
67 Result ActivateVibrationDeviceImpl(const Core::HID::VibrationDeviceHandle& handle) {
68 std::scoped_lock lock{mutex};
69
70 const Result is_valid = IsVibrationHandleValid(handle);
71 if (is_valid.IsError()) {
72 return is_valid;
73 }
74
75 for (std::size_t i = 0; i < list_size; i++) {
76 if (handle.device_index == vibration_device_list[i].device_index &&
77 handle.npad_id == vibration_device_list[i].npad_id &&
78 handle.npad_type == vibration_device_list[i].npad_type) {
79 return ResultSuccess;
80 }
81 }
82 if (list_size == vibration_device_list.size()) {
83 return ResultVibrationDeviceIndexOutOfRange;
84 }
85 const Result result = resource_manager->GetVibrationDevice(handle)->Activate();
86 if (result.IsError()) {
87 return result;
88 }
89 vibration_device_list[list_size++] = handle;
90 return ResultSuccess;
91 }
92
93 mutable std::mutex mutex;
94 std::size_t list_size{};
95 std::array<Core::HID::VibrationDeviceHandle, 0x100> vibration_device_list{};
65 std::shared_ptr<ResourceManager> resource_manager; 96 std::shared_ptr<ResourceManager> resource_manager;
66}; 97};
67 98
@@ -153,7 +184,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
153 {209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, 184 {209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
154 {210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"}, 185 {210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
155 {211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, 186 {211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
156 {212, nullptr, "SendVibrationValueInBool"}, 187 {212, &IHidServer::SendVibrationValueInBool, "SendVibrationValueInBool"},
157 {300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, 188 {300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
158 {301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, 189 {301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
159 {302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, 190 {302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
@@ -1492,59 +1523,13 @@ void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1492void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { 1523void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1493 IPC::RequestParser rp{ctx}; 1524 IPC::RequestParser rp{ctx};
1494 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; 1525 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1495 const auto controller = GetResourceManager()->GetNpad();
1496
1497 Core::HID::VibrationDeviceInfo vibration_device_info;
1498 bool check_device_index = false;
1499
1500 switch (vibration_device_handle.npad_type) {
1501 case Core::HID::NpadStyleIndex::Fullkey:
1502 case Core::HID::NpadStyleIndex::Handheld:
1503 case Core::HID::NpadStyleIndex::JoyconDual:
1504 case Core::HID::NpadStyleIndex::JoyconLeft:
1505 case Core::HID::NpadStyleIndex::JoyconRight:
1506 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1507 check_device_index = true;
1508 break;
1509 case Core::HID::NpadStyleIndex::GameCube:
1510 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1511 break;
1512 case Core::HID::NpadStyleIndex::N64:
1513 vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
1514 break;
1515 default:
1516 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1517 break;
1518 }
1519
1520 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1521 if (check_device_index) {
1522 switch (vibration_device_handle.device_index) {
1523 case Core::HID::DeviceIndex::Left:
1524 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1525 break;
1526 case Core::HID::DeviceIndex::Right:
1527 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1528 break;
1529 case Core::HID::DeviceIndex::None:
1530 default:
1531 ASSERT_MSG(false, "DeviceIndex should never be None!");
1532 break;
1533 }
1534 }
1535 1526
1536 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", 1527 Core::HID::VibrationDeviceInfo vibration_device_info{};
1537 vibration_device_info.type, vibration_device_info.position); 1528 const auto result = GetResourceManager()->GetVibrationDeviceInfo(vibration_device_info,
1538 1529 vibration_device_handle);
1539 const auto result = IsVibrationHandleValid(vibration_device_handle);
1540 if (result.IsError()) {
1541 IPC::ResponseBuilder rb{ctx, 2};
1542 rb.Push(result);
1543 return;
1544 }
1545 1530
1546 IPC::ResponseBuilder rb{ctx, 4}; 1531 IPC::ResponseBuilder rb{ctx, 4};
1547 rb.Push(ResultSuccess); 1532 rb.Push(result);
1548 rb.PushRaw(vibration_device_info); 1533 rb.PushRaw(vibration_device_info);
1549} 1534}
1550 1535
@@ -1560,16 +1545,16 @@ void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
1560 1545
1561 const auto parameters{rp.PopRaw<Parameters>()}; 1546 const auto parameters{rp.PopRaw<Parameters>()};
1562 1547
1563 GetResourceManager()->GetNpad()->VibrateController(parameters.applet_resource_user_id,
1564 parameters.vibration_device_handle,
1565 parameters.vibration_value);
1566
1567 LOG_DEBUG(Service_HID, 1548 LOG_DEBUG(Service_HID,
1568 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 1549 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1569 parameters.vibration_device_handle.npad_type, 1550 parameters.vibration_device_handle.npad_type,
1570 parameters.vibration_device_handle.npad_id, 1551 parameters.vibration_device_handle.npad_id,
1571 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); 1552 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1572 1553
1554 GetResourceManager()->SendVibrationValue(parameters.applet_resource_user_id,
1555 parameters.vibration_device_handle,
1556 parameters.vibration_value);
1557
1573 IPC::ResponseBuilder rb{ctx, 2}; 1558 IPC::ResponseBuilder rb{ctx, 2};
1574 rb.Push(ResultSuccess); 1559 rb.Push(ResultSuccess);
1575} 1560}
@@ -1591,10 +1576,28 @@ void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
1591 parameters.vibration_device_handle.npad_id, 1576 parameters.vibration_device_handle.npad_id,
1592 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); 1577 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1593 1578
1579 bool has_active_aruid{};
1580 NpadVibrationDevice* device{nullptr};
1581 Core::HID::VibrationValue vibration_value{};
1582 Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
1583 has_active_aruid);
1584
1585 if (result.IsSuccess() && has_active_aruid) {
1586 result = IsVibrationHandleValid(parameters.vibration_device_handle);
1587 }
1588 if (result.IsSuccess() && has_active_aruid) {
1589 device = GetResourceManager()->GetNSVibrationDevice(parameters.vibration_device_handle);
1590 }
1591 if (device != nullptr) {
1592 result = device->GetActualVibrationValue(vibration_value);
1593 }
1594 if (result.IsError()) {
1595 vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE;
1596 }
1597
1594 IPC::ResponseBuilder rb{ctx, 6}; 1598 IPC::ResponseBuilder rb{ctx, 6};
1595 rb.Push(ResultSuccess); 1599 rb.Push(ResultSuccess);
1596 rb.PushRaw(GetResourceManager()->GetNpad()->GetLastVibration( 1600 rb.PushRaw(vibration_value);
1597 parameters.applet_resource_user_id, parameters.vibration_device_handle));
1598} 1601}
1599 1602
1600void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { 1603void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
@@ -1609,25 +1612,27 @@ void IHidServer::PermitVibration(HLERequestContext& ctx) {
1609 IPC::RequestParser rp{ctx}; 1612 IPC::RequestParser rp{ctx};
1610 const auto can_vibrate{rp.Pop<bool>()}; 1613 const auto can_vibrate{rp.Pop<bool>()};
1611 1614
1612 // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
1613 // by converting it to a bool
1614 Settings::values.vibration_enabled.SetValue(can_vibrate);
1615
1616 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); 1615 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
1617 1616
1617 const auto result =
1618 GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume(
1619 can_vibrate ? 1.0f : 0.0f);
1620
1618 IPC::ResponseBuilder rb{ctx, 2}; 1621 IPC::ResponseBuilder rb{ctx, 2};
1619 rb.Push(ResultSuccess); 1622 rb.Push(result);
1620} 1623}
1621 1624
1622void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) { 1625void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) {
1623 LOG_DEBUG(Service_HID, "called"); 1626 LOG_DEBUG(Service_HID, "called");
1624 1627
1625 // nnSDK checks if a float is greater than zero. We return the bool we stored earlier 1628 f32 master_volume{};
1626 const auto is_enabled = Settings::values.vibration_enabled.GetValue(); 1629 const auto result =
1630 GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume(
1631 master_volume);
1627 1632
1628 IPC::ResponseBuilder rb{ctx, 3}; 1633 IPC::ResponseBuilder rb{ctx, 3};
1629 rb.Push(ResultSuccess); 1634 rb.Push(result);
1630 rb.Push(is_enabled); 1635 rb.Push(master_volume > 0.0f);
1631} 1636}
1632 1637
1633void IHidServer::SendVibrationValues(HLERequestContext& ctx) { 1638void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
@@ -1645,13 +1650,22 @@ void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
1645 auto vibration_values = std::span( 1650 auto vibration_values = std::span(
1646 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); 1651 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
1647 1652
1648 GetResourceManager()->GetNpad()->VibrateControllers(applet_resource_user_id,
1649 vibration_device_handles, vibration_values);
1650
1651 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1653 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1652 1654
1655 Result result = ResultSuccess;
1656 if (handle_count != vibration_count) {
1657 result = ResultVibrationArraySizeMismatch;
1658 }
1659
1660 for (std::size_t i = 0; i < handle_count; i++) {
1661 if (result.IsSuccess()) {
1662 result = GetResourceManager()->SendVibrationValue(
1663 applet_resource_user_id, vibration_device_handles[i], vibration_values[i]);
1664 }
1665 }
1666
1653 IPC::ResponseBuilder rb{ctx, 2}; 1667 IPC::ResponseBuilder rb{ctx, 2};
1654 rb.Push(ResultSuccess); 1668 rb.Push(result);
1655} 1669}
1656 1670
1657void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) { 1671void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
@@ -1666,43 +1680,6 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1666 1680
1667 const auto parameters{rp.PopRaw<Parameters>()}; 1681 const auto parameters{rp.PopRaw<Parameters>()};
1668 1682
1669 /**
1670 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1671 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
1672 * in order to differentiate between Stop and StopHard commands.
1673 * This is done to reuse the controller vibration functions made for regular controllers.
1674 */
1675 const auto vibration_value = [parameters] {
1676 switch (parameters.gc_erm_command) {
1677 case Core::HID::VibrationGcErmCommand::Stop:
1678 return Core::HID::VibrationValue{
1679 .low_amplitude = 0.0f,
1680 .low_frequency = 160.0f,
1681 .high_amplitude = 0.0f,
1682 .high_frequency = 320.0f,
1683 };
1684 case Core::HID::VibrationGcErmCommand::Start:
1685 return Core::HID::VibrationValue{
1686 .low_amplitude = 1.0f,
1687 .low_frequency = 160.0f,
1688 .high_amplitude = 1.0f,
1689 .high_frequency = 320.0f,
1690 };
1691 case Core::HID::VibrationGcErmCommand::StopHard:
1692 return Core::HID::VibrationValue{
1693 .low_amplitude = 0.0f,
1694 .low_frequency = 0.0f,
1695 .high_amplitude = 0.0f,
1696 .high_frequency = 0.0f,
1697 };
1698 default:
1699 return Core::HID::DEFAULT_VIBRATION_VALUE;
1700 }
1701 }();
1702
1703 GetResourceManager()->GetNpad()->VibrateController(
1704 parameters.applet_resource_user_id, parameters.vibration_device_handle, vibration_value);
1705
1706 LOG_DEBUG(Service_HID, 1683 LOG_DEBUG(Service_HID,
1707 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " 1684 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
1708 "gc_erm_command={}", 1685 "gc_erm_command={}",
@@ -1711,8 +1688,23 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1711 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, 1688 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
1712 parameters.gc_erm_command); 1689 parameters.gc_erm_command);
1713 1690
1691 bool has_active_aruid{};
1692 NpadGcVibrationDevice* gc_device{nullptr};
1693 Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
1694 has_active_aruid);
1695
1696 if (result.IsSuccess() && has_active_aruid) {
1697 result = IsVibrationHandleValid(parameters.vibration_device_handle);
1698 }
1699 if (result.IsSuccess() && has_active_aruid) {
1700 gc_device = GetResourceManager()->GetGcVibrationDevice(parameters.vibration_device_handle);
1701 }
1702 if (gc_device != nullptr) {
1703 result = gc_device->SendVibrationGcErmCommand(parameters.gc_erm_command);
1704 }
1705
1714 IPC::ResponseBuilder rb{ctx, 2}; 1706 IPC::ResponseBuilder rb{ctx, 2};
1715 rb.Push(ResultSuccess); 1707 rb.Push(result);
1716} 1708}
1717 1709
1718void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { 1710void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
@@ -1725,33 +1717,31 @@ void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
1725 1717
1726 const auto parameters{rp.PopRaw<Parameters>()}; 1718 const auto parameters{rp.PopRaw<Parameters>()};
1727 1719
1728 const auto last_vibration = GetResourceManager()->GetNpad()->GetLastVibration(
1729 parameters.applet_resource_user_id, parameters.vibration_device_handle);
1730
1731 const auto gc_erm_command = [last_vibration] {
1732 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1733 return Core::HID::VibrationGcErmCommand::Start;
1734 }
1735
1736 /**
1737 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1738 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
1739 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1740 * This is done to reuse the controller vibration functions made for regular controllers.
1741 */
1742 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1743 return Core::HID::VibrationGcErmCommand::StopHard;
1744 }
1745
1746 return Core::HID::VibrationGcErmCommand::Stop;
1747 }();
1748
1749 LOG_DEBUG(Service_HID, 1720 LOG_DEBUG(Service_HID,
1750 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 1721 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1751 parameters.vibration_device_handle.npad_type, 1722 parameters.vibration_device_handle.npad_type,
1752 parameters.vibration_device_handle.npad_id, 1723 parameters.vibration_device_handle.npad_id,
1753 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); 1724 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1754 1725
1726 bool has_active_aruid{};
1727 NpadGcVibrationDevice* gc_device{nullptr};
1728 Core::HID::VibrationGcErmCommand gc_erm_command{};
1729 Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
1730 has_active_aruid);
1731
1732 if (result.IsSuccess() && has_active_aruid) {
1733 result = IsVibrationHandleValid(parameters.vibration_device_handle);
1734 }
1735 if (result.IsSuccess() && has_active_aruid) {
1736 gc_device = GetResourceManager()->GetGcVibrationDevice(parameters.vibration_device_handle);
1737 }
1738 if (gc_device != nullptr) {
1739 result = gc_device->GetActualVibrationGcErmCommand(gc_erm_command);
1740 }
1741 if (result.IsError()) {
1742 gc_erm_command = Core::HID::VibrationGcErmCommand::Stop;
1743 }
1744
1755 IPC::ResponseBuilder rb{ctx, 4}; 1745 IPC::ResponseBuilder rb{ctx, 4};
1756 rb.Push(ResultSuccess); 1746 rb.Push(ResultSuccess);
1757 rb.PushEnum(gc_erm_command); 1747 rb.PushEnum(gc_erm_command);
@@ -1761,21 +1751,24 @@ void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
1761 IPC::RequestParser rp{ctx}; 1751 IPC::RequestParser rp{ctx};
1762 const auto applet_resource_user_id{rp.Pop<u64>()}; 1752 const auto applet_resource_user_id{rp.Pop<u64>()};
1763 1753
1764 GetResourceManager()->GetNpad()->SetPermitVibrationSession(true);
1765
1766 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1754 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1767 1755
1756 const auto result =
1757 GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession(
1758 applet_resource_user_id);
1759
1768 IPC::ResponseBuilder rb{ctx, 2}; 1760 IPC::ResponseBuilder rb{ctx, 2};
1769 rb.Push(ResultSuccess); 1761 rb.Push(result);
1770} 1762}
1771 1763
1772void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) { 1764void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) {
1773 GetResourceManager()->GetNpad()->SetPermitVibrationSession(false);
1774
1775 LOG_DEBUG(Service_HID, "called"); 1765 LOG_DEBUG(Service_HID, "called");
1776 1766
1767 const auto result =
1768 GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession();
1769
1777 IPC::ResponseBuilder rb{ctx, 2}; 1770 IPC::ResponseBuilder rb{ctx, 2};
1778 rb.Push(ResultSuccess); 1771 rb.Push(result);
1779} 1772}
1780 1773
1781void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) { 1774void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
@@ -1795,10 +1788,61 @@ void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
1795 parameters.vibration_device_handle.npad_id, 1788 parameters.vibration_device_handle.npad_id,
1796 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); 1789 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1797 1790
1791 bool is_mounted{};
1792 NpadVibrationBase* device{nullptr};
1793 Result result = IsVibrationHandleValid(parameters.vibration_device_handle);
1794
1795 if (result.IsSuccess()) {
1796 device = GetResourceManager()->GetVibrationDevice(parameters.vibration_device_handle);
1797 }
1798
1799 if (device != nullptr) {
1800 is_mounted = device->IsVibrationMounted();
1801 }
1802
1798 IPC::ResponseBuilder rb{ctx, 3}; 1803 IPC::ResponseBuilder rb{ctx, 3};
1799 rb.Push(ResultSuccess); 1804 rb.Push(result);
1800 rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted( 1805 rb.Push(is_mounted);
1801 parameters.applet_resource_user_id, parameters.vibration_device_handle)); 1806}
1807
1808void IHidServer::SendVibrationValueInBool(HLERequestContext& ctx) {
1809 IPC::RequestParser rp{ctx};
1810 struct Parameters {
1811 Core::HID::VibrationDeviceHandle vibration_device_handle;
1812 INSERT_PADDING_WORDS_NOINIT(1);
1813 u64 applet_resource_user_id;
1814 bool is_vibrating;
1815 };
1816 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1817
1818 const auto parameters{rp.PopRaw<Parameters>()};
1819
1820 LOG_DEBUG(Service_HID,
1821 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
1822 "is_vibrating={}",
1823 parameters.vibration_device_handle.npad_type,
1824 parameters.vibration_device_handle.npad_id,
1825 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
1826 parameters.is_vibrating);
1827
1828 bool has_active_aruid{};
1829 NpadN64VibrationDevice* n64_device{nullptr};
1830 Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
1831 has_active_aruid);
1832
1833 if (result.IsSuccess() && has_active_aruid) {
1834 result = IsVibrationHandleValid(parameters.vibration_device_handle);
1835 }
1836 if (result.IsSuccess() && has_active_aruid) {
1837 n64_device =
1838 GetResourceManager()->GetN64VibrationDevice(parameters.vibration_device_handle);
1839 }
1840 if (n64_device != nullptr) {
1841 result = n64_device->SendValueInBool(parameters.is_vibrating);
1842 }
1843
1844 IPC::ResponseBuilder rb{ctx, 2};
1845 rb.Push(result);
1802} 1846}
1803 1847
1804void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { 1848void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h
index cc7c4ebdd..3a2e0a230 100644
--- a/src/core/hle/service/hid/hid_server.h
+++ b/src/core/hle/service/hid/hid_server.h
@@ -97,6 +97,7 @@ private:
97 void BeginPermitVibrationSession(HLERequestContext& ctx); 97 void BeginPermitVibrationSession(HLERequestContext& ctx);
98 void EndPermitVibrationSession(HLERequestContext& ctx); 98 void EndPermitVibrationSession(HLERequestContext& ctx);
99 void IsVibrationDeviceMounted(HLERequestContext& ctx); 99 void IsVibrationDeviceMounted(HLERequestContext& ctx);
100 void SendVibrationValueInBool(HLERequestContext& ctx);
100 void ActivateConsoleSixAxisSensor(HLERequestContext& ctx); 101 void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
101 void StartConsoleSixAxisSensor(HLERequestContext& ctx); 102 void StartConsoleSixAxisSensor(HLERequestContext& ctx);
102 void StopConsoleSixAxisSensor(HLERequestContext& ctx); 103 void StopConsoleSixAxisSensor(HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index 3a0cb3cb1..4466a189b 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -7,6 +7,7 @@
7#include "hid_core/resource_manager.h" 7#include "hid_core/resource_manager.h"
8#include "hid_core/resources/npad/npad.h" 8#include "hid_core/resources/npad/npad.h"
9#include "hid_core/resources/npad/npad_types.h" 9#include "hid_core/resources/npad/npad_types.h"
10#include "hid_core/resources/npad/npad_vibration.h"
10#include "hid_core/resources/palma/palma.h" 11#include "hid_core/resources/palma/palma.h"
11#include "hid_core/resources/touch_screen/touch_screen.h" 12#include "hid_core/resources/touch_screen/touch_screen.h"
12 13
@@ -67,14 +68,14 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
67 {501, &IHidSystemServer::RegisterAppletResourceUserId, "RegisterAppletResourceUserId"}, 68 {501, &IHidSystemServer::RegisterAppletResourceUserId, "RegisterAppletResourceUserId"},
68 {502, &IHidSystemServer::UnregisterAppletResourceUserId, "UnregisterAppletResourceUserId"}, 69 {502, &IHidSystemServer::UnregisterAppletResourceUserId, "UnregisterAppletResourceUserId"},
69 {503, &IHidSystemServer::EnableAppletToGetInput, "EnableAppletToGetInput"}, 70 {503, &IHidSystemServer::EnableAppletToGetInput, "EnableAppletToGetInput"},
70 {504, nullptr, "SetAruidValidForVibration"}, 71 {504, &IHidSystemServer::SetAruidValidForVibration, "SetAruidValidForVibration"},
71 {505, &IHidSystemServer::EnableAppletToGetSixAxisSensor, "EnableAppletToGetSixAxisSensor"}, 72 {505, &IHidSystemServer::EnableAppletToGetSixAxisSensor, "EnableAppletToGetSixAxisSensor"},
72 {506, &IHidSystemServer::EnableAppletToGetPadInput, "EnableAppletToGetPadInput"}, 73 {506, &IHidSystemServer::EnableAppletToGetPadInput, "EnableAppletToGetPadInput"},
73 {507, &IHidSystemServer::EnableAppletToGetTouchScreen, "EnableAppletToGetTouchScreen"}, 74 {507, &IHidSystemServer::EnableAppletToGetTouchScreen, "EnableAppletToGetTouchScreen"},
74 {510, nullptr, "SetVibrationMasterVolume"}, 75 {510, &IHidSystemServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"},
75 {511, nullptr, "GetVibrationMasterVolume"}, 76 {511, &IHidSystemServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"},
76 {512, nullptr, "BeginPermitVibrationSession"}, 77 {512, &IHidSystemServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
77 {513, nullptr, "EndPermitVibrationSession"}, 78 {513, &IHidSystemServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
78 {514, nullptr, "Unknown514"}, 79 {514, nullptr, "Unknown514"},
79 {520, nullptr, "EnableHandheldHids"}, 80 {520, nullptr, "EnableHandheldHids"},
80 {521, nullptr, "DisableHandheldHids"}, 81 {521, nullptr, "DisableHandheldHids"},
@@ -156,7 +157,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
156 {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, 157 {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
157 {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, 158 {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
158 {1154, nullptr, "IsFirmwareAvailableForNotification"}, 159 {1154, nullptr, "IsFirmwareAvailableForNotification"},
159 {1155, nullptr, "SetForceHandheldStyleVibration"}, 160 {1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"},
160 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, 161 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
161 {1157, nullptr, "CancelConnectionTrigger"}, 162 {1157, nullptr, "CancelConnectionTrigger"},
162 {1200, nullptr, "IsButtonConfigSupported"}, 163 {1200, nullptr, "IsButtonConfigSupported"},
@@ -538,6 +539,27 @@ void IHidSystemServer::EnableAppletToGetInput(HLERequestContext& ctx) {
538 rb.Push(ResultSuccess); 539 rb.Push(ResultSuccess);
539} 540}
540 541
542void IHidSystemServer::SetAruidValidForVibration(HLERequestContext& ctx) {
543 IPC::RequestParser rp{ctx};
544 struct Parameters {
545 bool is_enabled;
546 INSERT_PADDING_WORDS_NOINIT(1);
547 u64 applet_resource_user_id;
548 };
549 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
550
551 const auto parameters{rp.PopRaw<Parameters>()};
552
553 LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
554 parameters.is_enabled, parameters.applet_resource_user_id);
555
556 GetResourceManager()->SetAruidValidForVibration(parameters.applet_resource_user_id,
557 parameters.is_enabled);
558
559 IPC::ResponseBuilder rb{ctx, 2};
560 rb.Push(ResultSuccess);
561}
562
541void IHidSystemServer::EnableAppletToGetSixAxisSensor(HLERequestContext& ctx) { 563void IHidSystemServer::EnableAppletToGetSixAxisSensor(HLERequestContext& ctx) {
542 IPC::RequestParser rp{ctx}; 564 IPC::RequestParser rp{ctx};
543 struct Parameters { 565 struct Parameters {
@@ -601,6 +623,57 @@ void IHidSystemServer::EnableAppletToGetTouchScreen(HLERequestContext& ctx) {
601 rb.Push(ResultSuccess); 623 rb.Push(ResultSuccess);
602} 624}
603 625
626void IHidSystemServer::SetVibrationMasterVolume(HLERequestContext& ctx) {
627 IPC::RequestParser rp{ctx};
628 const auto master_volume{rp.Pop<f32>()};
629
630 LOG_INFO(Service_HID, "called, volume={}", master_volume);
631
632 const auto result =
633 GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume(
634 master_volume);
635
636 IPC::ResponseBuilder rb{ctx, 2};
637 rb.Push(result);
638}
639
640void IHidSystemServer::GetVibrationMasterVolume(HLERequestContext& ctx) {
641 f32 master_volume{};
642 const auto result =
643 GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume(
644 master_volume);
645
646 LOG_INFO(Service_HID, "called, volume={}", master_volume);
647
648 IPC::ResponseBuilder rb{ctx, 3};
649 rb.Push(result);
650 rb.Push(master_volume);
651}
652
653void IHidSystemServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
654 IPC::RequestParser rp{ctx};
655 const auto applet_resource_user_id{rp.Pop<u64>()};
656
657 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
658
659 const auto result =
660 GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession(
661 applet_resource_user_id);
662
663 IPC::ResponseBuilder rb{ctx, 2};
664 rb.Push(result);
665}
666
667void IHidSystemServer::EndPermitVibrationSession(HLERequestContext& ctx) {
668 LOG_INFO(Service_HID, "called");
669
670 const auto result =
671 GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession();
672
673 IPC::ResponseBuilder rb{ctx, 2};
674 rb.Push(result);
675}
676
604void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) { 677void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) {
605 const bool is_attached = true; 678 const bool is_attached = true;
606 679
@@ -749,6 +822,19 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx
749 rb.PushRaw(touchscreen_config); 822 rb.PushRaw(touchscreen_config);
750} 823}
751 824
825void IHidSystemServer::SetForceHandheldStyleVibration(HLERequestContext& ctx) {
826 IPC::RequestParser rp{ctx};
827 const auto is_forced{rp.Pop<bool>()};
828
829 LOG_INFO(Service_HID, "called, is_forced={}", is_forced);
830
831 GetResourceManager()->SetForceHandheldStyleVibration(is_forced);
832 GetResourceManager()->GetNpad()->UpdateHandheldAbstractState();
833
834 IPC::ResponseBuilder rb{ctx, 2};
835 rb.Push(ResultSuccess);
836}
837
752void IHidSystemServer::IsUsingCustomButtonConfig(HLERequestContext& ctx) { 838void IHidSystemServer::IsUsingCustomButtonConfig(HLERequestContext& ctx) {
753 const bool is_enabled = false; 839 const bool is_enabled = false;
754 840
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
index 0c2634e3f..90a719f02 100644
--- a/src/core/hle/service/hid/hid_system_server.h
+++ b/src/core/hle/service/hid/hid_system_server.h
@@ -42,9 +42,14 @@ private:
42 void RegisterAppletResourceUserId(HLERequestContext& ctx); 42 void RegisterAppletResourceUserId(HLERequestContext& ctx);
43 void UnregisterAppletResourceUserId(HLERequestContext& ctx); 43 void UnregisterAppletResourceUserId(HLERequestContext& ctx);
44 void EnableAppletToGetInput(HLERequestContext& ctx); 44 void EnableAppletToGetInput(HLERequestContext& ctx);
45 void SetAruidValidForVibration(HLERequestContext& ctx);
45 void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx); 46 void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx);
46 void EnableAppletToGetPadInput(HLERequestContext& ctx); 47 void EnableAppletToGetPadInput(HLERequestContext& ctx);
47 void EnableAppletToGetTouchScreen(HLERequestContext& ctx); 48 void EnableAppletToGetTouchScreen(HLERequestContext& ctx);
49 void SetVibrationMasterVolume(HLERequestContext& ctx);
50 void GetVibrationMasterVolume(HLERequestContext& ctx);
51 void BeginPermitVibrationSession(HLERequestContext& ctx);
52 void EndPermitVibrationSession(HLERequestContext& ctx);
48 void IsJoyConAttachedOnAllRail(HLERequestContext& ctx); 53 void IsJoyConAttachedOnAllRail(HLERequestContext& ctx);
49 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); 54 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
50 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); 55 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
@@ -61,6 +66,7 @@ private:
61 void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); 66 void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
62 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); 67 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
63 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); 68 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
69 void SetForceHandheldStyleVibration(HLERequestContext& ctx);
64 void IsUsingCustomButtonConfig(HLERequestContext& ctx); 70 void IsUsingCustomButtonConfig(HLERequestContext& ctx);
65 71
66 std::shared_ptr<ResourceManager> GetResourceManager(); 72 std::shared_ptr<ResourceManager> GetResourceManager();
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index 31cc87acc..cc7776efc 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -441,7 +441,10 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
441 device_state = DeviceState::TagMounted; 441 device_state = DeviceState::TagMounted;
442 mount_target = mount_target_; 442 mount_target = mount_target_;
443 443
444 if (!is_corrupted && mount_target != NFP::MountTarget::Rom) { 444 const bool create_backup =
445 mount_target == NFP::MountTarget::All || mount_target == NFP::MountTarget::Ram ||
446 (mount_target == NFP::MountTarget::Rom && HasBackup(encrypted_tag_data.uuid).IsError());
447 if (!is_corrupted && create_backup) {
445 std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); 448 std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
446 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); 449 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
447 WriteBackupData(encrypted_tag_data.uuid, data); 450 WriteBackupData(encrypted_tag_data.uuid, data);
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index 0469110e8..af6591370 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -112,9 +112,7 @@ void Nvnflinger::ShutdownLayers() {
112 { 112 {
113 const auto lock_guard = Lock(); 113 const auto lock_guard = Lock();
114 for (auto& display : displays) { 114 for (auto& display : displays) {
115 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { 115 display.Abandon();
116 display.GetLayer(layer).GetConsumer().Abandon();
117 }
118 } 116 }
119 117
120 is_abandoned = true; 118 is_abandoned = true;
@@ -176,24 +174,28 @@ void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
176 display.CreateLayer(layer_id, buffer_id, nvdrv->container); 174 display.CreateLayer(layer_id, buffer_id, nvdrv->container);
177} 175}
178 176
179void Nvnflinger::OpenLayer(u64 layer_id) { 177bool Nvnflinger::OpenLayer(u64 layer_id) {
180 const auto lock_guard = Lock(); 178 const auto lock_guard = Lock();
181 179
182 for (auto& display : displays) { 180 for (auto& display : displays) {
183 if (auto* layer = display.FindLayer(layer_id); layer) { 181 if (auto* layer = display.FindLayer(layer_id); layer) {
184 layer->Open(); 182 return layer->Open();
185 } 183 }
186 } 184 }
185
186 return false;
187} 187}
188 188
189void Nvnflinger::CloseLayer(u64 layer_id) { 189bool Nvnflinger::CloseLayer(u64 layer_id) {
190 const auto lock_guard = Lock(); 190 const auto lock_guard = Lock();
191 191
192 for (auto& display : displays) { 192 for (auto& display : displays) {
193 if (auto* layer = display.FindLayer(layer_id); layer) { 193 if (auto* layer = display.FindLayer(layer_id); layer) {
194 layer->Close(); 194 return layer->Close();
195 } 195 }
196 } 196 }
197
198 return false;
197} 199}
198 200
199void Nvnflinger::DestroyLayer(u64 layer_id) { 201void Nvnflinger::DestroyLayer(u64 layer_id) {
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index 871285764..a60e0ae6b 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -74,10 +74,10 @@ public:
74 [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); 74 [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
75 75
76 /// Opens a layer on all displays for the given layer ID. 76 /// Opens a layer on all displays for the given layer ID.
77 void OpenLayer(u64 layer_id); 77 bool OpenLayer(u64 layer_id);
78 78
79 /// Closes a layer on all displays for the given layer ID. 79 /// Closes a layer on all displays for the given layer ID.
80 void CloseLayer(u64 layer_id); 80 bool CloseLayer(u64 layer_id);
81 81
82 /// Destroys the given layer ID. 82 /// Destroys the given layer ID.
83 void DestroyLayer(u64 layer_id); 83 void DestroyLayer(u64 layer_id);
diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h
deleted file mode 100644
index b63eaf45c..000000000
--- a/src/core/hle/service/set/private_settings.h
+++ /dev/null
@@ -1,72 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/uuid.h"
12#include "core/hle/service/time/clock_types.h"
13
14namespace Service::Set {
15
16/// This is nn::settings::system::InitialLaunchFlag
17struct InitialLaunchFlag {
18 union {
19 u32 raw{};
20
21 BitField<0, 1, u32> InitialLaunchCompletionFlag;
22 BitField<8, 1, u32> InitialLaunchUserAdditionFlag;
23 BitField<16, 1, u32> InitialLaunchTimestampFlag;
24 };
25};
26static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size");
27
28/// This is nn::settings::system::InitialLaunchSettings
29struct InitialLaunchSettings {
30 InitialLaunchFlag flags;
31 INSERT_PADDING_BYTES(0x4);
32 Service::Time::Clock::SteadyClockTimePoint timestamp;
33};
34static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
35
36#pragma pack(push, 4)
37struct InitialLaunchSettingsPacked {
38 InitialLaunchFlag flags;
39 Service::Time::Clock::SteadyClockTimePoint timestamp;
40};
41#pragma pack(pop)
42static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C,
43 "InitialLaunchSettingsPacked is incorrect size");
44
45struct PrivateSettings {
46 std::array<u8, 0x10> reserved_00;
47
48 // nn::settings::system::InitialLaunchSettings
49 InitialLaunchSettings initial_launch_settings;
50
51 std::array<u8, 0x20> reserved_30;
52
53 Common::UUID external_clock_source_id;
54 s64 shutdown_rtc_value;
55 s64 external_steady_clock_internal_offset;
56
57 std::array<u8, 0x60> reserved_70;
58
59 // nn::settings::system::PlatformRegion
60 std::array<u8, 0x4> platform_region;
61
62 std::array<u8, 0x4> reserved_D4;
63};
64static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10);
65static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50);
66static_assert(offsetof(PrivateSettings, reserved_70) == 0x70);
67static_assert(offsetof(PrivateSettings, platform_region) == 0xD0);
68static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!");
69
70PrivateSettings DefaultPrivateSettings();
71
72} // namespace Service::Set
diff --git a/src/core/hle/service/set/appln_settings.cpp b/src/core/hle/service/set/setting_formats/appln_settings.cpp
index a5d802757..f7c7d5b91 100644
--- a/src/core/hle/service/set/appln_settings.cpp
+++ b/src/core/hle/service/set/setting_formats/appln_settings.cpp
@@ -1,12 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/set/appln_settings.h" 4#include "core/hle/service/set/setting_formats/appln_settings.h"
5 5
6namespace Service::Set { 6namespace Service::Set {
7 7
8ApplnSettings DefaultApplnSettings() { 8ApplnSettings DefaultApplnSettings() {
9 return {}; 9 ApplnSettings settings{};
10
11 settings.mii_author_id = Common::UUID::MakeDefault();
12
13 return settings;
10} 14}
11 15
12} // namespace Service::Set 16} // namespace Service::Set
diff --git a/src/core/hle/service/set/appln_settings.h b/src/core/hle/service/set/setting_formats/appln_settings.h
index 126375860..ba9af998a 100644
--- a/src/core/hle/service/set/appln_settings.h
+++ b/src/core/hle/service/set/setting_formats/appln_settings.h
@@ -7,24 +7,23 @@
7#include <cstddef> 7#include <cstddef>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/uuid.h"
11#include "core/hle/service/set/settings_types.h"
10 12
11namespace Service::Set { 13namespace Service::Set {
12struct ApplnSettings { 14struct ApplnSettings {
13 std::array<u8, 0x10> reserved_000; 15 INSERT_PADDING_BYTES(0x10); // Reserved
14 16
15 // nn::util::Uuid MiiAuthorId, copied from system settings 0x94B0 17 // nn::util::Uuid MiiAuthorId, copied from system settings 0x94B0
16 std::array<u8, 0x10> mii_author_id; 18 Common::UUID mii_author_id;
17 19 INSERT_PADDING_BYTES(0x30); // Reserved
18 std::array<u8, 0x30> reserved_020;
19 20
20 // nn::settings::system::ServiceDiscoveryControlSettings 21 // nn::settings::system::ServiceDiscoveryControlSettings
21 std::array<u8, 0x4> service_discovery_control_settings; 22 u32 service_discovery_control_settings;
22 23 INSERT_PADDING_BYTES(0x20); // Reserved
23 std::array<u8, 0x20> reserved_054;
24 24
25 bool in_repair_process_enable_flag; 25 bool in_repair_process_enable_flag;
26 26 INSERT_PADDING_BYTES(0x3);
27 std::array<u8, 0x3> pad_075;
28}; 27};
29static_assert(offsetof(ApplnSettings, mii_author_id) == 0x10); 28static_assert(offsetof(ApplnSettings, mii_author_id) == 0x10);
30static_assert(offsetof(ApplnSettings, service_discovery_control_settings) == 0x50); 29static_assert(offsetof(ApplnSettings, service_discovery_control_settings) == 0x50);
diff --git a/src/core/hle/service/set/device_settings.cpp b/src/core/hle/service/set/setting_formats/device_settings.cpp
index e423ce38a..5f295404d 100644
--- a/src/core/hle/service/set/device_settings.cpp
+++ b/src/core/hle/service/set/setting_formats/device_settings.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/set/device_settings.h" 4#include "core/hle/service/set/setting_formats/device_settings.h"
5 5
6namespace Service::Set { 6namespace Service::Set {
7 7
diff --git a/src/core/hle/service/set/device_settings.h b/src/core/hle/service/set/setting_formats/device_settings.h
index f291d0ebe..2827756f6 100644
--- a/src/core/hle/service/set/device_settings.h
+++ b/src/core/hle/service/set/setting_formats/device_settings.h
@@ -7,10 +7,12 @@
7#include <cstddef> 7#include <cstddef>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/vector_math.h"
11#include "core/hle/service/set/settings_types.h"
10 12
11namespace Service::Set { 13namespace Service::Set {
12struct DeviceSettings { 14struct DeviceSettings {
13 std::array<u8, 0x10> reserved_000; 15 INSERT_PADDING_BYTES(0x10); // Reserved
14 16
15 // nn::settings::BatteryLot 17 // nn::settings::BatteryLot
16 std::array<u8, 0x18> ptm_battery_lot; 18 std::array<u8, 0x18> ptm_battery_lot;
@@ -19,26 +21,24 @@ struct DeviceSettings {
19 u8 ptm_battery_version; 21 u8 ptm_battery_version;
20 // nn::settings::system::PtmCycleCountReliability 22 // nn::settings::system::PtmCycleCountReliability
21 u32 ptm_cycle_count_reliability; 23 u32 ptm_cycle_count_reliability;
22 24 INSERT_PADDING_BYTES(0x48); // Reserved
23 std::array<u8, 0x48> reserved_048;
24 25
25 // nn::settings::system::AnalogStickUserCalibration L 26 // nn::settings::system::AnalogStickUserCalibration L
26 std::array<u8, 0x10> analog_user_stick_calibration_l; 27 std::array<u8, 0x10> analog_user_stick_calibration_l;
27 // nn::settings::system::AnalogStickUserCalibration R 28 // nn::settings::system::AnalogStickUserCalibration R
28 std::array<u8, 0x10> analog_user_stick_calibration_r; 29 std::array<u8, 0x10> analog_user_stick_calibration_r;
29 30 INSERT_PADDING_BYTES(0x20); // Reserved
30 std::array<u8, 0x20> reserved_0B0;
31 31
32 // nn::settings::system::ConsoleSixAxisSensorAccelerationBias 32 // nn::settings::system::ConsoleSixAxisSensorAccelerationBias
33 std::array<u8, 0xC> console_six_axis_sensor_acceleration_bias; 33 Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
34 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias 34 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
35 std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_bias; 35 Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
36 // nn::settings::system::ConsoleSixAxisSensorAccelerationGain 36 // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
37 std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; 37 std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
38 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain 38 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
39 std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; 39 std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
40 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias 40 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
41 std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_time_bias; 41 Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
42 // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration 42 // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
43 std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration; 43 std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
44}; 44};
diff --git a/src/core/hle/service/set/private_settings.cpp b/src/core/hle/service/set/setting_formats/private_settings.cpp
index 70bf65727..81c362482 100644
--- a/src/core/hle/service/set/private_settings.cpp
+++ b/src/core/hle/service/set/setting_formats/private_settings.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/set/private_settings.h" 4#include "core/hle/service/set/setting_formats/private_settings.h"
5 5
6namespace Service::Set { 6namespace Service::Set {
7 7
diff --git a/src/core/hle/service/set/setting_formats/private_settings.h b/src/core/hle/service/set/setting_formats/private_settings.h
new file mode 100644
index 000000000..6c40f62e1
--- /dev/null
+++ b/src/core/hle/service/set/setting_formats/private_settings.h
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/common_types.h"
9#include "common/uuid.h"
10#include "core/hle/service/set/settings_types.h"
11#include "core/hle/service/time/clock_types.h"
12
13namespace Service::Set {
14
15struct PrivateSettings {
16 INSERT_PADDING_BYTES(0x10); // Reserved
17
18 InitialLaunchSettings initial_launch_settings;
19 INSERT_PADDING_BYTES(0x20); // Reserved
20
21 Common::UUID external_clock_source_id;
22 s64 shutdown_rtc_value;
23 s64 external_steady_clock_internal_offset;
24 INSERT_PADDING_BYTES(0x60); // Reserved
25
26 // nn::settings::system::PlatformRegion
27 s32 platform_region;
28 INSERT_PADDING_BYTES(0x4); // Reserved
29};
30static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10);
31static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50);
32static_assert(offsetof(PrivateSettings, shutdown_rtc_value) == 0x60);
33static_assert(offsetof(PrivateSettings, external_steady_clock_internal_offset) == 0x68);
34static_assert(offsetof(PrivateSettings, platform_region) == 0xD0);
35static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!");
36
37PrivateSettings DefaultPrivateSettings();
38
39} // namespace Service::Set
diff --git a/src/core/hle/service/set/system_settings.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp
index 5977429b2..66e57651e 100644
--- a/src/core/hle/service/set/system_settings.cpp
+++ b/src/core/hle/service/set/setting_formats/system_settings.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/set/system_settings.h" 4#include "core/hle/service/set/setting_formats/system_settings.h"
5 5
6namespace Service::Set { 6namespace Service::Set {
7 7
@@ -11,6 +11,8 @@ SystemSettings DefaultSystemSettings() {
11 settings.version = 0x140000; 11 settings.version = 0x140000;
12 settings.flags = 7; 12 settings.flags = 7;
13 13
14 settings.mii_author_id = Common::UUID::MakeDefault();
15
14 settings.color_set_id = ColorSet::BasicWhite; 16 settings.color_set_id = ColorSet::BasicWhite;
15 17
16 settings.notification_settings = { 18 settings.notification_settings = {
@@ -45,6 +47,10 @@ SystemSettings DefaultSystemSettings() {
45 settings.device_time_zone_location_name = {"UTC"}; 47 settings.device_time_zone_location_name = {"UTC"};
46 settings.user_system_clock_automatic_correction_enabled = false; 48 settings.user_system_clock_automatic_correction_enabled = false;
47 49
50 settings.primary_album_storage = PrimaryAlbumStorage::SdCard;
51 settings.battery_percentage_flag = true;
52 settings.chinese_traditional_input_method = ChineseTraditionalInputMethod::Unknown0;
53
48 return settings; 54 return settings;
49} 55}
50 56
diff --git a/src/core/hle/service/set/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h
index 6ec9e71e7..14654f8b1 100644
--- a/src/core/hle/service/set/system_settings.h
+++ b/src/core/hle/service/set/setting_formats/system_settings.h
@@ -8,272 +8,14 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/service/set/private_settings.h" 11#include "common/uuid.h"
12#include "common/vector_math.h"
13#include "core/hle/service/set/setting_formats/private_settings.h"
14#include "core/hle/service/set/settings_types.h"
12#include "core/hle/service/time/clock_types.h" 15#include "core/hle/service/time/clock_types.h"
13 16
14namespace Service::Set { 17namespace Service::Set {
15 18
16/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64.
17enum class LanguageCode : u64 {
18 JA = 0x000000000000616A,
19 EN_US = 0x00000053552D6E65,
20 FR = 0x0000000000007266,
21 DE = 0x0000000000006564,
22 IT = 0x0000000000007469,
23 ES = 0x0000000000007365,
24 ZH_CN = 0x0000004E432D687A,
25 KO = 0x0000000000006F6B,
26 NL = 0x0000000000006C6E,
27 PT = 0x0000000000007470,
28 RU = 0x0000000000007572,
29 ZH_TW = 0x00000057542D687A,
30 EN_GB = 0x00000042472D6E65,
31 FR_CA = 0x00000041432D7266,
32 ES_419 = 0x00003931342D7365,
33 ZH_HANS = 0x00736E61482D687A,
34 ZH_HANT = 0x00746E61482D687A,
35 PT_BR = 0x00000052422D7470,
36};
37
38/// This is nn::settings::system::ErrorReportSharePermission
39enum class ErrorReportSharePermission : u32 {
40 NotConfirmed,
41 Granted,
42 Denied,
43};
44
45/// This is nn::settings::system::ChineseTraditionalInputMethod
46enum class ChineseTraditionalInputMethod : u32 {
47 Unknown0 = 0,
48 Unknown1 = 1,
49 Unknown2 = 2,
50};
51
52/// This is nn::settings::system::HomeMenuScheme
53struct HomeMenuScheme {
54 u32 main;
55 u32 back;
56 u32 sub;
57 u32 bezel;
58 u32 extra;
59};
60static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size");
61
62/// Indicates the current theme set by the system settings
63enum class ColorSet : u32 {
64 BasicWhite = 0,
65 BasicBlack = 1,
66};
67
68/// Indicates the current console is a retail or kiosk unit
69enum class QuestFlag : u8 {
70 Retail = 0,
71 Kiosk = 1,
72};
73
74/// This is nn::settings::system::RegionCode
75enum class RegionCode : u32 {
76 Japan,
77 Usa,
78 Europe,
79 Australia,
80 HongKongTaiwanKorea,
81 China,
82};
83
84/// This is nn::settings::system::AccountSettings
85struct AccountSettings {
86 u32 flags;
87};
88static_assert(sizeof(AccountSettings) == 4, "AccountSettings is an invalid size");
89
90/// This is nn::settings::system::NotificationVolume
91enum class NotificationVolume : u32 {
92 Mute,
93 Low,
94 High,
95};
96
97/// This is nn::settings::system::NotificationFlag
98struct NotificationFlag {
99 union {
100 u32 raw{};
101
102 BitField<0, 1, u32> RingtoneFlag;
103 BitField<1, 1, u32> DownloadCompletionFlag;
104 BitField<8, 1, u32> EnablesNews;
105 BitField<9, 1, u32> IncomingLampFlag;
106 };
107};
108static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size");
109
110/// This is nn::settings::system::NotificationTime
111struct NotificationTime {
112 u32 hour;
113 u32 minute;
114};
115static_assert(sizeof(NotificationTime) == 0x8, "NotificationTime is an invalid size");
116
117/// This is nn::settings::system::NotificationSettings
118struct NotificationSettings {
119 NotificationFlag flags;
120 NotificationVolume volume;
121 NotificationTime start_time;
122 NotificationTime stop_time;
123};
124static_assert(sizeof(NotificationSettings) == 0x18, "NotificationSettings is an invalid size");
125
126/// This is nn::settings::system::AccountNotificationFlag
127struct AccountNotificationFlag {
128 union {
129 u32 raw{};
130
131 BitField<0, 1, u32> FriendOnlineFlag;
132 BitField<1, 1, u32> FriendRequestFlag;
133 BitField<8, 1, u32> CoralInvitationFlag;
134 };
135};
136static_assert(sizeof(AccountNotificationFlag) == 4, "AccountNotificationFlag is an invalid size");
137
138/// This is nn::settings::system::FriendPresenceOverlayPermission
139enum class FriendPresenceOverlayPermission : u8 {
140 NotConfirmed,
141 NoDisplay,
142 FavoriteFriends,
143 Friends,
144};
145
146/// This is nn::settings::system::AccountNotificationSettings
147struct AccountNotificationSettings {
148 Common::UUID uid;
149 AccountNotificationFlag flags;
150 FriendPresenceOverlayPermission friend_presence_permission;
151 FriendPresenceOverlayPermission friend_invitation_permission;
152 INSERT_PADDING_BYTES(0x2);
153};
154static_assert(sizeof(AccountNotificationSettings) == 0x18,
155 "AccountNotificationSettings is an invalid size");
156
157/// This is nn::settings::system::TvFlag
158struct TvFlag {
159 union {
160 u32 raw{};
161
162 BitField<0, 1, u32> Allows4k;
163 BitField<1, 1, u32> Allows3d;
164 BitField<2, 1, u32> AllowsCec;
165 BitField<3, 1, u32> PreventsScreenBurnIn;
166 };
167};
168static_assert(sizeof(TvFlag) == 4, "TvFlag is an invalid size");
169
170/// This is nn::settings::system::TvResolution
171enum class TvResolution : u32 {
172 Auto,
173 Resolution1080p,
174 Resolution720p,
175 Resolution480p,
176};
177
178/// This is nn::settings::system::HdmiContentType
179enum class HdmiContentType : u32 {
180 None,
181 Graphics,
182 Cinema,
183 Photo,
184 Game,
185};
186
187/// This is nn::settings::system::RgbRange
188enum class RgbRange : u32 {
189 Auto,
190 Full,
191 Limited,
192};
193
194/// This is nn::settings::system::CmuMode
195enum class CmuMode : u32 {
196 None,
197 ColorInvert,
198 HighContrast,
199 GrayScale,
200};
201
202/// This is nn::settings::system::TvSettings
203struct TvSettings {
204 TvFlag flags;
205 TvResolution tv_resolution;
206 HdmiContentType hdmi_content_type;
207 RgbRange rgb_range;
208 CmuMode cmu_mode;
209 u32 tv_underscan;
210 f32 tv_gama;
211 f32 contrast_ratio;
212};
213static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size");
214
215/// This is nn::settings::system::PrimaryAlbumStorage
216enum class PrimaryAlbumStorage : u32 {
217 Nand,
218 SdCard,
219};
220
221/// This is nn::settings::system::HandheldSleepPlan
222enum class HandheldSleepPlan : u32 {
223 Sleep1Min,
224 Sleep3Min,
225 Sleep5Min,
226 Sleep10Min,
227 Sleep30Min,
228 Never,
229};
230
231/// This is nn::settings::system::ConsoleSleepPlan
232enum class ConsoleSleepPlan : u32 {
233 Sleep1Hour,
234 Sleep2Hour,
235 Sleep3Hour,
236 Sleep6Hour,
237 Sleep12Hour,
238 Never,
239};
240
241/// This is nn::settings::system::SleepFlag
242struct SleepFlag {
243 union {
244 u32 raw{};
245
246 BitField<0, 1, u32> SleepsWhilePlayingMedia;
247 BitField<1, 1, u32> WakesAtPowerStateChange;
248 };
249};
250static_assert(sizeof(SleepFlag) == 4, "TvFlag is an invalid size");
251
252/// This is nn::settings::system::SleepSettings
253struct SleepSettings {
254 SleepFlag flags;
255 HandheldSleepPlan handheld_sleep_plan;
256 ConsoleSleepPlan console_sleep_plan;
257};
258static_assert(sizeof(SleepSettings) == 0xc, "SleepSettings is incorrect size");
259
260/// This is nn::settings::system::EulaVersionClockType
261enum class EulaVersionClockType : u32 {
262 NetworkSystemClock,
263 SteadyClock,
264};
265
266/// This is nn::settings::system::EulaVersion
267struct EulaVersion {
268 u32 version;
269 RegionCode region_code;
270 EulaVersionClockType clock_type;
271 INSERT_PADDING_BYTES(0x4);
272 s64 posix_time;
273 Time::Clock::SteadyClockTimePoint timestamp;
274};
275static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
276
277struct SystemSettings { 19struct SystemSettings {
278 // 0/unwritten (1.0.0), 0x20000 (2.0.0), 0x30000 (3.0.0-3.0.1), 0x40001 (4.0.0-4.1.0), 0x50000 20 // 0/unwritten (1.0.0), 0x20000 (2.0.0), 0x30000 (3.0.0-3.0.1), 0x40001 (4.0.0-4.1.0), 0x50000
279 // (5.0.0-5.1.0), 0x60000 (6.0.0-6.2.0), 0x70000 (7.0.0), 0x80000 (8.0.0-8.1.1), 0x90000 21 // (5.0.0-5.1.0), 0x60000 (6.0.0-6.2.0), 0x70000 (7.0.0), 0x80000 (8.0.0-8.1.1), 0x90000
@@ -283,20 +25,16 @@ struct SystemSettings {
283 // 0/unwritten (1.0.0), 1 (6.0.0-8.1.0), 2 (8.1.1), 7 (9.0.0+). 25 // 0/unwritten (1.0.0), 1 (6.0.0-8.1.0), 2 (8.1.1), 7 (9.0.0+).
284 // if (flags & 2), defaults are written for AnalogStickUserCalibration 26 // if (flags & 2), defaults are written for AnalogStickUserCalibration
285 u32 flags; 27 u32 flags;
28 INSERT_PADDING_BYTES(0x8); // Reserved
286 29
287 std::array<u8, 0x8> reserved_00008;
288
289 // nn::settings::LanguageCode
290 LanguageCode language_code; 30 LanguageCode language_code;
291 31 INSERT_PADDING_BYTES(0x38); // Reserved
292 std::array<u8, 0x38> reserved_00018;
293 32
294 // nn::settings::system::NetworkSettings 33 // nn::settings::system::NetworkSettings
295 u32 network_setting_count; 34 u32 network_setting_count;
296 bool wireless_lan_enable_flag; 35 bool wireless_lan_enable_flag;
297 std::array<u8, 0x3> pad_00055; 36 INSERT_PADDING_BYTES(0x3);
298 37 INSERT_PADDING_BYTES(0x8); // Reserved
299 std::array<u8, 0x8> reserved_00058;
300 38
301 // nn::settings::system::NetworkSettings 39 // nn::settings::system::NetworkSettings
302 std::array<std::array<u8, 0x400>, 32> network_settings_1B0; 40 std::array<std::array<u8, 0x400>, 32> network_settings_1B0;
@@ -304,161 +42,142 @@ struct SystemSettings {
304 // nn::settings::system::BluetoothDevicesSettings 42 // nn::settings::system::BluetoothDevicesSettings
305 std::array<u8, 0x4> bluetooth_device_settings_count; 43 std::array<u8, 0x4> bluetooth_device_settings_count;
306 bool bluetooth_enable_flag; 44 bool bluetooth_enable_flag;
307 std::array<u8, 0x3> pad_08065; 45 INSERT_PADDING_BYTES(0x3);
308 bool bluetooth_afh_enable_flag; 46 bool bluetooth_afh_enable_flag;
309 std::array<u8, 0x3> pad_08069; 47 INSERT_PADDING_BYTES(0x3);
310 bool bluetooth_boost_enable_flag; 48 bool bluetooth_boost_enable_flag;
311 std::array<u8, 0x3> pad_0806D; 49 INSERT_PADDING_BYTES(0x3);
312 std::array<std::array<u8, 0x200>, 10> bluetooth_device_settings_first_10; 50 std::array<std::array<u8, 0x200>, 10> bluetooth_device_settings_first_10;
313 51
314 s32 ldn_channel; 52 s32 ldn_channel;
315 53 INSERT_PADDING_BYTES(0x3C); // Reserved
316 std::array<u8, 0x3C> reserved_09474;
317 54
318 // nn::util::Uuid MiiAuthorId 55 // nn::util::Uuid MiiAuthorId
319 std::array<u8, 0x10> mii_author_id; 56 Common::UUID mii_author_id;
320 57
321 std::array<u8, 0x30> reserved_094C0; 58 INSERT_PADDING_BYTES(0x30); // Reserved
322 59
323 // nn::settings::system::NxControllerSettings 60 // nn::settings::system::NxControllerSettings
324 u32 nx_controller_settings_count; 61 u32 nx_controller_settings_count;
325 62
326 std::array<u8, 0xC> reserved_094F4; 63 INSERT_PADDING_BYTES(0xC); // Reserved
327 64
328 // nn::settings::system::NxControllerSettings, 65 // nn::settings::system::NxControllerSettings,
329 // nn::settings::system::NxControllerLegacySettings on 13.0.0+ 66 // nn::settings::system::NxControllerLegacySettings on 13.0.0+
330 std::array<std::array<u8, 0x40>, 10> nx_controller_legacy_settings; 67 std::array<std::array<u8, 0x40>, 10> nx_controller_legacy_settings;
331 68 INSERT_PADDING_BYTES(0x170); // Reserved
332 std::array<u8, 0x170> reserved_09780;
333 69
334 bool external_rtc_reset_flag; 70 bool external_rtc_reset_flag;
335 std::array<u8, 0x3> pad_098F1; 71 INSERT_PADDING_BYTES(0x3);
336 72 INSERT_PADDING_BYTES(0x3C); // Reserved
337 std::array<u8, 0x3C> reserved_098F4;
338 73
339 s32 push_notification_activity_mode_on_sleep; 74 s32 push_notification_activity_mode_on_sleep;
75 INSERT_PADDING_BYTES(0x3C); // Reserved
340 76
341 std::array<u8, 0x3C> reserved_09934;
342
343 // nn::settings::system::ErrorReportSharePermission
344 ErrorReportSharePermission error_report_share_permission; 77 ErrorReportSharePermission error_report_share_permission;
78 INSERT_PADDING_BYTES(0x3C); // Reserved
345 79
346 std::array<u8, 0x3C> reserved_09974; 80 KeyboardLayout keyboard_layout;
347 81 INSERT_PADDING_BYTES(0x3C); // Reserved
348 // nn::settings::KeyboardLayout
349 std::array<u8, 0x4> keyboard_layout;
350
351 std::array<u8, 0x3C> reserved_099B4;
352 82
353 bool web_inspector_flag; 83 bool web_inspector_flag;
354 std::array<u8, 0x3> pad_099F1; 84 INSERT_PADDING_BYTES(0x3);
355 85
356 // nn::settings::system::AllowedSslHost 86 // nn::settings::system::AllowedSslHost
357 u32 allowed_ssl_host_count; 87 u32 allowed_ssl_host_count;
358 88
359 bool memory_usage_rate_flag; 89 bool memory_usage_rate_flag;
360 std::array<u8, 0x3> pad_099F9; 90 INSERT_PADDING_BYTES(0x3);
361 91 INSERT_PADDING_BYTES(0x34); // Reserved
362 std::array<u8, 0x34> reserved_099FC;
363 92
364 // nn::settings::system::HostFsMountPoint 93 // nn::settings::system::HostFsMountPoint
365 std::array<u8, 0x100> host_fs_mount_point; 94 std::array<u8, 0x100> host_fs_mount_point;
366 95
367 // nn::settings::system::AllowedSslHost 96 // nn::settings::system::AllowedSslHost
368 std::array<std::array<u8, 0x100>, 8> allowed_ssl_hosts; 97 std::array<std::array<u8, 0x100>, 8> allowed_ssl_hosts;
369 98 INSERT_PADDING_BYTES(0x6C0); // Reserved
370 std::array<u8, 0x6C0> reserved_0A330;
371 99
372 // nn::settings::system::BlePairingSettings 100 // nn::settings::system::BlePairingSettings
373 u32 ble_pairing_settings_count; 101 u32 ble_pairing_settings_count;
374 std::array<u8, 0xC> reserved_0A9F4; 102 INSERT_PADDING_BYTES(0xC); // Reserved
375 std::array<std::array<u8, 0x80>, 10> ble_pairing_settings; 103 std::array<std::array<u8, 0x80>, 10> ble_pairing_settings;
376 104
377 // nn::settings::system::AccountOnlineStorageSettings 105 // nn::settings::system::AccountOnlineStorageSettings
378 u32 account_online_storage_settings_count; 106 u32 account_online_storage_settings_count;
379 std::array<u8, 0xC> reserved_0AF04; 107 INSERT_PADDING_BYTES(0xC); // Reserved
380 std::array<std::array<u8, 0x40>, 8> account_online_storage_settings; 108 std::array<std::array<u8, 0x40>, 8> account_online_storage_settings;
381 109
382 bool pctl_ready_flag; 110 bool pctl_ready_flag;
383 std::array<u8, 0x3> pad_0B111; 111 INSERT_PADDING_BYTES(0x3);
384 112 INSERT_PADDING_BYTES(0x3C); // Reserved
385 std::array<u8, 0x3C> reserved_0B114;
386 113
387 // nn::settings::system::ThemeId 114 // nn::settings::system::ThemeId
388 std::array<u8, 0x80> theme_id_type0; 115 std::array<u8, 0x80> theme_id_type0;
389 std::array<u8, 0x80> theme_id_type1; 116 std::array<u8, 0x80> theme_id_type1;
117 INSERT_PADDING_BYTES(0x100); // Reserved
390 118
391 std::array<u8, 0x100> reserved_0B250;
392
393 // nn::settings::ChineseTraditionalInputMethod
394 ChineseTraditionalInputMethod chinese_traditional_input_method; 119 ChineseTraditionalInputMethod chinese_traditional_input_method;
395 120 INSERT_PADDING_BYTES(0x3C); // Reserved
396 std::array<u8, 0x3C> reserved_0B354;
397 121
398 bool zoom_flag; 122 bool zoom_flag;
399 std::array<u8, 0x3> pad_0B391; 123 INSERT_PADDING_BYTES(0x3);
400 124 INSERT_PADDING_BYTES(0x3C); // Reserved
401 std::array<u8, 0x3C> reserved_0B394;
402 125
403 // nn::settings::system::ButtonConfigRegisteredSettings 126 // nn::settings::system::ButtonConfigRegisteredSettings
404 u32 button_config_registered_settings_count; 127 u32 button_config_registered_settings_count;
405 std::array<u8, 0xC> reserved_0B3D4; 128 INSERT_PADDING_BYTES(0xC); // Reserved
406 129
407 // nn::settings::system::ButtonConfigSettings 130 // nn::settings::system::ButtonConfigSettings
408 u32 button_config_settings_count; 131 u32 button_config_settings_count;
409 std::array<u8, 0x4> reserved_0B3E4; 132 INSERT_PADDING_BYTES(0x4); // Reserved
410 std::array<std::array<u8, 0x5A8>, 5> button_config_settings; 133 std::array<std::array<u8, 0x5A8>, 5> button_config_settings;
411 std::array<u8, 0x13B0> reserved_0D030; 134 INSERT_PADDING_BYTES(0x13B0); // Reserved
412 u32 button_config_settings_embedded_count; 135 u32 button_config_settings_embedded_count;
413 std::array<u8, 0x4> reserved_0E3E4; 136 INSERT_PADDING_BYTES(0x4); // Reserved
414 std::array<std::array<u8, 0x5A8>, 5> button_config_settings_embedded; 137 std::array<std::array<u8, 0x5A8>, 5> button_config_settings_embedded;
415 std::array<u8, 0x13B0> reserved_10030; 138 INSERT_PADDING_BYTES(0x13B0); // Reserved
416 u32 button_config_settings_left_count; 139 u32 button_config_settings_left_count;
417 std::array<u8, 0x4> reserved_113E4; 140 INSERT_PADDING_BYTES(0x4); // Reserved
418 std::array<std::array<u8, 0x5A8>, 5> button_config_settings_left; 141 std::array<std::array<u8, 0x5A8>, 5> button_config_settings_left;
419 std::array<u8, 0x13B0> reserved_13030; 142 INSERT_PADDING_BYTES(0x13B0); // Reserved
420 u32 button_config_settings_right_count; 143 u32 button_config_settings_right_count;
421 std::array<u8, 0x4> reserved_143E4; 144 INSERT_PADDING_BYTES(0x4); // Reserved
422 std::array<std::array<u8, 0x5A8>, 5> button_config_settings_right; 145 std::array<std::array<u8, 0x5A8>, 5> button_config_settings_right;
423 std::array<u8, 0x73B0> reserved_16030; 146 INSERT_PADDING_BYTES(0x73B0); // Reserved
424 // nn::settings::system::ButtonConfigRegisteredSettings 147 // nn::settings::system::ButtonConfigRegisteredSettings
425 std::array<u8, 0x5C8> button_config_registered_settings_embedded; 148 std::array<u8, 0x5C8> button_config_registered_settings_embedded;
426 std::array<std::array<u8, 0x5C8>, 10> button_config_registered_settings; 149 std::array<std::array<u8, 0x5C8>, 10> button_config_registered_settings;
427 150 INSERT_PADDING_BYTES(0x7FF8); // Reserved
428 std::array<u8, 0x7FF8> reserved_21378;
429 151
430 // nn::settings::system::ConsoleSixAxisSensorAccelerationBias 152 // nn::settings::system::ConsoleSixAxisSensorAccelerationBias
431 std::array<u8, 0xC> console_six_axis_sensor_acceleration_bias; 153 Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
432 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias 154 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
433 std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_bias; 155 Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
434 // nn::settings::system::ConsoleSixAxisSensorAccelerationGain 156 // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
435 std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; 157 std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
436 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain 158 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
437 std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; 159 std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
438 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias 160 // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
439 std::array<u8, 0xC> console_six_axis_sensor_angular_velocity_time_bias; 161 Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
440 // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration 162 // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
441 std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration; 163 std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
442 164 INSERT_PADDING_BYTES(0x70); // Reserved
443 std::array<u8, 0x70> reserved_29400;
444 165
445 bool lock_screen_flag; 166 bool lock_screen_flag;
446 std::array<u8, 0x3> pad_29471; 167 INSERT_PADDING_BYTES(0x3);
447 168 INSERT_PADDING_BYTES(0x4); // Reserved
448 std::array<u8, 0x4> reserved_249274;
449 169
450 ColorSet color_set_id; 170 ColorSet color_set_id;
451 171
452 QuestFlag quest_flag; 172 QuestFlag quest_flag;
453 173
454 // nn::settings::system::RegionCode 174 SystemRegionCode region_code;
455 RegionCode region_code;
456 175
457 // Different to nn::settings::system::InitialLaunchSettings? 176 // Different to nn::settings::system::InitialLaunchSettings?
458 InitialLaunchSettingsPacked initial_launch_settings_packed; 177 InitialLaunchSettingsPacked initial_launch_settings_packed;
459 178
460 bool battery_percentage_flag; 179 bool battery_percentage_flag;
461 std::array<u8, 0x3> pad_294A1; 180 INSERT_PADDING_BYTES(0x3);
462 181
463 // BitFlagSet<32, nn::settings::system::AppletLaunchFlag> 182 // BitFlagSet<32, nn::settings::system::AppletLaunchFlag>
464 u32 applet_launch_flag; 183 u32 applet_launch_flag;
@@ -469,33 +188,26 @@ struct SystemSettings {
469 std::array<u8, 0x10> theme_key; 188 std::array<u8, 0x10> theme_key;
470 189
471 bool field_testing_flag; 190 bool field_testing_flag;
472 std::array<u8, 0x3> pad_294C1; 191 INSERT_PADDING_BYTES(0x3);
473 192
474 s32 panel_crc_mode; 193 s32 panel_crc_mode;
475 194 INSERT_PADDING_BYTES(0x28); // Reserved
476 std::array<u8, 0x28> reserved_294C8;
477 195
478 // nn::settings::system::BacklightSettings 196 // nn::settings::system::BacklightSettings
479 std::array<u8, 0x2C> backlight_settings_mixed_up; 197 std::array<u8, 0x2C> backlight_settings_mixed_up;
198 INSERT_PADDING_BYTES(0x64); // Reserved
480 199
481 std::array<u8, 0x64> reserved_2951C;
482
483 // nn::time::SystemClockContext
484 Service::Time::Clock::SystemClockContext user_system_clock_context; 200 Service::Time::Clock::SystemClockContext user_system_clock_context;
485 Service::Time::Clock::SystemClockContext network_system_clock_context; 201 Service::Time::Clock::SystemClockContext network_system_clock_context;
486 bool user_system_clock_automatic_correction_enabled; 202 bool user_system_clock_automatic_correction_enabled;
487 std::array<u8, 0x3> pad_295C1; 203 INSERT_PADDING_BYTES(0x3);
488 std::array<u8, 0x4> reserved_295C4; 204 INSERT_PADDING_BYTES(0x4); // Reserved
489 // nn::time::SteadyClockTimePoint
490 Service::Time::Clock::SteadyClockTimePoint 205 Service::Time::Clock::SteadyClockTimePoint
491 user_system_clock_automatic_correction_updated_time_point; 206 user_system_clock_automatic_correction_updated_time_point;
207 INSERT_PADDING_BYTES(0x10); // Reserved
492 208
493 std::array<u8, 0x10> reserved_295E0;
494
495 // nn::settings::system::AccountSettings
496 AccountSettings account_settings; 209 AccountSettings account_settings;
497 210 INSERT_PADDING_BYTES(0xFC); // Reserved
498 std::array<u8, 0xFC> reserved_295F4;
499 211
500 // nn::settings::system::AudioVolume 212 // nn::settings::system::AudioVolume
501 std::array<u8, 0x8> audio_volume_type0; 213 std::array<u8, 0x8> audio_volume_type0;
@@ -505,47 +217,42 @@ struct SystemSettings {
505 s32 audio_output_mode_type1; 217 s32 audio_output_mode_type1;
506 s32 audio_output_mode_type2; 218 s32 audio_output_mode_type2;
507 bool force_mute_on_headphone_removed; 219 bool force_mute_on_headphone_removed;
508 std::array<u8, 0x3> pad_2970D; 220 INSERT_PADDING_BYTES(0x3);
509 s32 headphone_volume_warning_count; 221 s32 headphone_volume_warning_count;
510 bool heaphone_volume_update_flag; 222 bool heaphone_volume_update_flag;
511 std::array<u8, 0x3> pad_29715; 223 INSERT_PADDING_BYTES(0x3);
512 // nn::settings::system::AudioVolume 224 // nn::settings::system::AudioVolume
513 std::array<u8, 0x8> audio_volume_type2; 225 std::array<u8, 0x8> audio_volume_type2;
514 // nn::settings::system::AudioOutputMode 226 // nn::settings::system::AudioOutputMode
515 s32 audio_output_mode_type3; 227 s32 audio_output_mode_type3;
516 s32 audio_output_mode_type4; 228 s32 audio_output_mode_type4;
517 bool hearing_protection_safeguard_flag; 229 bool hearing_protection_safeguard_flag;
518 std::array<u8, 0x3> pad_29729; 230 INSERT_PADDING_BYTES(0x3);
519 std::array<u8, 0x4> reserved_2972C; 231 INSERT_PADDING_BYTES(0x4); // Reserved
520 s64 hearing_protection_safeguard_remaining_time; 232 s64 hearing_protection_safeguard_remaining_time;
521 std::array<u8, 0x38> reserved_29738; 233 INSERT_PADDING_BYTES(0x38); // Reserved
522 234
523 bool console_information_upload_flag; 235 bool console_information_upload_flag;
524 std::array<u8, 0x3> pad_29771; 236 INSERT_PADDING_BYTES(0x3);
525 237 INSERT_PADDING_BYTES(0x3C); // Reserved
526 std::array<u8, 0x3C> reserved_29774;
527 238
528 bool automatic_application_download_flag; 239 bool automatic_application_download_flag;
529 std::array<u8, 0x3> pad_297B1; 240 INSERT_PADDING_BYTES(0x3);
530 241 INSERT_PADDING_BYTES(0x4); // Reserved
531 std::array<u8, 0x4> reserved_297B4;
532 242
533 // nn::settings::system::NotificationSettings
534 NotificationSettings notification_settings; 243 NotificationSettings notification_settings;
535 244 INSERT_PADDING_BYTES(0x60); // Reserved
536 std::array<u8, 0x60> reserved_297D0;
537 245
538 // nn::settings::system::AccountNotificationSettings 246 // nn::settings::system::AccountNotificationSettings
539 u32 account_notification_settings_count; 247 u32 account_notification_settings_count;
540 std::array<u8, 0xC> reserved_29834; 248 INSERT_PADDING_BYTES(0xC); // Reserved
541 std::array<AccountNotificationSettings, 8> account_notification_settings; 249 std::array<AccountNotificationSettings, 8> account_notification_settings;
542 250 INSERT_PADDING_BYTES(0x140); // Reserved
543 std::array<u8, 0x140> reserved_29900;
544 251
545 f32 vibration_master_volume; 252 f32 vibration_master_volume;
546 253
547 bool usb_full_key_enable_flag; 254 bool usb_full_key_enable_flag;
548 std::array<u8, 0x3> pad_29A45; 255 INSERT_PADDING_BYTES(0x3);
549 256
550 // nn::settings::system::AnalogStickUserCalibration 257 // nn::settings::system::AnalogStickUserCalibration
551 std::array<u8, 0x10> analog_stick_user_calibration_left; 258 std::array<u8, 0x10> analog_stick_user_calibration_left;
@@ -553,85 +260,68 @@ struct SystemSettings {
553 260
554 // nn::settings::system::TouchScreenMode 261 // nn::settings::system::TouchScreenMode
555 s32 touch_screen_mode; 262 s32 touch_screen_mode;
263 INSERT_PADDING_BYTES(0x14); // Reserved
556 264
557 std::array<u8, 0x14> reserved_29A6C;
558
559 // nn::settings::system::TvSettings
560 TvSettings tv_settings; 265 TvSettings tv_settings;
561 266
562 // nn::settings::system::Edid 267 // nn::settings::system::Edid
563 std::array<u8, 0x100> edid; 268 std::array<u8, 0x100> edid;
564 269 INSERT_PADDING_BYTES(0x2E0); // Reserved
565 std::array<u8, 0x2E0> reserved_29BA0;
566 270
567 // nn::settings::system::DataDeletionSettings 271 // nn::settings::system::DataDeletionSettings
568 std::array<u8, 0x8> data_deletion_settings; 272 std::array<u8, 0x8> data_deletion_settings;
569 273 INSERT_PADDING_BYTES(0x38); // Reserved
570 std::array<u8, 0x38> reserved_29E88;
571 274
572 // nn::ncm::ProgramId 275 // nn::ncm::ProgramId
573 std::array<u8, 0x8> initial_system_applet_program_id; 276 std::array<u8, 0x8> initial_system_applet_program_id;
574 std::array<u8, 0x8> overlay_disp_program_id; 277 std::array<u8, 0x8> overlay_disp_program_id;
575 278 INSERT_PADDING_BYTES(0x4); // Reserved
576 std::array<u8, 0x4> reserved_29ED0;
577 279
578 bool requires_run_repair_time_reviser; 280 bool requires_run_repair_time_reviser;
281 INSERT_PADDING_BYTES(0x6B); // Reserved
579 282
580 std::array<u8, 0x6B> reserved_29ED5;
581
582 // nn::time::LocationName
583 Service::Time::TimeZone::LocationName device_time_zone_location_name; 283 Service::Time::TimeZone::LocationName device_time_zone_location_name;
584 std::array<u8, 0x4> reserved_29F64; 284 INSERT_PADDING_BYTES(0x4); // Reserved
585 // nn::time::SteadyClockTimePoint
586 Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time; 285 Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time;
587 286 INSERT_PADDING_BYTES(0xC0); // Reserved
588 std::array<u8, 0xC0> reserved_29F80;
589 287
590 // nn::settings::system::PrimaryAlbumStorage 288 // nn::settings::system::PrimaryAlbumStorage
591 PrimaryAlbumStorage primary_album_storage; 289 PrimaryAlbumStorage primary_album_storage;
592 290 INSERT_PADDING_BYTES(0x3C); // Reserved
593 std::array<u8, 0x3C> reserved_2A044;
594 291
595 bool usb_30_enable_flag; 292 bool usb_30_enable_flag;
596 std::array<u8, 0x3> pad_2A081; 293 INSERT_PADDING_BYTES(0x3);
597 bool usb_30_host_enable_flag; 294 bool usb_30_host_enable_flag;
598 std::array<u8, 0x3> pad_2A085; 295 INSERT_PADDING_BYTES(0x3);
599 bool usb_30_device_enable_flag; 296 bool usb_30_device_enable_flag;
600 std::array<u8, 0x3> pad_2A089; 297 INSERT_PADDING_BYTES(0x3);
601 298 INSERT_PADDING_BYTES(0x34); // Reserved
602 std::array<u8, 0x34> reserved_2A08C;
603 299
604 bool nfc_enable_flag; 300 bool nfc_enable_flag;
605 std::array<u8, 0x3> pad_2A0C1; 301 INSERT_PADDING_BYTES(0x3);
606 302 INSERT_PADDING_BYTES(0x3C); // Reserved
607 std::array<u8, 0x3C> reserved_2A0C4;
608 303
609 // nn::settings::system::SleepSettings 304 // nn::settings::system::SleepSettings
610 SleepSettings sleep_settings; 305 SleepSettings sleep_settings;
611 306 INSERT_PADDING_BYTES(0x34); // Reserved
612 std::array<u8, 0x34> reserved_2A10C;
613 307
614 // nn::settings::system::EulaVersion 308 // nn::settings::system::EulaVersion
615 u32 eula_version_count; 309 u32 eula_version_count;
616 std::array<u8, 0xC> reserved_2A144; 310 INSERT_PADDING_BYTES(0xC); // Reserved
617 std::array<EulaVersion, 32> eula_versions; 311 std::array<EulaVersion, 32> eula_versions;
618 312 INSERT_PADDING_BYTES(0x200); // Reserved
619 std::array<u8, 0x200> reserved_2A750;
620 313
621 // nn::settings::system::DeviceNickName 314 // nn::settings::system::DeviceNickName
622 std::array<u8, 0x80> device_nick_name; 315 std::array<u8, 0x80> device_nick_name;
623 316 INSERT_PADDING_BYTES(0x80); // Reserved
624 std::array<u8, 0x80> reserved_2A9D0;
625 317
626 bool auto_update_enable_flag; 318 bool auto_update_enable_flag;
627 std::array<u8, 0x3> pad_2AA51; 319 INSERT_PADDING_BYTES(0x3);
628 320 INSERT_PADDING_BYTES(0x4C); // Reserved
629 std::array<u8, 0x4C> reserved_2AA54;
630 321
631 // nn::settings::system::BluetoothDevicesSettings 322 // nn::settings::system::BluetoothDevicesSettings
632 std::array<std::array<u8, 0x200>, 14> bluetooth_device_settings_last_14; 323 std::array<std::array<u8, 0x200>, 14> bluetooth_device_settings_last_14;
633 324 INSERT_PADDING_BYTES(0x2000); // Reserved
634 std::array<u8, 0x2000> reserved_2C6A0;
635 325
636 // nn::settings::system::NxControllerSettings 326 // nn::settings::system::NxControllerSettings
637 std::array<std::array<u8, 0x800>, 10> nx_controller_settings_data_from_offset_30; 327 std::array<std::array<u8, 0x800>, 10> nx_controller_settings_data_from_offset_30;
diff --git a/src/core/hle/service/set/settings_server.h b/src/core/hle/service/set/settings_server.h
index a4e78db6c..8304e8424 100644
--- a/src/core/hle/service/set/settings_server.h
+++ b/src/core/hle/service/set/settings_server.h
@@ -4,72 +4,13 @@
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/service.h" 6#include "core/hle/service/service.h"
7#include "core/hle/service/set/system_settings.h" 7#include "core/hle/service/set/settings_types.h"
8 8
9namespace Core { 9namespace Core {
10class System; 10class System;
11} 11}
12 12
13namespace Service::Set { 13namespace Service::Set {
14enum class KeyboardLayout : u64 {
15 Japanese = 0,
16 EnglishUs = 1,
17 EnglishUsInternational = 2,
18 EnglishUk = 3,
19 French = 4,
20 FrenchCa = 5,
21 Spanish = 6,
22 SpanishLatin = 7,
23 German = 8,
24 Italian = 9,
25 Portuguese = 10,
26 Russian = 11,
27 Korean = 12,
28 ChineseSimplified = 13,
29 ChineseTraditional = 14,
30};
31
32constexpr std::array<LanguageCode, 18> available_language_codes = {{
33 LanguageCode::JA,
34 LanguageCode::EN_US,
35 LanguageCode::FR,
36 LanguageCode::DE,
37 LanguageCode::IT,
38 LanguageCode::ES,
39 LanguageCode::ZH_CN,
40 LanguageCode::KO,
41 LanguageCode::NL,
42 LanguageCode::PT,
43 LanguageCode::RU,
44 LanguageCode::ZH_TW,
45 LanguageCode::EN_GB,
46 LanguageCode::FR_CA,
47 LanguageCode::ES_419,
48 LanguageCode::ZH_HANS,
49 LanguageCode::ZH_HANT,
50 LanguageCode::PT_BR,
51}};
52
53static constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
54 {LanguageCode::JA, KeyboardLayout::Japanese},
55 {LanguageCode::EN_US, KeyboardLayout::EnglishUs},
56 {LanguageCode::FR, KeyboardLayout::French},
57 {LanguageCode::DE, KeyboardLayout::German},
58 {LanguageCode::IT, KeyboardLayout::Italian},
59 {LanguageCode::ES, KeyboardLayout::Spanish},
60 {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified},
61 {LanguageCode::KO, KeyboardLayout::Korean},
62 {LanguageCode::NL, KeyboardLayout::EnglishUsInternational},
63 {LanguageCode::PT, KeyboardLayout::Portuguese},
64 {LanguageCode::RU, KeyboardLayout::Russian},
65 {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional},
66 {LanguageCode::EN_GB, KeyboardLayout::EnglishUk},
67 {LanguageCode::FR_CA, KeyboardLayout::FrenchCa},
68 {LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
69 {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
70 {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
71 {LanguageCode::PT_BR, KeyboardLayout::Portuguese},
72}};
73 14
74LanguageCode GetLanguageCodeFromIndex(std::size_t idx); 15LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
75 16
diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h
new file mode 100644
index 000000000..4dee202d7
--- /dev/null
+++ b/src/core/hle/service/set/settings_types.h
@@ -0,0 +1,451 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/uuid.h"
12#include "core/hle/service/time/clock_types.h"
13
14namespace Service::Set {
15
16/// This is nn::settings::system::AudioOutputMode
17enum class AudioOutputMode : u32 {
18 ch_1,
19 ch_2,
20 ch_5_1,
21 ch_7_1,
22};
23
24/// This is nn::settings::system::AudioOutputModeTarget
25enum class AudioOutputModeTarget : u32 {
26 Hdmi,
27 Speaker,
28 Headphone,
29};
30
31/// This is nn::settings::system::AudioVolumeTarget
32enum class AudioVolumeTarget : u32 {
33 Speaker,
34 Headphone,
35};
36
37/// This is nn::settings::system::ClockSourceId
38enum class ClockSourceId : u32 {
39 NetworkSystemClock,
40 SteadyClock,
41};
42
43/// This is nn::settings::system::CmuMode
44enum class CmuMode : u32 {
45 None,
46 ColorInvert,
47 HighContrast,
48 GrayScale,
49};
50
51/// This is nn::settings::system::ChineseTraditionalInputMethod
52enum class ChineseTraditionalInputMethod : u32 {
53 Unknown0 = 0,
54 Unknown1 = 1,
55 Unknown2 = 2,
56};
57
58/// Indicates the current theme set by the system settings
59enum class ColorSet : u32 {
60 BasicWhite = 0,
61 BasicBlack = 1,
62};
63
64/// This is nn::settings::system::ConsoleSleepPlan
65enum class ConsoleSleepPlan : u32 {
66 Sleep1Hour,
67 Sleep2Hour,
68 Sleep3Hour,
69 Sleep6Hour,
70 Sleep12Hour,
71 Never,
72};
73
74/// This is nn::settings::system::ErrorReportSharePermission
75enum class ErrorReportSharePermission : u32 {
76 NotConfirmed,
77 Granted,
78 Denied,
79};
80
81/// This is nn::settings::system::EulaVersionClockType
82enum class EulaVersionClockType : u32 {
83 NetworkSystemClock,
84 SteadyClock,
85};
86
87/// This is nn::settings::factory::RegionCode
88enum class FactoryRegionCode : u32 {
89 Japan,
90 Usa,
91 Europe,
92 Australia,
93 China,
94 Korea,
95 Taiwan,
96};
97
98/// This is nn::settings::system::FriendPresenceOverlayPermission
99enum class FriendPresenceOverlayPermission : u8 {
100 NotConfirmed,
101 NoDisplay,
102 FavoriteFriends,
103 Friends,
104};
105
106enum class GetFirmwareVersionType {
107 Version1,
108 Version2,
109};
110
111/// This is nn::settings::system::HandheldSleepPlan
112enum class HandheldSleepPlan : u32 {
113 Sleep1Min,
114 Sleep3Min,
115 Sleep5Min,
116 Sleep10Min,
117 Sleep30Min,
118 Never,
119};
120
121/// This is nn::settings::system::HdmiContentType
122enum class HdmiContentType : u32 {
123 None,
124 Graphics,
125 Cinema,
126 Photo,
127 Game,
128};
129
130enum class KeyboardLayout : u32 {
131 Japanese = 0,
132 EnglishUs = 1,
133 EnglishUsInternational = 2,
134 EnglishUk = 3,
135 French = 4,
136 FrenchCa = 5,
137 Spanish = 6,
138 SpanishLatin = 7,
139 German = 8,
140 Italian = 9,
141 Portuguese = 10,
142 Russian = 11,
143 Korean = 12,
144 ChineseSimplified = 13,
145 ChineseTraditional = 14,
146};
147
148/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64.
149enum class LanguageCode : u64 {
150 JA = 0x000000000000616A,
151 EN_US = 0x00000053552D6E65,
152 FR = 0x0000000000007266,
153 DE = 0x0000000000006564,
154 IT = 0x0000000000007469,
155 ES = 0x0000000000007365,
156 ZH_CN = 0x0000004E432D687A,
157 KO = 0x0000000000006F6B,
158 NL = 0x0000000000006C6E,
159 PT = 0x0000000000007470,
160 RU = 0x0000000000007572,
161 ZH_TW = 0x00000057542D687A,
162 EN_GB = 0x00000042472D6E65,
163 FR_CA = 0x00000041432D7266,
164 ES_419 = 0x00003931342D7365,
165 ZH_HANS = 0x00736E61482D687A,
166 ZH_HANT = 0x00746E61482D687A,
167 PT_BR = 0x00000052422D7470,
168};
169
170/// This is nn::settings::system::NotificationVolume
171enum class NotificationVolume : u32 {
172 Mute,
173 Low,
174 High,
175};
176
177/// This is nn::settings::system::PrimaryAlbumStorage
178enum class PrimaryAlbumStorage : u32 {
179 Nand,
180 SdCard,
181};
182
183/// Indicates the current console is a retail or kiosk unit
184enum class QuestFlag : u8 {
185 Retail = 0,
186 Kiosk = 1,
187};
188
189/// This is nn::settings::system::RgbRange
190enum class RgbRange : u32 {
191 Auto,
192 Full,
193 Limited,
194};
195
196/// This is nn::settings::system::RegionCode
197enum class SystemRegionCode : u32 {
198 Japan,
199 Usa,
200 Europe,
201 Australia,
202 HongKongTaiwanKorea,
203 China,
204};
205
206/// This is nn::settings::system::TouchScreenMode
207enum class TouchScreenMode : u32 {
208 Stylus,
209 Standard,
210};
211
212/// This is nn::settings::system::TvResolution
213enum class TvResolution : u32 {
214 Auto,
215 Resolution1080p,
216 Resolution720p,
217 Resolution480p,
218};
219
220constexpr std::array<LanguageCode, 18> available_language_codes = {{
221 LanguageCode::JA,
222 LanguageCode::EN_US,
223 LanguageCode::FR,
224 LanguageCode::DE,
225 LanguageCode::IT,
226 LanguageCode::ES,
227 LanguageCode::ZH_CN,
228 LanguageCode::KO,
229 LanguageCode::NL,
230 LanguageCode::PT,
231 LanguageCode::RU,
232 LanguageCode::ZH_TW,
233 LanguageCode::EN_GB,
234 LanguageCode::FR_CA,
235 LanguageCode::ES_419,
236 LanguageCode::ZH_HANS,
237 LanguageCode::ZH_HANT,
238 LanguageCode::PT_BR,
239}};
240
241static constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
242 {LanguageCode::JA, KeyboardLayout::Japanese},
243 {LanguageCode::EN_US, KeyboardLayout::EnglishUs},
244 {LanguageCode::FR, KeyboardLayout::French},
245 {LanguageCode::DE, KeyboardLayout::German},
246 {LanguageCode::IT, KeyboardLayout::Italian},
247 {LanguageCode::ES, KeyboardLayout::Spanish},
248 {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified},
249 {LanguageCode::KO, KeyboardLayout::Korean},
250 {LanguageCode::NL, KeyboardLayout::EnglishUsInternational},
251 {LanguageCode::PT, KeyboardLayout::Portuguese},
252 {LanguageCode::RU, KeyboardLayout::Russian},
253 {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional},
254 {LanguageCode::EN_GB, KeyboardLayout::EnglishUk},
255 {LanguageCode::FR_CA, KeyboardLayout::FrenchCa},
256 {LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
257 {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
258 {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
259 {LanguageCode::PT_BR, KeyboardLayout::Portuguese},
260}};
261
262/// This is nn::settings::system::AccountNotificationFlag
263struct AccountNotificationFlag {
264 union {
265 u32 raw{};
266
267 BitField<0, 1, u32> FriendOnlineFlag;
268 BitField<1, 1, u32> FriendRequestFlag;
269 BitField<8, 1, u32> CoralInvitationFlag;
270 };
271};
272static_assert(sizeof(AccountNotificationFlag) == 4, "AccountNotificationFlag is an invalid size");
273
274/// This is nn::settings::system::AccountSettings
275struct AccountSettings {
276 u32 flags;
277};
278static_assert(sizeof(AccountSettings) == 4, "AccountSettings is an invalid size");
279
280/// This is nn::settings::system::DataDeletionFlag
281struct DataDeletionFlag {
282 union {
283 u32 raw{};
284
285 BitField<0, 1, u32> AutomaticDeletionFlag;
286 };
287};
288static_assert(sizeof(DataDeletionFlag) == 4, "DataDeletionFlag is an invalid size");
289
290/// This is nn::settings::system::InitialLaunchFlag
291struct InitialLaunchFlag {
292 union {
293 u32 raw{};
294
295 BitField<0, 1, u32> InitialLaunchCompletionFlag;
296 BitField<8, 1, u32> InitialLaunchUserAdditionFlag;
297 BitField<16, 1, u32> InitialLaunchTimestampFlag;
298 };
299};
300static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size");
301
302/// This is nn::settings::system::SleepFlag
303struct SleepFlag {
304 union {
305 u32 raw{};
306
307 BitField<0, 1, u32> SleepsWhilePlayingMedia;
308 BitField<1, 1, u32> WakesAtPowerStateChange;
309 };
310};
311static_assert(sizeof(SleepFlag) == 4, "TvFlag is an invalid size");
312
313/// This is nn::settings::system::NotificationFlag
314struct NotificationFlag {
315 union {
316 u32 raw{};
317
318 BitField<0, 1, u32> RingtoneFlag;
319 BitField<1, 1, u32> DownloadCompletionFlag;
320 BitField<8, 1, u32> EnablesNews;
321 BitField<9, 1, u32> IncomingLampFlag;
322 };
323};
324static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size");
325
326/// This is nn::settings::system::TvFlag
327struct TvFlag {
328 union {
329 u32 raw{};
330
331 BitField<0, 1, u32> Allows4k;
332 BitField<1, 1, u32> Allows3d;
333 BitField<2, 1, u32> AllowsCec;
334 BitField<3, 1, u32> PreventsScreenBurnIn;
335 };
336};
337static_assert(sizeof(TvFlag) == 4, "TvFlag is an invalid size");
338
339/// This is nn::settings::system::UserSelectorFlag
340struct UserSelectorFlag {
341 union {
342 u32 raw{};
343
344 BitField<0, 1, u32> SkipIfSingleUser;
345 BitField<31, 1, u32> Unknown;
346 };
347};
348static_assert(sizeof(UserSelectorFlag) == 4, "UserSelectorFlag is an invalid size");
349
350/// This is nn::settings::system::AccountNotificationSettings
351struct AccountNotificationSettings {
352 Common::UUID uid;
353 AccountNotificationFlag flags;
354 FriendPresenceOverlayPermission friend_presence_permission;
355 FriendPresenceOverlayPermission friend_invitation_permission;
356 INSERT_PADDING_BYTES(0x2);
357};
358static_assert(sizeof(AccountNotificationSettings) == 0x18,
359 "AccountNotificationSettings is an invalid size");
360
361/// This is nn::settings::system::EulaVersion
362struct EulaVersion {
363 u32 version;
364 SystemRegionCode region_code;
365 EulaVersionClockType clock_type;
366 INSERT_PADDING_BYTES(0x4);
367 s64 posix_time;
368 Time::Clock::SteadyClockTimePoint timestamp;
369};
370static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
371
372struct FirmwareVersionFormat {
373 u8 major;
374 u8 minor;
375 u8 micro;
376 INSERT_PADDING_BYTES(1);
377 u8 revision_major;
378 u8 revision_minor;
379 INSERT_PADDING_BYTES(2);
380 std::array<char, 0x20> platform;
381 std::array<u8, 0x40> version_hash;
382 std::array<char, 0x18> display_version;
383 std::array<char, 0x80> display_title;
384};
385static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size");
386
387/// This is nn::settings::system::HomeMenuScheme
388struct HomeMenuScheme {
389 u32 main;
390 u32 back;
391 u32 sub;
392 u32 bezel;
393 u32 extra;
394};
395static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size");
396
397/// This is nn::settings::system::InitialLaunchSettings
398struct InitialLaunchSettings {
399 InitialLaunchFlag flags;
400 INSERT_PADDING_BYTES(0x4);
401 Service::Time::Clock::SteadyClockTimePoint timestamp;
402};
403static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
404
405#pragma pack(push, 4)
406struct InitialLaunchSettingsPacked {
407 InitialLaunchFlag flags;
408 Service::Time::Clock::SteadyClockTimePoint timestamp;
409};
410#pragma pack(pop)
411static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C,
412 "InitialLaunchSettingsPacked is incorrect size");
413
414/// This is nn::settings::system::NotificationTime
415struct NotificationTime {
416 u32 hour;
417 u32 minute;
418};
419static_assert(sizeof(NotificationTime) == 0x8, "NotificationTime is an invalid size");
420
421/// This is nn::settings::system::NotificationSettings
422struct NotificationSettings {
423 NotificationFlag flags;
424 NotificationVolume volume;
425 NotificationTime start_time;
426 NotificationTime stop_time;
427};
428static_assert(sizeof(NotificationSettings) == 0x18, "NotificationSettings is an invalid size");
429
430/// This is nn::settings::system::SleepSettings
431struct SleepSettings {
432 SleepFlag flags;
433 HandheldSleepPlan handheld_sleep_plan;
434 ConsoleSleepPlan console_sleep_plan;
435};
436static_assert(sizeof(SleepSettings) == 0xc, "SleepSettings is incorrect size");
437
438/// This is nn::settings::system::TvSettings
439struct TvSettings {
440 TvFlag flags;
441 TvResolution tv_resolution;
442 HdmiContentType hdmi_content_type;
443 RgbRange rgb_range;
444 CmuMode cmu_mode;
445 u32 tv_underscan;
446 f32 tv_gama;
447 f32 contrast_ratio;
448};
449static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size");
450
451} // namespace Service::Set
diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp
index af9348522..87242ae68 100644
--- a/src/core/hle/service/set/system_settings_server.cpp
+++ b/src/core/hle/service/set/system_settings_server.cpp
@@ -97,8 +97,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
97 {3, &ISystemSettingsServer::GetFirmwareVersion, "GetFirmwareVersion"}, 97 {3, &ISystemSettingsServer::GetFirmwareVersion, "GetFirmwareVersion"},
98 {4, &ISystemSettingsServer::GetFirmwareVersion2, "GetFirmwareVersion2"}, 98 {4, &ISystemSettingsServer::GetFirmwareVersion2, "GetFirmwareVersion2"},
99 {5, nullptr, "GetFirmwareVersionDigest"}, 99 {5, nullptr, "GetFirmwareVersionDigest"},
100 {7, nullptr, "GetLockScreenFlag"}, 100 {7, &ISystemSettingsServer::GetLockScreenFlag, "GetLockScreenFlag"},
101 {8, nullptr, "SetLockScreenFlag"}, 101 {8, &ISystemSettingsServer::SetLockScreenFlag, "SetLockScreenFlag"},
102 {9, nullptr, "GetBacklightSettings"}, 102 {9, nullptr, "GetBacklightSettings"},
103 {10, nullptr, "SetBacklightSettings"}, 103 {10, nullptr, "SetBacklightSettings"},
104 {11, nullptr, "SetBluetoothDevicesSettings"}, 104 {11, nullptr, "SetBluetoothDevicesSettings"},
@@ -157,12 +157,12 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
157 {66, nullptr, "SetUsb30EnableFlag"}, 157 {66, nullptr, "SetUsb30EnableFlag"},
158 {67, nullptr, "GetBatteryLot"}, 158 {67, nullptr, "GetBatteryLot"},
159 {68, nullptr, "GetSerialNumber"}, 159 {68, nullptr, "GetSerialNumber"},
160 {69, nullptr, "GetNfcEnableFlag"}, 160 {69, &ISystemSettingsServer::GetNfcEnableFlag, "GetNfcEnableFlag"},
161 {70, nullptr, "SetNfcEnableFlag"}, 161 {70, &ISystemSettingsServer::SetNfcEnableFlag, "SetNfcEnableFlag"},
162 {71, &ISystemSettingsServer::GetSleepSettings, "GetSleepSettings"}, 162 {71, &ISystemSettingsServer::GetSleepSettings, "GetSleepSettings"},
163 {72, &ISystemSettingsServer::SetSleepSettings, "SetSleepSettings"}, 163 {72, &ISystemSettingsServer::SetSleepSettings, "SetSleepSettings"},
164 {73, nullptr, "GetWirelessLanEnableFlag"}, 164 {73, &ISystemSettingsServer::GetWirelessLanEnableFlag, "GetWirelessLanEnableFlag"},
165 {74, nullptr, "SetWirelessLanEnableFlag"}, 165 {74, &ISystemSettingsServer::SetWirelessLanEnableFlag, "SetWirelessLanEnableFlag"},
166 {75, &ISystemSettingsServer::GetInitialLaunchSettings, "GetInitialLaunchSettings"}, 166 {75, &ISystemSettingsServer::GetInitialLaunchSettings, "GetInitialLaunchSettings"},
167 {76, &ISystemSettingsServer::SetInitialLaunchSettings, "SetInitialLaunchSettings"}, 167 {76, &ISystemSettingsServer::SetInitialLaunchSettings, "SetInitialLaunchSettings"},
168 {77, &ISystemSettingsServer::GetDeviceNickName, "GetDeviceNickName"}, 168 {77, &ISystemSettingsServer::GetDeviceNickName, "GetDeviceNickName"},
@@ -176,8 +176,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
176 {85, nullptr, "SetPtmBatteryLot"}, 176 {85, nullptr, "SetPtmBatteryLot"},
177 {86, nullptr, "GetPtmFuelGaugeParameter"}, 177 {86, nullptr, "GetPtmFuelGaugeParameter"},
178 {87, nullptr, "SetPtmFuelGaugeParameter"}, 178 {87, nullptr, "SetPtmFuelGaugeParameter"},
179 {88, nullptr, "GetBluetoothEnableFlag"}, 179 {88, &ISystemSettingsServer::GetBluetoothEnableFlag, "GetBluetoothEnableFlag"},
180 {89, nullptr, "SetBluetoothEnableFlag"}, 180 {89, &ISystemSettingsServer::SetBluetoothEnableFlag, "SetBluetoothEnableFlag"},
181 {90, &ISystemSettingsServer::GetMiiAuthorId, "GetMiiAuthorId"}, 181 {90, &ISystemSettingsServer::GetMiiAuthorId, "GetMiiAuthorId"},
182 {91, nullptr, "SetShutdownRtcValue"}, 182 {91, nullptr, "SetShutdownRtcValue"},
183 {92, nullptr, "GetShutdownRtcValue"}, 183 {92, nullptr, "GetShutdownRtcValue"},
@@ -510,6 +510,25 @@ void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) {
510 rb.Push(res); 510 rb.Push(res);
511} 511}
512 512
513void ISystemSettingsServer::GetLockScreenFlag(HLERequestContext& ctx) {
514 LOG_INFO(Service_SET, "called, lock_screen_flag={}", m_system_settings.lock_screen_flag);
515
516 IPC::ResponseBuilder rb{ctx, 3};
517 rb.Push(ResultSuccess);
518 rb.Push(m_system_settings.lock_screen_flag);
519}
520
521void ISystemSettingsServer::SetLockScreenFlag(HLERequestContext& ctx) {
522 IPC::RequestParser rp{ctx};
523 m_system_settings.lock_screen_flag = rp.Pop<bool>();
524 SetSaveNeeded();
525
526 LOG_INFO(Service_SET, "called, lock_screen_flag={}", m_system_settings.lock_screen_flag);
527
528 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(ResultSuccess);
530}
531
513void ISystemSettingsServer::GetAccountSettings(HLERequestContext& ctx) { 532void ISystemSettingsServer::GetAccountSettings(HLERequestContext& ctx) {
514 LOG_INFO(Service_SET, "called"); 533 LOG_INFO(Service_SET, "called");
515 534
@@ -531,7 +550,7 @@ void ISystemSettingsServer::SetAccountSettings(HLERequestContext& ctx) {
531} 550}
532 551
533void ISystemSettingsServer::GetEulaVersions(HLERequestContext& ctx) { 552void ISystemSettingsServer::GetEulaVersions(HLERequestContext& ctx) {
534 LOG_INFO(Service_SET, "called"); 553 LOG_INFO(Service_SET, "called, elements={}", m_system_settings.eula_version_count);
535 554
536 ctx.WriteBuffer(m_system_settings.eula_versions); 555 ctx.WriteBuffer(m_system_settings.eula_versions);
537 556
@@ -557,7 +576,7 @@ void ISystemSettingsServer::SetEulaVersions(HLERequestContext& ctx) {
557} 576}
558 577
559void ISystemSettingsServer::GetColorSetId(HLERequestContext& ctx) { 578void ISystemSettingsServer::GetColorSetId(HLERequestContext& ctx) {
560 LOG_DEBUG(Service_SET, "called"); 579 LOG_DEBUG(Service_SET, "called, color_set=", m_system_settings.color_set_id);
561 580
562 IPC::ResponseBuilder rb{ctx, 3}; 581 IPC::ResponseBuilder rb{ctx, 3};
563 rb.Push(ResultSuccess); 582 rb.Push(ResultSuccess);
@@ -576,7 +595,13 @@ void ISystemSettingsServer::SetColorSetId(HLERequestContext& ctx) {
576} 595}
577 596
578void ISystemSettingsServer::GetNotificationSettings(HLERequestContext& ctx) { 597void ISystemSettingsServer::GetNotificationSettings(HLERequestContext& ctx) {
579 LOG_INFO(Service_SET, "called"); 598 LOG_INFO(Service_SET, "called, flags={}, volume={}, head_time={}:{}, tailt_time={}:{}",
599 m_system_settings.notification_settings.flags.raw,
600 m_system_settings.notification_settings.volume,
601 m_system_settings.notification_settings.start_time.hour,
602 m_system_settings.notification_settings.start_time.minute,
603 m_system_settings.notification_settings.stop_time.hour,
604 m_system_settings.notification_settings.stop_time.minute);
580 605
581 IPC::ResponseBuilder rb{ctx, 8}; 606 IPC::ResponseBuilder rb{ctx, 8};
582 rb.Push(ResultSuccess); 607 rb.Push(ResultSuccess);
@@ -601,7 +626,8 @@ void ISystemSettingsServer::SetNotificationSettings(HLERequestContext& ctx) {
601} 626}
602 627
603void ISystemSettingsServer::GetAccountNotificationSettings(HLERequestContext& ctx) { 628void ISystemSettingsServer::GetAccountNotificationSettings(HLERequestContext& ctx) {
604 LOG_INFO(Service_SET, "called"); 629 LOG_INFO(Service_SET, "called, elements={}",
630 m_system_settings.account_notification_settings_count);
605 631
606 ctx.WriteBuffer(m_system_settings.account_notification_settings); 632 ctx.WriteBuffer(m_system_settings.account_notification_settings);
607 633
@@ -645,6 +671,7 @@ using Settings =
645static Settings GetSettings() { 671static Settings GetSettings() {
646 Settings ret; 672 Settings ret;
647 673
674 // AM
648 ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0}); 675 ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0});
649 ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000}); 676 ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000});
650 677
@@ -656,6 +683,24 @@ static Settings GetSettings() {
656 ret["time"]["standard_steady_clock_test_offset_minutes"] = ToBytes(s32{0}); 683 ret["time"]["standard_steady_clock_test_offset_minutes"] = ToBytes(s32{0});
657 ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023}); 684 ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023});
658 685
686 // HID
687 ret["hid_debug"]["enables_debugpad"] = ToBytes(bool{true});
688 ret["hid_debug"]["manages_devices"] = ToBytes(bool{true});
689 ret["hid_debug"]["manages_touch_ic_i2c"] = ToBytes(bool{true});
690 ret["hid_debug"]["emulate_future_device"] = ToBytes(bool{false});
691 ret["hid_debug"]["emulate_mcu_hardware_error"] = ToBytes(bool{false});
692 ret["hid_debug"]["enables_rail"] = ToBytes(bool{true});
693 ret["hid_debug"]["emulate_firmware_update_failure"] = ToBytes(bool{false});
694 ret["hid_debug"]["failure_firmware_update"] = ToBytes(s32{0});
695 ret["hid_debug"]["ble_disabled"] = ToBytes(bool{false});
696 ret["hid_debug"]["dscale_disabled"] = ToBytes(bool{false});
697 ret["hid_debug"]["force_handheld"] = ToBytes(bool{true});
698 ret["hid_debug"]["disabled_features_per_id"] = std::vector<u8>(0xa8);
699 ret["hid_debug"]["touch_firmware_auto_update_disabled"] = ToBytes(bool{false});
700
701 // Settings
702 ret["settings_debug"]["is_debug_mode_enabled"] = ToBytes(bool{false});
703
659 return ret; 704 return ret;
660} 705}
661 706
@@ -708,7 +753,15 @@ void ISystemSettingsServer::GetSettingsItemValue(HLERequestContext& ctx) {
708} 753}
709 754
710void ISystemSettingsServer::GetTvSettings(HLERequestContext& ctx) { 755void ISystemSettingsServer::GetTvSettings(HLERequestContext& ctx) {
711 LOG_INFO(Service_SET, "called"); 756 LOG_INFO(Service_SET,
757 "called, flags={}, cmu_mode={}, contrast_ratio={}, hdmi_content_type={}, "
758 "rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}",
759 m_system_settings.tv_settings.flags.raw, m_system_settings.tv_settings.cmu_mode,
760 m_system_settings.tv_settings.contrast_ratio,
761 m_system_settings.tv_settings.hdmi_content_type,
762 m_system_settings.tv_settings.rgb_range, m_system_settings.tv_settings.tv_gama,
763 m_system_settings.tv_settings.tv_resolution,
764 m_system_settings.tv_settings.tv_underscan);
712 765
713 IPC::ResponseBuilder rb{ctx, 10}; 766 IPC::ResponseBuilder rb{ctx, 10};
714 rb.Push(ResultSuccess); 767 rb.Push(ResultSuccess);
@@ -735,23 +788,26 @@ void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) {
735} 788}
736 789
737void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) { 790void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) {
738 LOG_DEBUG(Service_SET, "called"); 791 bool is_debug_mode_enabled = false;
792 GetSettingsItemValue<bool>(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled");
793
794 LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled);
739 795
740 IPC::ResponseBuilder rb{ctx, 3}; 796 IPC::ResponseBuilder rb{ctx, 3};
741 rb.Push(ResultSuccess); 797 rb.Push(ResultSuccess);
742 rb.Push<u32>(0); 798 rb.Push(is_debug_mode_enabled);
743} 799}
744 800
745void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { 801void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) {
746 LOG_WARNING(Service_SET, "(STUBBED) called"); 802 LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag);
747 803
748 IPC::ResponseBuilder rb{ctx, 3}; 804 IPC::ResponseBuilder rb{ctx, 3};
749 rb.Push(ResultSuccess); 805 rb.Push(ResultSuccess);
750 rb.PushEnum(QuestFlag::Retail); 806 rb.PushEnum(m_system_settings.quest_flag);
751} 807}
752 808
753void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { 809void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
754 LOG_WARNING(Service_SET, "called"); 810 LOG_INFO(Service_SET, "called");
755 811
756 Service::Time::TimeZone::LocationName name{}; 812 Service::Time::TimeZone::LocationName name{};
757 auto res = GetDeviceTimeZoneLocationName(name); 813 auto res = GetDeviceTimeZoneLocationName(name);
@@ -762,7 +818,7 @@ void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx
762} 818}
763 819
764void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { 820void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
765 LOG_WARNING(Service_SET, "called"); 821 LOG_INFO(Service_SET, "called");
766 822
767 IPC::RequestParser rp{ctx}; 823 IPC::RequestParser rp{ctx};
768 auto name{rp.PopRaw<Service::Time::TimeZone::LocationName>()}; 824 auto name{rp.PopRaw<Service::Time::TimeZone::LocationName>()};
@@ -775,7 +831,7 @@ void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx
775 831
776void ISystemSettingsServer::SetRegionCode(HLERequestContext& ctx) { 832void ISystemSettingsServer::SetRegionCode(HLERequestContext& ctx) {
777 IPC::RequestParser rp{ctx}; 833 IPC::RequestParser rp{ctx};
778 m_system_settings.region_code = rp.PopEnum<RegionCode>(); 834 m_system_settings.region_code = rp.PopEnum<SystemRegionCode>();
779 SetSaveNeeded(); 835 SetSaveNeeded();
780 836
781 LOG_INFO(Service_SET, "called, region_code={}", m_system_settings.region_code); 837 LOG_INFO(Service_SET, "called, region_code={}", m_system_settings.region_code);
@@ -832,15 +888,38 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(HLERequ
832} 888}
833 889
834void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) { 890void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) {
835 LOG_WARNING(Service_SET, "(STUBBED) called"); 891 LOG_INFO(Service_SET, "called, primary_album_storage={}",
892 m_system_settings.primary_album_storage);
893
894 IPC::ResponseBuilder rb{ctx, 3};
895 rb.Push(ResultSuccess);
896 rb.PushEnum(m_system_settings.primary_album_storage);
897}
898
899void ISystemSettingsServer::GetNfcEnableFlag(HLERequestContext& ctx) {
900 LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag);
836 901
837 IPC::ResponseBuilder rb{ctx, 3}; 902 IPC::ResponseBuilder rb{ctx, 3};
838 rb.Push(ResultSuccess); 903 rb.Push(ResultSuccess);
839 rb.PushEnum(PrimaryAlbumStorage::SdCard); 904 rb.Push<u8>(m_system_settings.nfc_enable_flag);
905}
906
907void ISystemSettingsServer::SetNfcEnableFlag(HLERequestContext& ctx) {
908 IPC::RequestParser rp{ctx};
909 m_system_settings.nfc_enable_flag = rp.Pop<bool>();
910 SetSaveNeeded();
911
912 LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag);
913
914 IPC::ResponseBuilder rb{ctx, 2};
915 rb.Push(ResultSuccess);
840} 916}
841 917
842void ISystemSettingsServer::GetSleepSettings(HLERequestContext& ctx) { 918void ISystemSettingsServer::GetSleepSettings(HLERequestContext& ctx) {
843 LOG_INFO(Service_SET, "called"); 919 LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}",
920 m_system_settings.sleep_settings.flags.raw,
921 m_system_settings.sleep_settings.handheld_sleep_plan,
922 m_system_settings.sleep_settings.console_sleep_plan);
844 923
845 IPC::ResponseBuilder rb{ctx, 5}; 924 IPC::ResponseBuilder rb{ctx, 5};
846 rb.Push(ResultSuccess); 925 rb.Push(ResultSuccess);
@@ -861,8 +940,32 @@ void ISystemSettingsServer::SetSleepSettings(HLERequestContext& ctx) {
861 rb.Push(ResultSuccess); 940 rb.Push(ResultSuccess);
862} 941}
863 942
943void ISystemSettingsServer::GetWirelessLanEnableFlag(HLERequestContext& ctx) {
944 LOG_INFO(Service_SET, "called, wireless_lan_enable_flag={}",
945 m_system_settings.wireless_lan_enable_flag);
946
947 IPC::ResponseBuilder rb{ctx, 3};
948 rb.Push(ResultSuccess);
949 rb.Push(m_system_settings.wireless_lan_enable_flag);
950}
951
952void ISystemSettingsServer::SetWirelessLanEnableFlag(HLERequestContext& ctx) {
953 IPC::RequestParser rp{ctx};
954 m_system_settings.wireless_lan_enable_flag = rp.Pop<bool>();
955 SetSaveNeeded();
956
957 LOG_INFO(Service_SET, "called, wireless_lan_enable_flag={}",
958 m_system_settings.wireless_lan_enable_flag);
959
960 IPC::ResponseBuilder rb{ctx, 2};
961 rb.Push(ResultSuccess);
962}
963
864void ISystemSettingsServer::GetInitialLaunchSettings(HLERequestContext& ctx) { 964void ISystemSettingsServer::GetInitialLaunchSettings(HLERequestContext& ctx) {
865 LOG_INFO(Service_SET, "called"); 965 LOG_INFO(Service_SET, "called, flags={}, timestamp={}",
966 m_system_settings.initial_launch_settings_packed.flags.raw,
967 m_system_settings.initial_launch_settings_packed.timestamp.time_point);
968
866 IPC::ResponseBuilder rb{ctx, 10}; 969 IPC::ResponseBuilder rb{ctx, 10};
867 rb.Push(ResultSuccess); 970 rb.Push(ResultSuccess);
868 rb.PushRaw(m_system_settings.initial_launch_settings_packed); 971 rb.PushRaw(m_system_settings.initial_launch_settings_packed);
@@ -913,35 +1016,51 @@ void ISystemSettingsServer::GetProductModel(HLERequestContext& ctx) {
913 rb.Push(product_model); 1016 rb.Push(product_model);
914} 1017}
915 1018
916void ISystemSettingsServer::GetMiiAuthorId(HLERequestContext& ctx) { 1019void ISystemSettingsServer::GetBluetoothEnableFlag(HLERequestContext& ctx) {
917 const auto author_id = Common::UUID::MakeDefault(); 1020 LOG_INFO(Service_SET, "called, bluetooth_enable_flag={}",
1021 m_system_settings.bluetooth_enable_flag);
918 1022
919 LOG_WARNING(Service_SET, "(STUBBED) called, author_id={}", author_id.FormattedString()); 1023 IPC::ResponseBuilder rb{ctx, 3};
1024 rb.Push(ResultSuccess);
1025 rb.Push<u8>(m_system_settings.bluetooth_enable_flag);
1026}
1027
1028void ISystemSettingsServer::SetBluetoothEnableFlag(HLERequestContext& ctx) {
1029 IPC::RequestParser rp{ctx};
1030 m_system_settings.bluetooth_enable_flag = rp.Pop<bool>();
1031 SetSaveNeeded();
1032
1033 LOG_INFO(Service_SET, "called, bluetooth_enable_flag={}",
1034 m_system_settings.bluetooth_enable_flag);
1035
1036 IPC::ResponseBuilder rb{ctx, 2};
1037 rb.Push(ResultSuccess);
1038}
1039
1040void ISystemSettingsServer::GetMiiAuthorId(HLERequestContext& ctx) {
1041 LOG_INFO(Service_SET, "called, author_id={}",
1042 m_system_settings.mii_author_id.FormattedString());
920 1043
921 IPC::ResponseBuilder rb{ctx, 6}; 1044 IPC::ResponseBuilder rb{ctx, 6};
922 rb.Push(ResultSuccess); 1045 rb.Push(ResultSuccess);
923 rb.PushRaw(author_id); 1046 rb.PushRaw(m_system_settings.mii_author_id);
924} 1047}
925 1048
926void ISystemSettingsServer::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { 1049void ISystemSettingsServer::GetAutoUpdateEnableFlag(HLERequestContext& ctx) {
927 u8 auto_update_flag{}; 1050 LOG_INFO(Service_SET, "called, auto_update_flag={}", m_system_settings.auto_update_enable_flag);
928
929 LOG_WARNING(Service_SET, "(STUBBED) called, auto_update_flag={}", auto_update_flag);
930 1051
931 IPC::ResponseBuilder rb{ctx, 3}; 1052 IPC::ResponseBuilder rb{ctx, 3};
932 rb.Push(ResultSuccess); 1053 rb.Push(ResultSuccess);
933 rb.Push(auto_update_flag); 1054 rb.Push(m_system_settings.auto_update_enable_flag);
934} 1055}
935 1056
936void ISystemSettingsServer::GetBatteryPercentageFlag(HLERequestContext& ctx) { 1057void ISystemSettingsServer::GetBatteryPercentageFlag(HLERequestContext& ctx) {
937 u8 battery_percentage_flag{1}; 1058 LOG_DEBUG(Service_SET, "called, battery_percentage_flag={}",
938 1059 m_system_settings.battery_percentage_flag);
939 LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}",
940 battery_percentage_flag);
941 1060
942 IPC::ResponseBuilder rb{ctx, 3}; 1061 IPC::ResponseBuilder rb{ctx, 3};
943 rb.Push(ResultSuccess); 1062 rb.Push(ResultSuccess);
944 rb.Push(battery_percentage_flag); 1063 rb.Push(m_system_settings.battery_percentage_flag);
945} 1064}
946 1065
947void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { 1066void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestContext& ctx) {
@@ -968,11 +1087,12 @@ void ISystemSettingsServer::GetExternalSteadyClockInternalOffset(HLERequestConte
968} 1087}
969 1088
970void ISystemSettingsServer::GetErrorReportSharePermission(HLERequestContext& ctx) { 1089void ISystemSettingsServer::GetErrorReportSharePermission(HLERequestContext& ctx) {
971 LOG_WARNING(Service_SET, "(STUBBED) called"); 1090 LOG_INFO(Service_SET, "called, error_report_share_permission={}",
1091 m_system_settings.error_report_share_permission);
972 1092
973 IPC::ResponseBuilder rb{ctx, 3}; 1093 IPC::ResponseBuilder rb{ctx, 3};
974 rb.Push(ResultSuccess); 1094 rb.Push(ResultSuccess);
975 rb.PushEnum(ErrorReportSharePermission::Denied); 1095 rb.PushEnum(m_system_settings.error_report_share_permission);
976} 1096}
977 1097
978void ISystemSettingsServer::GetAppletLaunchFlags(HLERequestContext& ctx) { 1098void ISystemSettingsServer::GetAppletLaunchFlags(HLERequestContext& ctx) {
@@ -1014,7 +1134,7 @@ void ISystemSettingsServer::GetKeyboardLayout(HLERequestContext& ctx) {
1014} 1134}
1015 1135
1016void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { 1136void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
1017 LOG_WARNING(Service_SET, "called."); 1137 LOG_INFO(Service_SET, "called");
1018 1138
1019 Service::Time::Clock::SteadyClockTimePoint time_point{}; 1139 Service::Time::Clock::SteadyClockTimePoint time_point{};
1020 auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); 1140 auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point);
@@ -1025,7 +1145,7 @@ void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestConte
1025} 1145}
1026 1146
1027void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { 1147void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
1028 LOG_WARNING(Service_SET, "called."); 1148 LOG_INFO(Service_SET, "called");
1029 1149
1030 IPC::RequestParser rp{ctx}; 1150 IPC::RequestParser rp{ctx};
1031 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; 1151 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()};
@@ -1038,7 +1158,7 @@ void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestConte
1038 1158
1039void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( 1159void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
1040 HLERequestContext& ctx) { 1160 HLERequestContext& ctx) {
1041 LOG_WARNING(Service_SET, "called."); 1161 LOG_INFO(Service_SET, "called");
1042 1162
1043 Service::Time::Clock::SteadyClockTimePoint time_point{}; 1163 Service::Time::Clock::SteadyClockTimePoint time_point{};
1044 auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); 1164 auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
@@ -1050,7 +1170,7 @@ void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
1050 1170
1051void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( 1171void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1052 HLERequestContext& ctx) { 1172 HLERequestContext& ctx) {
1053 LOG_WARNING(Service_SET, "called."); 1173 LOG_INFO(Service_SET, "called");
1054 1174
1055 IPC::RequestParser rp{ctx}; 1175 IPC::RequestParser rp{ctx};
1056 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; 1176 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()};
@@ -1062,11 +1182,12 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1062} 1182}
1063 1183
1064void ISystemSettingsServer::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { 1184void ISystemSettingsServer::GetChineseTraditionalInputMethod(HLERequestContext& ctx) {
1065 LOG_WARNING(Service_SET, "(STUBBED) called"); 1185 LOG_INFO(Service_SET, "called, chinese_traditional_input_method={}",
1186 m_system_settings.chinese_traditional_input_method);
1066 1187
1067 IPC::ResponseBuilder rb{ctx, 3}; 1188 IPC::ResponseBuilder rb{ctx, 3};
1068 rb.Push(ResultSuccess); 1189 rb.Push(ResultSuccess);
1069 rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); 1190 rb.PushEnum(m_system_settings.chinese_traditional_input_method);
1070} 1191}
1071 1192
1072void ISystemSettingsServer::GetHomeMenuScheme(HLERequestContext& ctx) { 1193void ISystemSettingsServer::GetHomeMenuScheme(HLERequestContext& ctx) {
@@ -1094,11 +1215,11 @@ void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
1094} 1215}
1095 1216
1096void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) { 1217void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) {
1097 LOG_WARNING(Service_SET, "(STUBBED) called"); 1218 LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag);
1098 1219
1099 IPC::ResponseBuilder rb{ctx, 3}; 1220 IPC::ResponseBuilder rb{ctx, 3};
1100 rb.Push(ResultSuccess); 1221 rb.Push(ResultSuccess);
1101 rb.Push<u8>(false); 1222 rb.Push(m_system_settings.field_testing_flag);
1102} 1223}
1103 1224
1104void ISystemSettingsServer::SetupSettings() { 1225void ISystemSettingsServer::SetupSettings() {
diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h
index 6f587e0b3..32716f567 100644
--- a/src/core/hle/service/set/system_settings_server.h
+++ b/src/core/hle/service/set/system_settings_server.h
@@ -12,10 +12,11 @@
12#include "common/uuid.h" 12#include "common/uuid.h"
13#include "core/hle/result.h" 13#include "core/hle/result.h"
14#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
15#include "core/hle/service/set/appln_settings.h" 15#include "core/hle/service/set/setting_formats/appln_settings.h"
16#include "core/hle/service/set/device_settings.h" 16#include "core/hle/service/set/setting_formats/device_settings.h"
17#include "core/hle/service/set/private_settings.h" 17#include "core/hle/service/set/setting_formats/private_settings.h"
18#include "core/hle/service/set/system_settings.h" 18#include "core/hle/service/set/setting_formats/system_settings.h"
19#include "core/hle/service/set/settings_types.h"
19#include "core/hle/service/time/clock_types.h" 20#include "core/hle/service/time/clock_types.h"
20#include "core/hle/service/time/time_zone_types.h" 21#include "core/hle/service/time/time_zone_types.h"
21 22
@@ -24,25 +25,6 @@ class System;
24} 25}
25 26
26namespace Service::Set { 27namespace Service::Set {
27enum class GetFirmwareVersionType {
28 Version1,
29 Version2,
30};
31
32struct FirmwareVersionFormat {
33 u8 major;
34 u8 minor;
35 u8 micro;
36 INSERT_PADDING_BYTES(1);
37 u8 revision_major;
38 u8 revision_minor;
39 INSERT_PADDING_BYTES(2);
40 std::array<char, 0x20> platform;
41 std::array<u8, 0x40> version_hash;
42 std::array<char, 0x18> display_version;
43 std::array<char, 0x80> display_title;
44};
45static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size");
46 28
47Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, 29Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
48 GetFirmwareVersionType type); 30 GetFirmwareVersionType type);
@@ -55,6 +37,18 @@ public:
55 Result GetSettingsItemValue(std::vector<u8>& out_value, const std::string& category, 37 Result GetSettingsItemValue(std::vector<u8>& out_value, const std::string& category,
56 const std::string& name); 38 const std::string& name);
57 39
40 template <typename T>
41 Result GetSettingsItemValue(T& value, const std::string& category, const std::string& name) {
42 std::vector<u8> data;
43 const auto result = GetSettingsItemValue(data, category, name);
44 if (result.IsError()) {
45 return result;
46 }
47 ASSERT(data.size() >= sizeof(T));
48 std::memcpy(&value, data.data(), sizeof(T));
49 return result;
50 }
51
58 Result GetExternalSteadyClockSourceId(Common::UUID& out_id); 52 Result GetExternalSteadyClockSourceId(Common::UUID& out_id);
59 Result SetExternalSteadyClockSourceId(Common::UUID id); 53 Result SetExternalSteadyClockSourceId(Common::UUID id);
60 Result GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); 54 Result GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context);
@@ -80,6 +74,8 @@ private:
80 void SetLanguageCode(HLERequestContext& ctx); 74 void SetLanguageCode(HLERequestContext& ctx);
81 void GetFirmwareVersion(HLERequestContext& ctx); 75 void GetFirmwareVersion(HLERequestContext& ctx);
82 void GetFirmwareVersion2(HLERequestContext& ctx); 76 void GetFirmwareVersion2(HLERequestContext& ctx);
77 void GetLockScreenFlag(HLERequestContext& ctx);
78 void SetLockScreenFlag(HLERequestContext& ctx);
83 void GetExternalSteadyClockSourceId(HLERequestContext& ctx); 79 void GetExternalSteadyClockSourceId(HLERequestContext& ctx);
84 void SetExternalSteadyClockSourceId(HLERequestContext& ctx); 80 void SetExternalSteadyClockSourceId(HLERequestContext& ctx);
85 void GetUserSystemClockContext(HLERequestContext& ctx); 81 void GetUserSystemClockContext(HLERequestContext& ctx);
@@ -108,13 +104,19 @@ private:
108 void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); 104 void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
109 void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); 105 void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
110 void GetPrimaryAlbumStorage(HLERequestContext& ctx); 106 void GetPrimaryAlbumStorage(HLERequestContext& ctx);
107 void GetNfcEnableFlag(HLERequestContext& ctx);
108 void SetNfcEnableFlag(HLERequestContext& ctx);
111 void GetSleepSettings(HLERequestContext& ctx); 109 void GetSleepSettings(HLERequestContext& ctx);
112 void SetSleepSettings(HLERequestContext& ctx); 110 void SetSleepSettings(HLERequestContext& ctx);
111 void GetWirelessLanEnableFlag(HLERequestContext& ctx);
112 void SetWirelessLanEnableFlag(HLERequestContext& ctx);
113 void GetInitialLaunchSettings(HLERequestContext& ctx); 113 void GetInitialLaunchSettings(HLERequestContext& ctx);
114 void SetInitialLaunchSettings(HLERequestContext& ctx); 114 void SetInitialLaunchSettings(HLERequestContext& ctx);
115 void GetDeviceNickName(HLERequestContext& ctx); 115 void GetDeviceNickName(HLERequestContext& ctx);
116 void SetDeviceNickName(HLERequestContext& ctx); 116 void SetDeviceNickName(HLERequestContext& ctx);
117 void GetProductModel(HLERequestContext& ctx); 117 void GetProductModel(HLERequestContext& ctx);
118 void GetBluetoothEnableFlag(HLERequestContext& ctx);
119 void SetBluetoothEnableFlag(HLERequestContext& ctx);
118 void GetMiiAuthorId(HLERequestContext& ctx); 120 void GetMiiAuthorId(HLERequestContext& ctx);
119 void GetAutoUpdateEnableFlag(HLERequestContext& ctx); 121 void GetAutoUpdateEnableFlag(HLERequestContext& ctx);
120 void GetBatteryPercentageFlag(HLERequestContext& ctx); 122 void GetBatteryPercentageFlag(HLERequestContext& ctx);
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index e2d9cd98a..725311c53 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -91,6 +91,10 @@ void Display::CreateLayer(u64 layer_id, u32 binder_id,
91 layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer, 91 layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
92 std::move(buffer_item_consumer))); 92 std::move(buffer_item_consumer)));
93 93
94 if (is_abandoned) {
95 this->FindLayer(layer_id)->GetConsumer().Abandon();
96 }
97
94 hos_binder_driver_server.RegisterProducer(std::move(producer)); 98 hos_binder_driver_server.RegisterProducer(std::move(producer));
95} 99}
96 100
@@ -103,6 +107,13 @@ void Display::DestroyLayer(u64 layer_id) {
103 [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; }); 107 [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
104} 108}
105 109
110void Display::Abandon() {
111 for (auto& layer : layers) {
112 layer->GetConsumer().Abandon();
113 }
114 is_abandoned = true;
115}
116
106Layer* Display::FindLayer(u64 layer_id) { 117Layer* Display::FindLayer(u64 layer_id) {
107 const auto itr = 118 const auto itr =
108 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) { 119 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 7e68ee79b..8eb8a5155 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -98,6 +98,8 @@ public:
98 layers.clear(); 98 layers.clear();
99 } 99 }
100 100
101 void Abandon();
102
101 /// Attempts to find a layer with the given ID. 103 /// Attempts to find a layer with the given ID.
102 /// 104 ///
103 /// @param layer_id The layer ID. 105 /// @param layer_id The layer ID.
@@ -124,6 +126,7 @@ private:
124 126
125 std::vector<std::unique_ptr<Layer>> layers; 127 std::vector<std::unique_ptr<Layer>> layers;
126 Kernel::KEvent* vsync_event{}; 128 Kernel::KEvent* vsync_event{};
129 bool is_abandoned{};
127}; 130};
128 131
129} // namespace Service::VI 132} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
index 295005e23..f95e2dc71 100644
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <utility>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
@@ -75,12 +76,12 @@ public:
75 return open; 76 return open;
76 } 77 }
77 78
78 void Close() { 79 bool Close() {
79 open = false; 80 return std::exchange(open, false);
80 } 81 }
81 82
82 void Open() { 83 bool Open() {
83 open = true; 84 return !std::exchange(open, true);
84 } 85 }
85 86
86private: 87private:
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 39d5be90d..bfcc27ddc 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -719,7 +719,12 @@ private:
719 return; 719 return;
720 } 720 }
721 721
722 nvnflinger.OpenLayer(layer_id); 722 if (!nvnflinger.OpenLayer(layer_id)) {
723 LOG_WARNING(Service_VI, "Tried to open layer which was already open");
724 IPC::ResponseBuilder rb{ctx, 2};
725 rb.Push(ResultOperationFailed);
726 return;
727 }
723 728
724 android::OutputParcel parcel; 729 android::OutputParcel parcel;
725 parcel.WriteInterface(NativeWindow{*buffer_queue_id}); 730 parcel.WriteInterface(NativeWindow{*buffer_queue_id});
@@ -737,7 +742,12 @@ private:
737 742
738 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id); 743 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id);
739 744
740 nvnflinger.CloseLayer(layer_id); 745 if (!nvnflinger.CloseLayer(layer_id)) {
746 LOG_WARNING(Service_VI, "Tried to close layer which was not open");
747 IPC::ResponseBuilder rb{ctx, 2};
748 rb.Push(ResultOperationFailed);
749 return;
750 }
741 751
742 IPC::ResponseBuilder rb{ctx, 2}; 752 IPC::ResponseBuilder rb{ctx, 2};
743 rb.Push(ResultSuccess); 753 rb.Push(ResultSuccess);
diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt
index 22e9337c4..94d8cc4c3 100644
--- a/src/frontend_common/CMakeLists.txt
+++ b/src/frontend_common/CMakeLists.txt
@@ -4,6 +4,7 @@
4add_library(frontend_common STATIC 4add_library(frontend_common STATIC
5 config.cpp 5 config.cpp
6 config.h 6 config.h
7 content_manager.h
7) 8)
8 9
9create_target_directory_groups(frontend_common) 10create_target_directory_groups(frontend_common)
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index 9eb4799a6..46277e288 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -5,6 +5,7 @@
5#include <array> 5#include <array>
6#include "common/fs/fs.h" 6#include "common/fs/fs.h"
7#include "common/fs/path_util.h" 7#include "common/fs/path_util.h"
8#include "common/logging/log.h"
8#include "common/settings.h" 9#include "common/settings.h"
9#include "common/settings_common.h" 10#include "common/settings_common.h"
10#include "common/settings_enums.h" 11#include "common/settings_enums.h"
@@ -58,6 +59,19 @@ void Config::Initialize(const std::optional<std::string> config_path) {
58} 59}
59 60
60void Config::WriteToIni() const { 61void Config::WriteToIni() const {
62 std::string config_type;
63 switch (type) {
64 case ConfigType::GlobalConfig:
65 config_type = "Global";
66 break;
67 case ConfigType::PerGameConfig:
68 config_type = "Game Specific";
69 break;
70 case ConfigType::InputProfile:
71 config_type = "Input Profile";
72 break;
73 }
74 LOG_INFO(Config, "Writing {} configuration to: {}", config_type, config_loc);
61 FILE* fp = nullptr; 75 FILE* fp = nullptr;
62#ifdef _WIN32 76#ifdef _WIN32
63 fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb"); 77 fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb");
@@ -117,10 +131,10 @@ void Config::ReadPlayerValues(const std::size_t player_index) {
117 player_prefix.append("player_").append(ToString(player_index)).append("_"); 131 player_prefix.append("player_").append(ToString(player_index)).append("_");
118 } 132 }
119 133
134 const auto profile_name = ReadStringSetting(std::string(player_prefix).append("profile_name"));
135
120 auto& player = Settings::values.players.GetValue()[player_index]; 136 auto& player = Settings::values.players.GetValue()[player_index];
121 if (IsCustomConfig()) { 137 if (IsCustomConfig()) {
122 const auto profile_name =
123 ReadStringSetting(std::string(player_prefix).append("profile_name"));
124 if (profile_name.empty()) { 138 if (profile_name.empty()) {
125 // Use the global input config 139 // Use the global input config
126 player = Settings::values.players.GetValue(true)[player_index]; 140 player = Settings::values.players.GetValue(true)[player_index];
@@ -139,6 +153,10 @@ void Config::ReadPlayerValues(const std::size_t player_index) {
139 player.controller_type = controller; 153 player.controller_type = controller;
140 } 154 }
141 } else { 155 } else {
156 if (global) {
157 auto& player_global = Settings::values.players.GetValue(true)[player_index];
158 player_global.profile_name = profile_name;
159 }
142 std::string connected_key = player_prefix; 160 std::string connected_key = player_prefix;
143 player.connected = ReadBooleanSetting(connected_key.append("connected"), 161 player.connected = ReadBooleanSetting(connected_key.append("connected"),
144 std::make_optional(player_index == 0)); 162 std::make_optional(player_index == 0));
@@ -412,6 +430,11 @@ void Config::SavePlayerValues(const std::size_t player_index) {
412 std::make_optional(static_cast<u8>(Settings::ControllerType::ProController))); 430 std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
413 431
414 if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) { 432 if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) {
433 if (global) {
434 const auto& player_global = Settings::values.players.GetValue(true)[player_index];
435 WriteStringSetting(std::string(player_prefix).append("profile_name"),
436 player_global.profile_name, std::make_optional(std::string("")));
437 }
415 WriteBooleanSetting(std::string(player_prefix).append("connected"), player.connected, 438 WriteBooleanSetting(std::string(player_prefix).append("connected"), player.connected,
416 std::make_optional(player_index == 0)); 439 std::make_optional(player_index == 0));
417 WriteIntegerSetting(std::string(player_prefix).append("vibration_enabled"), 440 WriteIntegerSetting(std::string(player_prefix).append("vibration_enabled"),
@@ -468,12 +491,15 @@ void Config::SaveMotionTouchValues() {
468 491
469void Config::SaveValues() { 492void Config::SaveValues() {
470 if (global) { 493 if (global) {
494 LOG_DEBUG(Config, "Saving global generic configuration values");
471 SaveDataStorageValues(); 495 SaveDataStorageValues();
472 SaveDebuggingValues(); 496 SaveDebuggingValues();
473 SaveDisabledAddOnValues(); 497 SaveDisabledAddOnValues();
474 SaveNetworkValues(); 498 SaveNetworkValues();
475 SaveWebServiceValues(); 499 SaveWebServiceValues();
476 SaveMiscellaneousValues(); 500 SaveMiscellaneousValues();
501 } else {
502 LOG_DEBUG(Config, "Saving only generic configuration values");
477 } 503 }
478 SaveControlValues(); 504 SaveControlValues();
479 SaveCoreValues(); 505 SaveCoreValues();
@@ -814,10 +840,6 @@ void Config::Reload() {
814 SaveValues(); 840 SaveValues();
815} 841}
816 842
817void Config::Save() {
818 SaveValues();
819}
820
821void Config::ClearControlPlayerValues() const { 843void Config::ClearControlPlayerValues() const {
822 // If key is an empty string, all keys in the current group() are removed. 844 // If key is an empty string, all keys in the current group() are removed.
823 const char* section = Settings::TranslateCategory(Settings::Category::Controls); 845 const char* section = Settings::TranslateCategory(Settings::Category::Controls);
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h
index b01631649..4798d6432 100644
--- a/src/frontend_common/config.h
+++ b/src/frontend_common/config.h
@@ -51,7 +51,6 @@ protected:
51 [[nodiscard]] bool IsCustomConfig() const; 51 [[nodiscard]] bool IsCustomConfig() const;
52 52
53 void Reload(); 53 void Reload();
54 void Save();
55 54
56 /** 55 /**
57 * Derived config classes must implement this so they can reload all platform-specific 56 * Derived config classes must implement this so they can reload all platform-specific
diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h
new file mode 100644
index 000000000..23f2979db
--- /dev/null
+++ b/src/frontend_common/content_manager.h
@@ -0,0 +1,238 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <boost/algorithm/string.hpp>
7#include "common/common_types.h"
8#include "common/literals.h"
9#include "core/core.h"
10#include "core/file_sys/common_funcs.h"
11#include "core/file_sys/content_archive.h"
12#include "core/file_sys/mode.h"
13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/registered_cache.h"
15#include "core/file_sys/submission_package.h"
16#include "core/hle/service/filesystem/filesystem.h"
17#include "core/loader/loader.h"
18
19namespace ContentManager {
20
21enum class InstallResult {
22 Success,
23 Overwrite,
24 Failure,
25 BaseInstallAttempted,
26};
27
28/**
29 * \brief Removes a single installed DLC
30 * \param fs_controller [FileSystemController] reference from the Core::System instance
31 * \param title_id Unique title ID representing the DLC which will be removed
32 * \return 'true' if successful
33 */
34inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_controller,
35 const u64 title_id) {
36 return fs_controller.GetUserNANDContents()->RemoveExistingEntry(title_id) ||
37 fs_controller.GetSDMCContents()->RemoveExistingEntry(title_id);
38}
39
40/**
41 * \brief Removes all DLC for a game
42 * \param system Raw pointer to the system instance
43 * \param program_id Program ID for the game that will have all of its DLC removed
44 * \return Number of DLC removed
45 */
46inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) {
47 size_t count{};
48 const auto& fs_controller = system->GetFileSystemController();
49 const auto dlc_entries = system->GetContentProvider().ListEntriesFilter(
50 FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
51 std::vector<u64> program_dlc_entries;
52
53 for (const auto& entry : dlc_entries) {
54 if (FileSys::GetBaseTitleID(entry.title_id) == program_id) {
55 program_dlc_entries.push_back(entry.title_id);
56 }
57 }
58
59 for (const auto& entry : program_dlc_entries) {
60 if (RemoveDLC(fs_controller, entry)) {
61 ++count;
62 }
63 }
64 return count;
65}
66
67/**
68 * \brief Removes the installed update for a game
69 * \param fs_controller [FileSystemController] reference from the Core::System instance
70 * \param program_id Program ID for the game that will have its installed update removed
71 * \return 'true' if successful
72 */
73inline bool RemoveUpdate(const Service::FileSystem::FileSystemController& fs_controller,
74 const u64 program_id) {
75 const auto update_id = program_id | 0x800;
76 return fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
77 fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
78}
79
80/**
81 * \brief Removes the base content for a game
82 * \param fs_controller [FileSystemController] reference from the Core::System instance
83 * \param program_id Program ID for the game that will have its base content removed
84 * \return 'true' if successful
85 */
86inline bool RemoveBaseContent(const Service::FileSystem::FileSystemController& fs_controller,
87 const u64 program_id) {
88 return fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
89 fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
90}
91
92/**
93 * \brief Removes a mod for a game
94 * \param fs_controller [FileSystemController] reference from the Core::System instance
95 * \param program_id Program ID for the game where [mod_name] will be removed
96 * \param mod_name The name of a mod as given by FileSys::PatchManager::GetPatches. This corresponds
97 * with the name of the mod's directory in a game's load folder.
98 * \return 'true' if successful
99 */
100inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_controller,
101 const u64 program_id, const std::string& mod_name) {
102 // Check general Mods (LayeredFS and IPS)
103 const auto mod_dir = fs_controller.GetModificationLoadRoot(program_id);
104 if (mod_dir != nullptr) {
105 return mod_dir->DeleteSubdirectoryRecursive(mod_name);
106 }
107
108 // Check SDMC mod directory (RomFS LayeredFS)
109 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(program_id);
110 if (sdmc_mod_dir != nullptr) {
111 return sdmc_mod_dir->DeleteSubdirectoryRecursive(mod_name);
112 }
113
114 return false;
115}
116
117/**
118 * \brief Installs an NSP
119 * \param system Raw pointer to the system instance
120 * \param vfs Raw pointer to the VfsFilesystem instance in Core::System
121 * \param filename Path to the NSP file
122 * \param callback Optional callback to report the progress of the installation. The first size_t
123 * parameter is the total size of the virtual file and the second is the current progress. If you
124 * return false to the callback, it will cancel the installation as soon as possible.
125 * \return [InstallResult] representing how the installation finished
126 */
127inline InstallResult InstallNSP(
128 Core::System* system, FileSys::VfsFilesystem* vfs, const std::string& filename,
129 const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) {
130 const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
131 std::size_t block_size) {
132 if (src == nullptr || dest == nullptr) {
133 return false;
134 }
135 if (!dest->Resize(src->GetSize())) {
136 return false;
137 }
138
139 using namespace Common::Literals;
140 std::vector<u8> buffer(1_MiB);
141
142 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
143 if (callback(src->GetSize(), i)) {
144 dest->Resize(0);
145 return false;
146 }
147 const auto read = src->Read(buffer.data(), buffer.size(), i);
148 dest->Write(buffer.data(), read, i);
149 }
150 return true;
151 };
152
153 std::shared_ptr<FileSys::NSP> nsp;
154 FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read);
155 if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) {
156 nsp = std::make_shared<FileSys::NSP>(file);
157 if (nsp->IsExtractedType()) {
158 return InstallResult::Failure;
159 }
160 } else {
161 return InstallResult::Failure;
162 }
163
164 if (nsp->GetStatus() != Loader::ResultStatus::Success) {
165 return InstallResult::Failure;
166 }
167 const auto res =
168 system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy);
169 switch (res) {
170 case FileSys::InstallResult::Success:
171 return InstallResult::Success;
172 case FileSys::InstallResult::OverwriteExisting:
173 return InstallResult::Overwrite;
174 case FileSys::InstallResult::ErrorBaseInstall:
175 return InstallResult::BaseInstallAttempted;
176 default:
177 return InstallResult::Failure;
178 }
179}
180
181/**
182 * \brief Installs an NCA
183 * \param vfs Raw pointer to the VfsFilesystem instance in Core::System
184 * \param filename Path to the NCA file
185 * \param registered_cache Raw pointer to the registered cache that the NCA will be installed to
186 * \param title_type Type of NCA package to install
187 * \param callback Optional callback to report the progress of the installation. The first size_t
188 * parameter is the total size of the virtual file and the second is the current progress. If you
189 * return false to the callback, it will cancel the installation as soon as possible.
190 * \return [InstallResult] representing how the installation finished
191 */
192inline InstallResult InstallNCA(
193 FileSys::VfsFilesystem* vfs, const std::string& filename,
194 FileSys::RegisteredCache* registered_cache, const FileSys::TitleType title_type,
195 const std::function<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) {
196 const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
197 std::size_t block_size) {
198 if (src == nullptr || dest == nullptr) {
199 return false;
200 }
201 if (!dest->Resize(src->GetSize())) {
202 return false;
203 }
204
205 using namespace Common::Literals;
206 std::vector<u8> buffer(1_MiB);
207
208 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
209 if (callback(src->GetSize(), i)) {
210 dest->Resize(0);
211 return false;
212 }
213 const auto read = src->Read(buffer.data(), buffer.size(), i);
214 dest->Write(buffer.data(), read, i);
215 }
216 return true;
217 };
218
219 const auto nca = std::make_shared<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read));
220 const auto id = nca->GetStatus();
221
222 // Game updates necessary are missing base RomFS
223 if (id != Loader::ResultStatus::Success &&
224 id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
225 return InstallResult::Failure;
226 }
227
228 const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy);
229 if (res == FileSys::InstallResult::Success) {
230 return InstallResult::Success;
231 } else if (res == FileSys::InstallResult::OverwriteExisting) {
232 return InstallResult::Overwrite;
233 } else {
234 return InstallResult::Failure;
235 }
236}
237
238} // namespace ContentManager
diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp
index 2ab93402d..f2499502d 100644
--- a/src/hid_core/frontend/emulated_controller.cpp
+++ b/src/hid_core/frontend/emulated_controller.cpp
@@ -110,6 +110,7 @@ void EmulatedController::ReloadFromSettings() {
110 original_npad_type = npad_type; 110 original_npad_type = npad_type;
111 } 111 }
112 112
113 SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Active);
113 Disconnect(); 114 Disconnect();
114 if (player.connected) { 115 if (player.connected) {
115 Connect(); 116 Connect();
@@ -144,8 +145,8 @@ void EmulatedController::ReloadColorsFromSettings() {
144 145
145void EmulatedController::LoadDevices() { 146void EmulatedController::LoadDevices() {
146 // TODO(german77): Use more buttons to detect the correct device 147 // TODO(german77): Use more buttons to detect the correct device
147 const auto left_joycon = button_params[Settings::NativeButton::DRight]; 148 const auto& left_joycon = button_params[Settings::NativeButton::DRight];
148 const auto right_joycon = button_params[Settings::NativeButton::A]; 149 const auto& right_joycon = button_params[Settings::NativeButton::A];
149 150
150 // Triggers for GC controllers 151 // Triggers for GC controllers
151 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; 152 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
@@ -1208,20 +1209,43 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1208 controller.nfc_state = controller.nfc_values; 1209 controller.nfc_state = controller.nfc_values;
1209} 1210}
1210 1211
1211bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1212bool EmulatedController::SetVibration(bool should_vibrate) {
1213 VibrationValue vibration_value = DEFAULT_VIBRATION_VALUE;
1214 if (should_vibrate) {
1215 vibration_value.high_amplitude = 1.0f;
1216 vibration_value.low_amplitude = 1.0f;
1217 }
1218
1219 return SetVibration(DeviceIndex::Left, vibration_value);
1220}
1221
1222bool EmulatedController::SetVibration(u32 slot, Core::HID::VibrationGcErmCommand erm_command) {
1223 VibrationValue vibration_value = DEFAULT_VIBRATION_VALUE;
1224 if (erm_command == Core::HID::VibrationGcErmCommand::Start) {
1225 vibration_value.high_amplitude = 1.0f;
1226 vibration_value.low_amplitude = 1.0f;
1227 }
1228
1229 return SetVibration(DeviceIndex::Left, vibration_value);
1230}
1231
1232bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationValue& vibration) {
1212 if (!is_initialized) { 1233 if (!is_initialized) {
1213 return false; 1234 return false;
1214 } 1235 }
1215 if (device_index >= output_devices.size()) { 1236 if (device_index >= DeviceIndex::MaxDeviceIndex) {
1216 return false; 1237 return false;
1217 } 1238 }
1218 if (!output_devices[device_index]) { 1239 const std::size_t index = static_cast<std::size_t>(device_index);
1240 if (!output_devices[index]) {
1219 return false; 1241 return false;
1220 } 1242 }
1221 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); 1243 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1222 const auto& player = Settings::values.players.GetValue()[player_index]; 1244 const auto& player = Settings::values.players.GetValue()[player_index];
1223 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; 1245 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
1224 1246
1247 last_vibration_value = vibration;
1248
1225 if (!player.vibration_enabled) { 1249 if (!player.vibration_enabled) {
1226 return false; 1250 return false;
1227 } 1251 }
@@ -1239,8 +1263,11 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
1239 .high_frequency = vibration.high_frequency, 1263 .high_frequency = vibration.high_frequency,
1240 .type = type, 1264 .type = type,
1241 }; 1265 };
1242 return output_devices[device_index]->SetVibration(status) == 1266 return output_devices[index]->SetVibration(status) == Common::Input::DriverResult::Success;
1243 Common::Input::DriverResult::Success; 1267}
1268
1269VibrationValue EmulatedController::GetActualVibrationValue(DeviceIndex device_index) const {
1270 return last_vibration_value;
1244} 1271}
1245 1272
1246bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { 1273bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
diff --git a/src/hid_core/frontend/emulated_controller.h b/src/hid_core/frontend/emulated_controller.h
index 90e536e07..168abe089 100644
--- a/src/hid_core/frontend/emulated_controller.h
+++ b/src/hid_core/frontend/emulated_controller.h
@@ -356,10 +356,27 @@ public:
356 const NfcState& GetNfc() const; 356 const NfcState& GetNfc() const;
357 357
358 /** 358 /**
359 * Sends an on/off vibration to the left device
360 * @return true if vibration had no errors
361 */
362 bool SetVibration(bool should_vibrate);
363
364 /**
365 * Sends an GC vibration to the left device
366 * @return true if vibration had no errors
367 */
368 bool SetVibration(u32 slot, Core::HID::VibrationGcErmCommand erm_command);
369
370 /**
359 * Sends a specific vibration to the output device 371 * Sends a specific vibration to the output device
360 * @return true if vibration had no errors 372 * @return true if vibration had no errors
361 */ 373 */
362 bool SetVibration(std::size_t device_index, VibrationValue vibration); 374 bool SetVibration(DeviceIndex device_index, const VibrationValue& vibration);
375
376 /**
377 * @return The last sent vibration
378 */
379 VibrationValue GetActualVibrationValue(DeviceIndex device_index) const;
363 380
364 /** 381 /**
365 * Sends a small vibration to the output device 382 * Sends a small vibration to the output device
@@ -564,6 +581,7 @@ private:
564 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; 581 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
565 u32 turbo_button_state{0}; 582 u32 turbo_button_state{0};
566 std::size_t nfc_handles{0}; 583 std::size_t nfc_handles{0};
584 VibrationValue last_vibration_value{DEFAULT_VIBRATION_VALUE};
567 585
568 // Temporary values to avoid doing changes while the controller is in configuring mode 586 // Temporary values to avoid doing changes while the controller is in configuring mode
569 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; 587 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp
index 2c5fe6d51..ca824b4a3 100644
--- a/src/hid_core/resource_manager.cpp
+++ b/src/hid_core/resource_manager.cpp
@@ -7,6 +7,7 @@
7#include "core/hle/kernel/k_shared_memory.h" 7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/service/ipc_helpers.h" 8#include "core/hle/service/ipc_helpers.h"
9#include "hid_core/hid_core.h" 9#include "hid_core/hid_core.h"
10#include "hid_core/hid_util.h"
10#include "hid_core/resource_manager.h" 11#include "hid_core/resource_manager.h"
11 12
12#include "hid_core/resources/applet_resource.h" 13#include "hid_core/resources/applet_resource.h"
@@ -27,6 +28,10 @@
27#include "hid_core/resources/touch_screen/gesture.h" 28#include "hid_core/resources/touch_screen/gesture.h"
28#include "hid_core/resources/touch_screen/touch_screen.h" 29#include "hid_core/resources/touch_screen/touch_screen.h"
29#include "hid_core/resources/unique_pad/unique_pad.h" 30#include "hid_core/resources/unique_pad/unique_pad.h"
31#include "hid_core/resources/vibration/gc_vibration_device.h"
32#include "hid_core/resources/vibration/n64_vibration_device.h"
33#include "hid_core/resources/vibration/vibration_base.h"
34#include "hid_core/resources/vibration/vibration_device.h"
30 35
31namespace Service::HID { 36namespace Service::HID {
32 37
@@ -52,6 +57,7 @@ void ResourceManager::Initialize() {
52 57
53 system.HIDCore().ReloadInputDevices(); 58 system.HIDCore().ReloadInputDevices();
54 59
60 handheld_config = std::make_shared<HandheldConfig>();
55 InitializeHidCommonSampler(); 61 InitializeHidCommonSampler();
56 InitializeTouchScreenSampler(); 62 InitializeTouchScreenSampler();
57 InitializeConsoleSixAxisSampler(); 63 InitializeConsoleSixAxisSampler();
@@ -174,7 +180,7 @@ void ResourceManager::InitializeHidCommonSampler() {
174 debug_pad->SetAppletResource(applet_resource, &shared_mutex); 180 debug_pad->SetAppletResource(applet_resource, &shared_mutex);
175 digitizer->SetAppletResource(applet_resource, &shared_mutex); 181 digitizer->SetAppletResource(applet_resource, &shared_mutex);
176 keyboard->SetAppletResource(applet_resource, &shared_mutex); 182 keyboard->SetAppletResource(applet_resource, &shared_mutex);
177 npad->SetNpadExternals(applet_resource, &shared_mutex); 183 npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config);
178 six_axis->SetAppletResource(applet_resource, &shared_mutex); 184 six_axis->SetAppletResource(applet_resource, &shared_mutex);
179 mouse->SetAppletResource(applet_resource, &shared_mutex); 185 mouse->SetAppletResource(applet_resource, &shared_mutex);
180 debug_mouse->SetAppletResource(applet_resource, &shared_mutex); 186 debug_mouse->SetAppletResource(applet_resource, &shared_mutex);
@@ -257,6 +263,121 @@ void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) {
257 applet_resource->EnableTouchScreen(aruid, is_enabled); 263 applet_resource->EnableTouchScreen(aruid, is_enabled);
258} 264}
259 265
266NpadVibrationBase* ResourceManager::GetVibrationDevice(
267 const Core::HID::VibrationDeviceHandle& handle) {
268 return npad->GetVibrationDevice(handle);
269}
270
271NpadN64VibrationDevice* ResourceManager::GetN64VibrationDevice(
272 const Core::HID::VibrationDeviceHandle& handle) {
273 return npad->GetN64VibrationDevice(handle);
274}
275
276NpadVibrationDevice* ResourceManager::GetNSVibrationDevice(
277 const Core::HID::VibrationDeviceHandle& handle) {
278 return npad->GetNSVibrationDevice(handle);
279}
280
281NpadGcVibrationDevice* ResourceManager::GetGcVibrationDevice(
282 const Core::HID::VibrationDeviceHandle& handle) {
283 return npad->GetGcVibrationDevice(handle);
284}
285
286Result ResourceManager::SetAruidValidForVibration(u64 aruid, bool is_enabled) {
287 std::scoped_lock lock{shared_mutex};
288 const bool has_changed = applet_resource->SetAruidValidForVibration(aruid, is_enabled);
289
290 if (has_changed) {
291 auto devices = npad->GetAllVibrationDevices();
292 for ([[maybe_unused]] auto* device : devices) {
293 // TODO
294 }
295 }
296
297 auto* vibration_handler = npad->GetVibrationHandler();
298 if (aruid != vibration_handler->GetSessionAruid()) {
299 vibration_handler->EndPermitVibrationSession();
300 }
301
302 return ResultSuccess;
303}
304
305void ResourceManager::SetForceHandheldStyleVibration(bool is_forced) {
306 handheld_config->is_force_handheld_style_vibration = is_forced;
307}
308
309Result ResourceManager::IsVibrationAruidActive(u64 aruid, bool& is_active) const {
310 std::scoped_lock lock{shared_mutex};
311 is_active = applet_resource->IsVibrationAruidActive(aruid);
312 return ResultSuccess;
313}
314
315Result ResourceManager::GetVibrationDeviceInfo(Core::HID::VibrationDeviceInfo& device_info,
316 const Core::HID::VibrationDeviceHandle& handle) {
317 bool check_device_index = false;
318
319 const Result is_valid = IsVibrationHandleValid(handle);
320 if (is_valid.IsError()) {
321 return is_valid;
322 }
323
324 switch (handle.npad_type) {
325 case Core::HID::NpadStyleIndex::Fullkey:
326 case Core::HID::NpadStyleIndex::Handheld:
327 case Core::HID::NpadStyleIndex::JoyconDual:
328 case Core::HID::NpadStyleIndex::JoyconLeft:
329 case Core::HID::NpadStyleIndex::JoyconRight:
330 device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
331 check_device_index = true;
332 break;
333 case Core::HID::NpadStyleIndex::GameCube:
334 device_info.type = Core::HID::VibrationDeviceType::GcErm;
335 break;
336 case Core::HID::NpadStyleIndex::N64:
337 device_info.type = Core::HID::VibrationDeviceType::N64;
338 break;
339 default:
340 device_info.type = Core::HID::VibrationDeviceType::Unknown;
341 break;
342 }
343
344 device_info.position = Core::HID::VibrationDevicePosition::None;
345 if (check_device_index) {
346 switch (handle.device_index) {
347 case Core::HID::DeviceIndex::Left:
348 device_info.position = Core::HID::VibrationDevicePosition::Left;
349 break;
350 case Core::HID::DeviceIndex::Right:
351 device_info.position = Core::HID::VibrationDevicePosition::Right;
352 break;
353 case Core::HID::DeviceIndex::None:
354 default:
355 ASSERT_MSG(false, "DeviceIndex should never be None!");
356 break;
357 }
358 }
359 return ResultSuccess;
360}
361
362Result ResourceManager::SendVibrationValue(u64 aruid,
363 const Core::HID::VibrationDeviceHandle& handle,
364 const Core::HID::VibrationValue& value) {
365 bool has_active_aruid{};
366 NpadVibrationDevice* device{nullptr};
367 Result result = IsVibrationAruidActive(aruid, has_active_aruid);
368
369 if (result.IsSuccess() && has_active_aruid) {
370 result = IsVibrationHandleValid(handle);
371 }
372 if (result.IsSuccess() && has_active_aruid) {
373 device = GetNSVibrationDevice(handle);
374 }
375 if (device != nullptr) {
376 result = device->SendVibrationValue(value);
377 }
378 return result;
379}
380
260void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) { 381void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
261 auto& core_timing = system.CoreTiming(); 382 auto& core_timing = system.CoreTiming();
262 debug_pad->OnUpdate(core_timing); 383 debug_pad->OnUpdate(core_timing);
diff --git a/src/hid_core/resource_manager.h b/src/hid_core/resource_manager.h
index 7a21d8eb8..128e00125 100644
--- a/src/hid_core/resource_manager.h
+++ b/src/hid_core/resource_manager.h
@@ -10,6 +10,12 @@ namespace Core {
10class System; 10class System;
11} 11}
12 12
13namespace Core::HID {
14struct VibrationDeviceHandle;
15struct VibrationValue;
16struct VibrationDeviceInfo;
17} // namespace Core::HID
18
13namespace Core::Timing { 19namespace Core::Timing {
14struct EventType; 20struct EventType;
15} 21}
@@ -37,6 +43,11 @@ class SixAxis;
37class SleepButton; 43class SleepButton;
38class TouchScreen; 44class TouchScreen;
39class UniquePad; 45class UniquePad;
46class NpadVibrationBase;
47class NpadN64VibrationDevice;
48class NpadGcVibrationDevice;
49class NpadVibrationDevice;
50struct HandheldConfig;
40 51
41class ResourceManager { 52class ResourceManager {
42 53
@@ -79,6 +90,18 @@ public:
79 void EnablePadInput(u64 aruid, bool is_enabled); 90 void EnablePadInput(u64 aruid, bool is_enabled);
80 void EnableTouchScreen(u64 aruid, bool is_enabled); 91 void EnableTouchScreen(u64 aruid, bool is_enabled);
81 92
93 NpadVibrationBase* GetVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
94 NpadN64VibrationDevice* GetN64VibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
95 NpadVibrationDevice* GetNSVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
96 NpadGcVibrationDevice* GetGcVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
97 Result SetAruidValidForVibration(u64 aruid, bool is_enabled);
98 void SetForceHandheldStyleVibration(bool is_forced);
99 Result IsVibrationAruidActive(u64 aruid, bool& is_active) const;
100 Result GetVibrationDeviceInfo(Core::HID::VibrationDeviceInfo& device_info,
101 const Core::HID::VibrationDeviceHandle& handle);
102 Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle,
103 const Core::HID::VibrationValue& value);
104
82 void UpdateControllers(std::chrono::nanoseconds ns_late); 105 void UpdateControllers(std::chrono::nanoseconds ns_late);
83 void UpdateNpad(std::chrono::nanoseconds ns_late); 106 void UpdateNpad(std::chrono::nanoseconds ns_late);
84 void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late); 107 void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
@@ -113,6 +136,8 @@ private:
113 std::shared_ptr<TouchScreen> touch_screen = nullptr; 136 std::shared_ptr<TouchScreen> touch_screen = nullptr;
114 std::shared_ptr<UniquePad> unique_pad = nullptr; 137 std::shared_ptr<UniquePad> unique_pad = nullptr;
115 138
139 std::shared_ptr<HandheldConfig> handheld_config = nullptr;
140
116 // TODO: Create these resources 141 // TODO: Create these resources
117 // std::shared_ptr<AudioControl> audio_control = nullptr; 142 // std::shared_ptr<AudioControl> audio_control = nullptr;
118 // std::shared_ptr<ButtonConfig> button_config = nullptr; 143 // std::shared_ptr<ButtonConfig> button_config = nullptr;
diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp
index d4e4181bf..e399edfd7 100644
--- a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp
+++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp
@@ -115,7 +115,7 @@ Result NpadAbstractIrSensorHandler::GetXcdHandleForNpadWithIrSensor(u64& handle)
115 if (sensor_state < NpadIrSensorState::Available) { 115 if (sensor_state < NpadIrSensorState::Available) {
116 return ResultIrSensorIsNotReady; 116 return ResultIrSensorIsNotReady;
117 } 117 }
118 handle = xcd_handle; 118 // handle = xcd_handle;
119 return ResultSuccess; 119 return ResultSuccess;
120} 120}
121 121
diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h
index fe8e005af..997811511 100644
--- a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h
+++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h
@@ -7,6 +7,10 @@
7#include "core/hle/result.h" 7#include "core/hle/result.h"
8#include "hid_core/hid_types.h" 8#include "hid_core/hid_types.h"
9 9
10namespace Core::HID {
11class EmulatedController;
12}
13
10namespace Kernel { 14namespace Kernel {
11class KEvent; 15class KEvent;
12class KReadableEvent; 16class KReadableEvent;
@@ -50,7 +54,7 @@ private:
50 54
51 s32 ref_counter{}; 55 s32 ref_counter{};
52 Kernel::KEvent* ir_sensor_event{nullptr}; 56 Kernel::KEvent* ir_sensor_event{nullptr};
53 u64 xcd_handle{}; 57 Core::HID::EmulatedController* xcd_handle{};
54 NpadIrSensorState sensor_state{}; 58 NpadIrSensorState sensor_state{};
55}; 59};
56} // namespace Service::HID 60} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.cpp b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp
index 2c7691d7c..435b095f0 100644
--- a/src/hid_core/resources/abstracted_pad/abstract_pad.cpp
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "hid_core/hid_core.h"
4#include "hid_core/hid_result.h" 5#include "hid_core/hid_result.h"
5#include "hid_core/resources/abstracted_pad/abstract_pad.h" 6#include "hid_core/resources/abstracted_pad/abstract_pad.h"
6#include "hid_core/resources/applet_resource.h" 7#include "hid_core/resources/applet_resource.h"
@@ -16,7 +17,7 @@ void AbstractPad::SetExternals(AppletResourceHolder* applet_resource,
16 CaptureButtonResource* capture_button_resource, 17 CaptureButtonResource* capture_button_resource,
17 HomeButtonResource* home_button_resource, 18 HomeButtonResource* home_button_resource,
18 SixAxisResource* sixaxis_resource, PalmaResource* palma_resource, 19 SixAxisResource* sixaxis_resource, PalmaResource* palma_resource,
19 VibrationHandler* vibration) { 20 NpadVibration* vibration, Core::HID::HIDCore* core) {
20 applet_resource_holder = applet_resource; 21 applet_resource_holder = applet_resource;
21 22
22 properties_handler.SetAppletResource(applet_resource_holder); 23 properties_handler.SetAppletResource(applet_resource_holder);
@@ -35,13 +36,14 @@ void AbstractPad::SetExternals(AppletResourceHolder* applet_resource,
35 mcu_handler.SetAbstractPadHolder(&abstract_pad_holder); 36 mcu_handler.SetAbstractPadHolder(&abstract_pad_holder);
36 mcu_handler.SetPropertiesHandler(&properties_handler); 37 mcu_handler.SetPropertiesHandler(&properties_handler);
37 38
38 std::array<NpadVibrationDevice*, 2> vibration_devices{&vibration_left, &vibration_right};
39 vibration_handler.SetAppletResource(applet_resource_holder); 39 vibration_handler.SetAppletResource(applet_resource_holder);
40 vibration_handler.SetAbstractPadHolder(&abstract_pad_holder); 40 vibration_handler.SetAbstractPadHolder(&abstract_pad_holder);
41 vibration_handler.SetPropertiesHandler(&properties_handler); 41 vibration_handler.SetPropertiesHandler(&properties_handler);
42 vibration_handler.SetN64Vibration(&vibration_n64); 42 vibration_handler.SetN64Vibration(&vibration_n64);
43 vibration_handler.SetVibration(vibration_devices); 43 vibration_handler.SetVibration(&vibration_left, &vibration_right);
44 vibration_handler.SetGcVibration(&vibration_gc); 44 vibration_handler.SetGcVibration(&vibration_gc);
45 vibration_handler.SetVibrationHandler(vibration);
46 vibration_handler.SetHidCore(core);
45 47
46 sixaxis_handler.SetAppletResource(applet_resource_holder); 48 sixaxis_handler.SetAppletResource(applet_resource_holder);
47 sixaxis_handler.SetAbstractPadHolder(&abstract_pad_holder); 49 sixaxis_handler.SetAbstractPadHolder(&abstract_pad_holder);
@@ -239,11 +241,6 @@ NpadVibrationDevice* AbstractPad::GetVibrationDevice(Core::HID::DeviceIndex devi
239 return &vibration_left; 241 return &vibration_left;
240} 242}
241 243
242void AbstractPad::GetLeftRightVibrationDevice(std::vector<NpadVibrationDevice*> list) {
243 list.emplace_back(&vibration_left);
244 list.emplace_back(&vibration_right);
245}
246
247NpadGcVibrationDevice* AbstractPad::GetGCVibrationDevice() { 244NpadGcVibrationDevice* AbstractPad::GetGCVibrationDevice() {
248 return &vibration_gc; 245 return &vibration_gc;
249} 246}
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.h b/src/hid_core/resources/abstracted_pad/abstract_pad.h
index cbdf84af7..329792457 100644
--- a/src/hid_core/resources/abstracted_pad/abstract_pad.h
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad.h
@@ -32,7 +32,6 @@ class AppletResource;
32class SixAxisResource; 32class SixAxisResource;
33class PalmaResource; 33class PalmaResource;
34class NPadResource; 34class NPadResource;
35class AbstractPad;
36class NpadLastActiveHandler; 35class NpadLastActiveHandler;
37class NpadIrNfcHandler; 36class NpadIrNfcHandler;
38class UniquePads; 37class UniquePads;
@@ -44,7 +43,6 @@ class NpadGcVibration;
44 43
45class CaptureButtonResource; 44class CaptureButtonResource;
46class HomeButtonResource; 45class HomeButtonResource;
47class VibrationHandler;
48 46
49struct HandheldConfig; 47struct HandheldConfig;
50 48
@@ -57,7 +55,8 @@ public:
57 void SetExternals(AppletResourceHolder* applet_resource, 55 void SetExternals(AppletResourceHolder* applet_resource,
58 CaptureButtonResource* capture_button_resource, 56 CaptureButtonResource* capture_button_resource,
59 HomeButtonResource* home_button_resource, SixAxisResource* sixaxis_resource, 57 HomeButtonResource* home_button_resource, SixAxisResource* sixaxis_resource,
60 PalmaResource* palma_resource, VibrationHandler* vibration); 58 PalmaResource* palma_resource, NpadVibration* vibration,
59 Core::HID::HIDCore* core);
61 void SetNpadId(Core::HID::NpadIdType npad_id); 60 void SetNpadId(Core::HID::NpadIdType npad_id);
62 61
63 Result Activate(); 62 Result Activate();
@@ -78,7 +77,6 @@ public:
78 77
79 NpadN64VibrationDevice* GetN64VibrationDevice(); 78 NpadN64VibrationDevice* GetN64VibrationDevice();
80 NpadVibrationDevice* GetVibrationDevice(Core::HID::DeviceIndex device_index); 79 NpadVibrationDevice* GetVibrationDevice(Core::HID::DeviceIndex device_index);
81 void GetLeftRightVibrationDevice(std::vector<NpadVibrationDevice*> list);
82 NpadGcVibrationDevice* GetGCVibrationDevice(); 80 NpadGcVibrationDevice* GetGCVibrationDevice();
83 81
84 Core::HID::NpadIdType GetLastActiveNpad(); 82 Core::HID::NpadIdType GetLastActiveNpad();
diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp
index a00d6c9de..ca64b0a43 100644
--- a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp
+++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "hid_core/frontend/emulated_controller.h"
5#include "hid_core/hid_core.h"
4#include "hid_core/hid_result.h" 6#include "hid_core/hid_result.h"
5#include "hid_core/hid_util.h" 7#include "hid_core/hid_util.h"
6#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" 8#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
@@ -30,14 +32,22 @@ void NpadAbstractVibrationHandler::SetPropertiesHandler(NpadAbstractPropertiesHa
30 properties_handler = handler; 32 properties_handler = handler;
31} 33}
32 34
35void NpadAbstractVibrationHandler::SetVibrationHandler(NpadVibration* handler) {
36 vibration_handler = handler;
37}
38
39void NpadAbstractVibrationHandler::SetHidCore(Core::HID::HIDCore* core) {
40 hid_core = core;
41}
42
33void NpadAbstractVibrationHandler::SetN64Vibration(NpadN64VibrationDevice* n64_device) { 43void NpadAbstractVibrationHandler::SetN64Vibration(NpadN64VibrationDevice* n64_device) {
34 n64_vibration_device = n64_device; 44 n64_vibration_device = n64_device;
35} 45}
36 46
37void NpadAbstractVibrationHandler::SetVibration(std::span<NpadVibrationDevice*> device) { 47void NpadAbstractVibrationHandler::SetVibration(NpadVibrationDevice* left_device,
38 for (std::size_t i = 0; i < device.size() && i < vibration_device.size(); i++) { 48 NpadVibrationDevice* right_device) {
39 vibration_device[i] = device[i]; 49 left_vibration_device = left_device;
40 } 50 right_vibration_device = right_device;
41} 51}
42 52
43void NpadAbstractVibrationHandler::SetGcVibration(NpadGcVibrationDevice* gc_device) { 53void NpadAbstractVibrationHandler::SetGcVibration(NpadGcVibrationDevice* gc_device) {
@@ -69,5 +79,29 @@ void NpadAbstractVibrationHandler::UpdateVibrationState() {
69 if (!is_handheld_hid_enabled && is_force_handheld_style_vibration) { 79 if (!is_handheld_hid_enabled && is_force_handheld_style_vibration) {
70 // TODO 80 // TODO
71 } 81 }
82
83 // TODO: This function isn't accurate. It's supposed to get 5 abstracted pads from the
84 // NpadAbstractPropertiesHandler but this handler isn't fully implemented yet
85 IAbstractedPad abstracted_pad{};
86 const auto npad_id = properties_handler->GetNpadId();
87 abstracted_pad.xcd_handle = hid_core->GetEmulatedController(npad_id);
88 abstracted_pad.internal_flags.is_connected.Assign(abstracted_pad.xcd_handle->IsConnected());
89
90 if (abstracted_pad.internal_flags.is_connected) {
91 left_vibration_device->Mount(abstracted_pad, Core::HID::DeviceIndex::Left,
92 vibration_handler);
93 right_vibration_device->Mount(abstracted_pad, Core::HID::DeviceIndex::Right,
94 vibration_handler);
95 gc_vibration_device->Mount(abstracted_pad, 0, vibration_handler);
96 gc_vibration_device->Mount(abstracted_pad, 0, vibration_handler);
97 n64_vibration_device->Mount(abstracted_pad, vibration_handler);
98 return;
99 }
100
101 left_vibration_device->Unmount();
102 right_vibration_device->Unmount();
103 gc_vibration_device->Unmount();
104 gc_vibration_device->Unmount();
105 n64_vibration_device->Unmount();
72} 106}
73} // namespace Service::HID 107} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h
index aeb07ce86..8bc8129c2 100644
--- a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h
+++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h
@@ -9,6 +9,10 @@
9#include "core/hle/result.h" 9#include "core/hle/result.h"
10#include "hid_core/hid_types.h" 10#include "hid_core/hid_types.h"
11 11
12namespace Core::HID {
13class HIDCore;
14}
15
12namespace Service::HID { 16namespace Service::HID {
13struct AppletResourceHolder; 17struct AppletResourceHolder;
14class NpadAbstractedPadHolder; 18class NpadAbstractedPadHolder;
@@ -27,9 +31,11 @@ public:
27 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); 31 void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
28 void SetAppletResource(AppletResourceHolder* applet_resource); 32 void SetAppletResource(AppletResourceHolder* applet_resource);
29 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); 33 void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
34 void SetVibrationHandler(NpadVibration* handler);
35 void SetHidCore(Core::HID::HIDCore* core);
30 36
31 void SetN64Vibration(NpadN64VibrationDevice* n64_device); 37 void SetN64Vibration(NpadN64VibrationDevice* n64_device);
32 void SetVibration(std::span<NpadVibrationDevice*> device); 38 void SetVibration(NpadVibrationDevice* left_device, NpadVibrationDevice* right_device);
33 void SetGcVibration(NpadGcVibrationDevice* gc_device); 39 void SetGcVibration(NpadGcVibrationDevice* gc_device);
34 40
35 Result IncrementRefCounter(); 41 Result IncrementRefCounter();
@@ -41,9 +47,11 @@ private:
41 AppletResourceHolder* applet_resource_holder{nullptr}; 47 AppletResourceHolder* applet_resource_holder{nullptr};
42 NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; 48 NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
43 NpadAbstractPropertiesHandler* properties_handler{nullptr}; 49 NpadAbstractPropertiesHandler* properties_handler{nullptr};
50 Core::HID::HIDCore* hid_core{nullptr};
44 51
45 NpadN64VibrationDevice* n64_vibration_device{nullptr}; 52 NpadN64VibrationDevice* n64_vibration_device{nullptr};
46 std::array<NpadVibrationDevice*, 2> vibration_device{}; 53 NpadVibrationDevice* left_vibration_device{};
54 NpadVibrationDevice* right_vibration_device{};
47 NpadGcVibrationDevice* gc_vibration_device{nullptr}; 55 NpadGcVibrationDevice* gc_vibration_device{nullptr};
48 NpadVibration* vibration_handler{nullptr}; 56 NpadVibration* vibration_handler{nullptr};
49 s32 ref_counter{}; 57 s32 ref_counter{};
diff --git a/src/hid_core/resources/applet_resource.cpp b/src/hid_core/resources/applet_resource.cpp
index a84826050..db4134037 100644
--- a/src/hid_core/resources/applet_resource.cpp
+++ b/src/hid_core/resources/applet_resource.cpp
@@ -200,6 +200,25 @@ void AppletResource::EnableInput(u64 aruid, bool is_enabled) {
200 data[index].flag.enable_touchscreen.Assign(is_enabled); 200 data[index].flag.enable_touchscreen.Assign(is_enabled);
201} 201}
202 202
203bool AppletResource::SetAruidValidForVibration(u64 aruid, bool is_enabled) {
204 const u64 index = GetIndexFromAruid(aruid);
205 if (index >= AruidIndexMax) {
206 return false;
207 }
208
209 if (!is_enabled && aruid == active_vibration_aruid) {
210 active_vibration_aruid = SystemAruid;
211 return true;
212 }
213
214 if (is_enabled && aruid != active_vibration_aruid) {
215 active_vibration_aruid = aruid;
216 return true;
217 }
218
219 return false;
220}
221
203void AppletResource::EnableSixAxisSensor(u64 aruid, bool is_enabled) { 222void AppletResource::EnableSixAxisSensor(u64 aruid, bool is_enabled) {
204 const u64 index = GetIndexFromAruid(aruid); 223 const u64 index = GetIndexFromAruid(aruid);
205 if (index >= AruidIndexMax) { 224 if (index >= AruidIndexMax) {
diff --git a/src/hid_core/resources/applet_resource.h b/src/hid_core/resources/applet_resource.h
index f3f32bac1..e9710d306 100644
--- a/src/hid_core/resources/applet_resource.h
+++ b/src/hid_core/resources/applet_resource.h
@@ -101,6 +101,7 @@ public:
101 Result DestroySevenSixAxisTransferMemory(); 101 Result DestroySevenSixAxisTransferMemory();
102 102
103 void EnableInput(u64 aruid, bool is_enabled); 103 void EnableInput(u64 aruid, bool is_enabled);
104 bool SetAruidValidForVibration(u64 aruid, bool is_enabled);
104 void EnableSixAxisSensor(u64 aruid, bool is_enabled); 105 void EnableSixAxisSensor(u64 aruid, bool is_enabled);
105 void EnablePadInput(u64 aruid, bool is_enabled); 106 void EnablePadInput(u64 aruid, bool is_enabled);
106 void EnableTouchScreen(u64 aruid, bool is_enabled); 107 void EnableTouchScreen(u64 aruid, bool is_enabled);
diff --git a/src/hid_core/resources/hid_firmware_settings.cpp b/src/hid_core/resources/hid_firmware_settings.cpp
index 9fa0db17e..00ceff7e6 100644
--- a/src/hid_core/resources/hid_firmware_settings.cpp
+++ b/src/hid_core/resources/hid_firmware_settings.cpp
@@ -1,11 +1,14 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "core/hle/service/set/system_settings_server.h"
5#include "core/hle/service/sm/sm.h"
4#include "hid_core/resources/hid_firmware_settings.h" 6#include "hid_core/resources/hid_firmware_settings.h"
5 7
6namespace Service::HID { 8namespace Service::HID {
7 9
8HidFirmwareSettings::HidFirmwareSettings() { 10HidFirmwareSettings::HidFirmwareSettings(Core::System& system) {
11 m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys");
9 LoadSettings(true); 12 LoadSettings(true);
10} 13}
11 14
@@ -18,21 +21,25 @@ void HidFirmwareSettings::LoadSettings(bool reload_config) {
18 return; 21 return;
19 } 22 }
20 23
21 // TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values 24 m_set_sys->GetSettingsItemValue<bool>(is_debug_pad_enabled, "hid_debug", "enables_debugpad");
22 25 m_set_sys->GetSettingsItemValue<bool>(is_device_managed, "hid_debug", "manages_devices");
23 is_debug_pad_enabled = true; 26 m_set_sys->GetSettingsItemValue<bool>(is_touch_i2c_managed, "hid_debug",
24 is_device_managed = true; 27 "manages_touch_ic_i2c");
25 is_touch_i2c_managed = is_device_managed; 28 m_set_sys->GetSettingsItemValue<bool>(is_future_devices_emulated, "hid_debug",
26 is_future_devices_emulated = false; 29 "emulate_future_device");
27 is_mcu_hardware_error_emulated = false; 30 m_set_sys->GetSettingsItemValue<bool>(is_mcu_hardware_error_emulated, "hid_debug",
28 is_rail_enabled = true; 31 "emulate_mcu_hardware_error");
29 is_firmware_update_failure_emulated = false; 32 m_set_sys->GetSettingsItemValue<bool>(is_rail_enabled, "hid_debug", "enables_rail");
33 m_set_sys->GetSettingsItemValue<bool>(is_firmware_update_failure_emulated, "hid_debug",
34 "emulate_firmware_update_failure");
30 is_firmware_update_failure = {}; 35 is_firmware_update_failure = {};
31 is_ble_disabled = false; 36 m_set_sys->GetSettingsItemValue<bool>(is_ble_disabled, "hid_debug", "ble_disabled");
32 is_dscale_disabled = false; 37 m_set_sys->GetSettingsItemValue<bool>(is_dscale_disabled, "hid_debug", "dscale_disabled");
33 is_handheld_forced = true; 38 m_set_sys->GetSettingsItemValue<bool>(is_handheld_forced, "hid_debug", "force_handheld");
34 features_per_id_disabled = {}; 39 features_per_id_disabled = {};
35 is_touch_firmware_auto_update_disabled = false; 40 m_set_sys->GetSettingsItemValue<bool>(is_touch_firmware_auto_update_disabled, "hid_debug",
41 "touch_firmware_auto_update_disabled");
42
36 is_initialized = true; 43 is_initialized = true;
37} 44}
38 45
diff --git a/src/hid_core/resources/hid_firmware_settings.h b/src/hid_core/resources/hid_firmware_settings.h
index 00201fd94..3694fa9a3 100644
--- a/src/hid_core/resources/hid_firmware_settings.h
+++ b/src/hid_core/resources/hid_firmware_settings.h
@@ -5,6 +5,14 @@
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7 7
8namespace Core {
9class System;
10}
11
12namespace Service::Set {
13class ISystemSettingsServer;
14}
15
8namespace Service::HID { 16namespace Service::HID {
9 17
10/// Loads firmware config from nn::settings::fwdbg 18/// Loads firmware config from nn::settings::fwdbg
@@ -13,7 +21,7 @@ public:
13 using FirmwareSetting = std::array<u8, 4>; 21 using FirmwareSetting = std::array<u8, 4>;
14 using FeaturesPerId = std::array<bool, 0xA8>; 22 using FeaturesPerId = std::array<bool, 0xA8>;
15 23
16 HidFirmwareSettings(); 24 HidFirmwareSettings(Core::System& system);
17 25
18 void Reload(); 26 void Reload();
19 void LoadSettings(bool reload_config); 27 void LoadSettings(bool reload_config);
@@ -49,6 +57,8 @@ private:
49 bool is_touch_firmware_auto_update_disabled{}; 57 bool is_touch_firmware_auto_update_disabled{};
50 FirmwareSetting is_firmware_update_failure{}; 58 FirmwareSetting is_firmware_update_failure{};
51 FeaturesPerId features_per_id_disabled{}; 59 FeaturesPerId features_per_id_disabled{};
60
61 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
52}; 62};
53 63
54} // namespace Service::HID 64} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp
index 97537a2e2..212f01429 100644
--- a/src/hid_core/resources/npad/npad.cpp
+++ b/src/hid_core/resources/npad/npad.cpp
@@ -21,6 +21,7 @@
21#include "hid_core/hid_util.h" 21#include "hid_core/hid_util.h"
22#include "hid_core/resources/applet_resource.h" 22#include "hid_core/resources/applet_resource.h"
23#include "hid_core/resources/npad/npad.h" 23#include "hid_core/resources/npad/npad.h"
24#include "hid_core/resources/npad/npad_vibration.h"
24#include "hid_core/resources/shared_memory_format.h" 25#include "hid_core/resources/shared_memory_format.h"
25 26
26namespace Service::HID { 27namespace Service::HID {
@@ -31,10 +32,6 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
31 for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) { 32 for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
32 auto& controller = controller_data[aruid_index][i]; 33 auto& controller = controller_data[aruid_index][i];
33 controller.device = hid_core.GetEmulatedControllerByIndex(i); 34 controller.device = hid_core.GetEmulatedControllerByIndex(i);
34 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
35 Core::HID::DEFAULT_VIBRATION_VALUE;
36 controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex]
37 .latest_vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE;
38 Core::HID::ControllerUpdateCallback engine_callback{ 35 Core::HID::ControllerUpdateCallback engine_callback{
39 .on_change = 36 .on_change =
40 [this, i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); }, 37 [this, i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
@@ -43,6 +40,10 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
43 controller.callback_key = controller.device->SetCallback(engine_callback); 40 controller.callback_key = controller.device->SetCallback(engine_callback);
44 } 41 }
45 } 42 }
43 for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
44 abstracted_pads[i] = AbstractPad{};
45 abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
46 }
46} 47}
47 48
48NPad::~NPad() { 49NPad::~NPad() {
@@ -359,6 +360,7 @@ void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) {
359 npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id); 360 npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
360 WriteEmptyEntry(controller.shared_memory); 361 WriteEmptyEntry(controller.shared_memory);
361 hid_core.SetLastActiveController(npad_id); 362 hid_core.SetLastActiveController(npad_id);
363 abstracted_pads[NpadIdTypeToIndex(npad_id)].Update();
362} 364}
363 365
364void NPad::WriteEmptyEntry(NpadInternalState* npad) { 366void NPad::WriteEmptyEntry(NpadInternalState* npad) {
@@ -740,171 +742,6 @@ bool NPad::SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID:
740 return true; 742 return true;
741} 743}
742 744
743bool NPad::VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
744 std::size_t device_index,
745 const Core::HID::VibrationValue& vibration_value) {
746 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
747 if (!controller.device->IsConnected()) {
748 return false;
749 }
750
751 if (!controller.device->IsVibrationEnabled(device_index)) {
752 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
753 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
754 // Send an empty vibration to stop any vibrations.
755 Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
756 controller.device->SetVibration(device_index, vibration);
757 // Then reset the vibration value to its default value.
758 controller.vibration[device_index].latest_vibration_value =
759 Core::HID::DEFAULT_VIBRATION_VALUE;
760 }
761
762 return false;
763 }
764
765 if (!Settings::values.enable_accurate_vibrations.GetValue()) {
766 using std::chrono::duration_cast;
767 using std::chrono::milliseconds;
768 using std::chrono::steady_clock;
769
770 const auto now = steady_clock::now();
771
772 // Filter out non-zero vibrations that are within 15ms of each other.
773 if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
774 duration_cast<milliseconds>(
775 now - controller.vibration[device_index].last_vibration_timepoint) <
776 milliseconds(15)) {
777 return false;
778 }
779
780 controller.vibration[device_index].last_vibration_timepoint = now;
781 }
782
783 Core::HID::VibrationValue vibration{
784 vibration_value.low_amplitude, vibration_value.low_frequency,
785 vibration_value.high_amplitude, vibration_value.high_frequency};
786 return controller.device->SetVibration(device_index, vibration);
787}
788
789void NPad::VibrateController(u64 aruid,
790 const Core::HID::VibrationDeviceHandle& vibration_device_handle,
791 const Core::HID::VibrationValue& vibration_value) {
792 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
793 return;
794 }
795
796 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
797 return;
798 }
799
800 auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
801 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
802
803 if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) {
804 return;
805 }
806
807 if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
808 ASSERT_MSG(false, "DeviceIndex should never be None!");
809 return;
810 }
811
812 // Some games try to send mismatched parameters in the device handle, block these.
813 if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
814 (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight ||
815 vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) ||
816 (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight &&
817 (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft ||
818 vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) {
819 return;
820 }
821
822 // Filter out vibrations with equivalent values to reduce unnecessary state changes.
823 if (vibration_value.low_amplitude ==
824 controller.vibration[device_index].latest_vibration_value.low_amplitude &&
825 vibration_value.high_amplitude ==
826 controller.vibration[device_index].latest_vibration_value.high_amplitude) {
827 return;
828 }
829
830 if (VibrateControllerAtIndex(aruid, controller.device->GetNpadIdType(), device_index,
831 vibration_value)) {
832 controller.vibration[device_index].latest_vibration_value = vibration_value;
833 }
834}
835
836void NPad::VibrateControllers(
837 u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
838 std::span<const Core::HID::VibrationValue> vibration_values) {
839 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
840 return;
841 }
842
843 ASSERT_OR_EXECUTE_MSG(
844 vibration_device_handles.size() == vibration_values.size(), { return; },
845 "The amount of device handles does not match with the amount of vibration values,"
846 "this is undefined behavior!");
847
848 for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
849 VibrateController(aruid, vibration_device_handles[i], vibration_values[i]);
850 }
851}
852
853Core::HID::VibrationValue NPad::GetLastVibration(
854 u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
855 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
856 return {};
857 }
858
859 const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
860 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
861 return controller.vibration[device_index].latest_vibration_value;
862}
863
864void NPad::InitializeVibrationDevice(
865 const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
866 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
867 return;
868 }
869
870 const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
871 const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
872 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
873
874 if (aruid == 0) {
875 return;
876 }
877
878 InitializeVibrationDeviceAtIndex(aruid, npad_index, device_index);
879}
880
881void NPad::InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
882 std::size_t device_index) {
883 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
884 if (!Settings::values.vibration_enabled.GetValue()) {
885 controller.vibration[device_index].device_mounted = false;
886 return;
887 }
888
889 controller.vibration[device_index].device_mounted =
890 controller.device->IsVibrationEnabled(device_index);
891}
892
893void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
894 permit_vibration_session_enabled = permit_vibration_session;
895}
896
897bool NPad::IsVibrationDeviceMounted(
898 u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
899 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
900 return false;
901 }
902
903 const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
904 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
905 return controller.vibration[device_index].device_mounted;
906}
907
908Result NPad::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event, 745Result NPad::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
909 Core::HID::NpadIdType npad_id) { 746 Core::HID::NpadIdType npad_id) {
910 std::scoped_lock lock{mutex}; 747 std::scoped_lock lock{mutex};
@@ -936,11 +773,6 @@ Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) {
936 773
937 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id); 774 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
938 auto& controller = GetControllerFromNpadIdType(aruid, npad_id); 775 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
939 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
940 // Send an empty vibration to stop any vibrations.
941 VibrateControllerAtIndex(aruid, npad_id, device_idx, {});
942 controller.vibration[device_idx].device_mounted = false;
943 }
944 776
945 auto* shared_memory = controller.shared_memory; 777 auto* shared_memory = controller.shared_memory;
946 // Don't reset shared_memory->assignment_mode this value is persistent 778 // Don't reset shared_memory->assignment_mode this value is persistent
@@ -1232,26 +1064,28 @@ Result NPad::RegisterAppletResourceUserId(u64 aruid) {
1232} 1064}
1233 1065
1234void NPad::UnregisterAppletResourceUserId(u64 aruid) { 1066void NPad::UnregisterAppletResourceUserId(u64 aruid) {
1067 // TODO: Remove this once abstract pad is emulated properly
1068 const auto aruid_index = npad_resource.GetIndexFromAruid(aruid);
1069 for (auto& controller : controller_data[aruid_index]) {
1070 controller.is_connected = false;
1071 controller.shared_memory = nullptr;
1072 }
1073
1235 npad_resource.UnregisterAppletResourceUserId(aruid); 1074 npad_resource.UnregisterAppletResourceUserId(aruid);
1236} 1075}
1237 1076
1238void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, 1077void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
1239 std::recursive_mutex* shared_mutex) { 1078 std::recursive_mutex* shared_mutex,
1079 std::shared_ptr<HandheldConfig> handheld_config) {
1240 applet_resource_holder.applet_resource = resource; 1080 applet_resource_holder.applet_resource = resource;
1241 applet_resource_holder.shared_mutex = shared_mutex; 1081 applet_resource_holder.shared_mutex = shared_mutex;
1242 applet_resource_holder.shared_npad_resource = &npad_resource; 1082 applet_resource_holder.shared_npad_resource = &npad_resource;
1243} 1083 applet_resource_holder.handheld_config = handheld_config;
1244
1245NPad::NpadControllerData& NPad::GetControllerFromHandle(
1246 u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) {
1247 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1248 return GetControllerFromNpadIdType(aruid, npad_id);
1249}
1250 1084
1251const NPad::NpadControllerData& NPad::GetControllerFromHandle( 1085 for (auto& abstract_pad : abstracted_pads) {
1252 u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const { 1086 abstract_pad.SetExternals(&applet_resource_holder, nullptr, nullptr, nullptr, nullptr,
1253 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1087 &vibration_handler, &hid_core);
1254 return GetControllerFromNpadIdType(aruid, npad_id); 1088 }
1255} 1089}
1256 1090
1257NPad::NpadControllerData& NPad::GetControllerFromHandle( 1091NPad::NpadControllerData& NPad::GetControllerFromHandle(
@@ -1389,4 +1223,97 @@ Result NPad::GetLastActiveNpad(Core::HID::NpadIdType& out_npad_id) const {
1389 return ResultSuccess; 1223 return ResultSuccess;
1390} 1224}
1391 1225
1226NpadVibration* NPad::GetVibrationHandler() {
1227 return &vibration_handler;
1228}
1229
1230std::vector<NpadVibrationBase*> NPad::GetAllVibrationDevices() {
1231 std::vector<NpadVibrationBase*> vibration_devices;
1232
1233 for (auto& abstract_pad : abstracted_pads) {
1234 auto* left_device = abstract_pad.GetVibrationDevice(Core::HID::DeviceIndex::Left);
1235 auto* right_device = abstract_pad.GetVibrationDevice(Core::HID::DeviceIndex::Right);
1236 auto* n64_device = abstract_pad.GetGCVibrationDevice();
1237 auto* gc_device = abstract_pad.GetGCVibrationDevice();
1238
1239 if (left_device != nullptr) {
1240 vibration_devices.emplace_back(left_device);
1241 }
1242 if (right_device != nullptr) {
1243 vibration_devices.emplace_back(right_device);
1244 }
1245 if (n64_device != nullptr) {
1246 vibration_devices.emplace_back(n64_device);
1247 }
1248 if (gc_device != nullptr) {
1249 vibration_devices.emplace_back(gc_device);
1250 }
1251 }
1252
1253 return vibration_devices;
1254}
1255
1256NpadVibrationBase* NPad::GetVibrationDevice(const Core::HID::VibrationDeviceHandle& handle) {
1257 if (IsVibrationHandleValid(handle).IsError()) {
1258 return nullptr;
1259 }
1260
1261 const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
1262 const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
1263 if (style_inde == Core::HID::NpadStyleIndex::GameCube) {
1264 return abstracted_pads[npad_index].GetGCVibrationDevice();
1265 }
1266 if (style_inde == Core::HID::NpadStyleIndex::N64) {
1267 return abstracted_pads[npad_index].GetN64VibrationDevice();
1268 }
1269 return abstracted_pads[npad_index].GetVibrationDevice(handle.device_index);
1270}
1271
1272NpadN64VibrationDevice* NPad::GetN64VibrationDevice(
1273 const Core::HID::VibrationDeviceHandle& handle) {
1274 if (IsVibrationHandleValid(handle).IsError()) {
1275 return nullptr;
1276 }
1277
1278 const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
1279 const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
1280 if (style_inde != Core::HID::NpadStyleIndex::N64) {
1281 return nullptr;
1282 }
1283 return abstracted_pads[npad_index].GetN64VibrationDevice();
1284}
1285
1286NpadVibrationDevice* NPad::GetNSVibrationDevice(const Core::HID::VibrationDeviceHandle& handle) {
1287 if (IsVibrationHandleValid(handle).IsError()) {
1288 return nullptr;
1289 }
1290
1291 const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
1292 const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
1293 if (style_inde == Core::HID::NpadStyleIndex::GameCube ||
1294 style_inde == Core::HID::NpadStyleIndex::N64) {
1295 return nullptr;
1296 }
1297
1298 return abstracted_pads[npad_index].GetVibrationDevice(handle.device_index);
1299}
1300
1301NpadGcVibrationDevice* NPad::GetGcVibrationDevice(const Core::HID::VibrationDeviceHandle& handle) {
1302 if (IsVibrationHandleValid(handle).IsError()) {
1303 return nullptr;
1304 }
1305
1306 const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
1307 const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
1308 if (style_inde != Core::HID::NpadStyleIndex::GameCube) {
1309 return nullptr;
1310 }
1311 return abstracted_pads[npad_index].GetGCVibrationDevice();
1312}
1313
1314void NPad::UpdateHandheldAbstractState() {
1315 std::scoped_lock lock{mutex};
1316 abstracted_pads[NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld)].Update();
1317}
1318
1392} // namespace Service::HID 1319} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h
index 01f3dabb1..18b25c688 100644
--- a/src/hid_core/resources/npad/npad.h
+++ b/src/hid_core/resources/npad/npad.h
@@ -10,9 +10,15 @@
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "hid_core/hid_types.h" 12#include "hid_core/hid_types.h"
13#include "hid_core/resources/abstracted_pad/abstract_pad.h"
13#include "hid_core/resources/controller_base.h" 14#include "hid_core/resources/controller_base.h"
14#include "hid_core/resources/npad/npad_resource.h" 15#include "hid_core/resources/npad/npad_resource.h"
15#include "hid_core/resources/npad/npad_types.h" 16#include "hid_core/resources/npad/npad_types.h"
17#include "hid_core/resources/npad/npad_vibration.h"
18#include "hid_core/resources/vibration/gc_vibration_device.h"
19#include "hid_core/resources/vibration/n64_vibration_device.h"
20#include "hid_core/resources/vibration/vibration_base.h"
21#include "hid_core/resources/vibration/vibration_device.h"
16 22
17namespace Core::HID { 23namespace Core::HID {
18class EmulatedController; 24class EmulatedController;
@@ -32,6 +38,7 @@ union Result;
32 38
33namespace Service::HID { 39namespace Service::HID {
34class AppletResource; 40class AppletResource;
41struct HandheldConfig;
35struct NpadInternalState; 42struct NpadInternalState;
36struct NpadSixAxisSensorLifo; 43struct NpadSixAxisSensorLifo;
37struct NpadSharedMemoryFormat; 44struct NpadSharedMemoryFormat;
@@ -68,31 +75,6 @@ public:
68 bool SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, 75 bool SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
69 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode); 76 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode);
70 77
71 bool VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
72 std::size_t device_index,
73 const Core::HID::VibrationValue& vibration_value);
74
75 void VibrateController(u64 aruid,
76 const Core::HID::VibrationDeviceHandle& vibration_device_handle,
77 const Core::HID::VibrationValue& vibration_value);
78
79 void VibrateControllers(
80 u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
81 std::span<const Core::HID::VibrationValue> vibration_values);
82
83 Core::HID::VibrationValue GetLastVibration(
84 u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
85
86 void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
87
88 void InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
89 std::size_t device_index);
90
91 void SetPermitVibrationSession(bool permit_vibration_session);
92
93 bool IsVibrationDeviceMounted(
94 u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
95
96 Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event, 78 Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
97 Core::HID::NpadIdType npad_id); 79 Core::HID::NpadIdType npad_id);
98 80
@@ -145,7 +127,8 @@ public:
145 Result RegisterAppletResourceUserId(u64 aruid); 127 Result RegisterAppletResourceUserId(u64 aruid);
146 void UnregisterAppletResourceUserId(u64 aruid); 128 void UnregisterAppletResourceUserId(u64 aruid);
147 void SetNpadExternals(std::shared_ptr<AppletResource> resource, 129 void SetNpadExternals(std::shared_ptr<AppletResource> resource,
148 std::recursive_mutex* shared_mutex); 130 std::recursive_mutex* shared_mutex,
131 std::shared_ptr<HandheldConfig> handheld_config);
149 132
150 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); 133 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
151 134
@@ -161,18 +144,20 @@ public:
161 144
162 Result GetLastActiveNpad(Core::HID::NpadIdType& out_npad_id) const; 145 Result GetLastActiveNpad(Core::HID::NpadIdType& out_npad_id) const;
163 146
164private: 147 NpadVibration* GetVibrationHandler();
165 struct VibrationData { 148 std::vector<NpadVibrationBase*> GetAllVibrationDevices();
166 bool device_mounted{}; 149 NpadVibrationBase* GetVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
167 Core::HID::VibrationValue latest_vibration_value{}; 150 NpadN64VibrationDevice* GetN64VibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
168 std::chrono::steady_clock::time_point last_vibration_timepoint{}; 151 NpadVibrationDevice* GetNSVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
169 }; 152 NpadGcVibrationDevice* GetGcVibrationDevice(const Core::HID::VibrationDeviceHandle& handle);
170 153
154 void UpdateHandheldAbstractState();
155
156private:
171 struct NpadControllerData { 157 struct NpadControllerData {
172 NpadInternalState* shared_memory = nullptr; 158 NpadInternalState* shared_memory = nullptr;
173 Core::HID::EmulatedController* device = nullptr; 159 Core::HID::EmulatedController* device = nullptr;
174 160
175 std::array<VibrationData, 2> vibration{};
176 bool is_connected{}; 161 bool is_connected{};
177 162
178 // Dual joycons can have only one side connected 163 // Dual joycons can have only one side connected
@@ -192,10 +177,6 @@ private:
192 void WriteEmptyEntry(NpadInternalState* npad); 177 void WriteEmptyEntry(NpadInternalState* npad);
193 178
194 NpadControllerData& GetControllerFromHandle( 179 NpadControllerData& GetControllerFromHandle(
195 u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle);
196 const NpadControllerData& GetControllerFromHandle(
197 u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const;
198 NpadControllerData& GetControllerFromHandle(
199 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle); 180 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle);
200 const NpadControllerData& GetControllerFromHandle( 181 const NpadControllerData& GetControllerFromHandle(
201 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const; 182 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const;
@@ -215,11 +196,13 @@ private:
215 mutable std::mutex mutex; 196 mutable std::mutex mutex;
216 NPadResource npad_resource; 197 NPadResource npad_resource;
217 AppletResourceHolder applet_resource_holder{}; 198 AppletResourceHolder applet_resource_holder{};
199 std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads;
200 NpadVibration vibration_handler{};
201
218 Kernel::KEvent* input_event{nullptr}; 202 Kernel::KEvent* input_event{nullptr};
219 std::mutex* input_mutex{nullptr}; 203 std::mutex* input_mutex{nullptr};
220 204
221 std::atomic<u64> press_state{}; 205 std::atomic<u64> press_state{};
222 bool permit_vibration_session_enabled;
223 std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax> 206 std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax>
224 controller_data{}; 207 controller_data{};
225}; 208};
diff --git a/src/hid_core/resources/npad/npad_types.h b/src/hid_core/resources/npad/npad_types.h
index fd86c8e40..92700d69a 100644
--- a/src/hid_core/resources/npad/npad_types.h
+++ b/src/hid_core/resources/npad/npad_types.h
@@ -8,6 +8,10 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "hid_core/hid_types.h" 9#include "hid_core/hid_types.h"
10 10
11namespace Core::HID {
12class EmulatedController;
13}
14
11namespace Service::HID { 15namespace Service::HID {
12static constexpr std::size_t MaxSupportedNpadIdTypes = 10; 16static constexpr std::size_t MaxSupportedNpadIdTypes = 10;
13static constexpr std::size_t StyleIndexCount = 7; 17static constexpr std::size_t StyleIndexCount = 7;
@@ -348,7 +352,7 @@ struct IAbstractedPad {
348 u8 indicator; 352 u8 indicator;
349 std::vector<f32> virtual_six_axis_sensor_acceleration; 353 std::vector<f32> virtual_six_axis_sensor_acceleration;
350 std::vector<f32> virtual_six_axis_sensor_angle; 354 std::vector<f32> virtual_six_axis_sensor_angle;
351 u64 xcd_handle; 355 Core::HID::EmulatedController* xcd_handle;
352 u64 color; 356 u64 color;
353}; 357};
354} // namespace Service::HID 358} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad_vibration.cpp b/src/hid_core/resources/npad/npad_vibration.cpp
index 7056e8eab..05aad4c54 100644
--- a/src/hid_core/resources/npad/npad_vibration.cpp
+++ b/src/hid_core/resources/npad/npad_vibration.cpp
@@ -77,4 +77,8 @@ Result NpadVibration::EndPermitVibrationSession() {
77 return ResultSuccess; 77 return ResultSuccess;
78} 78}
79 79
80u64 NpadVibration::GetSessionAruid() const {
81 return session_aruid;
82}
83
80} // namespace Service::HID 84} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad_vibration.h b/src/hid_core/resources/npad/npad_vibration.h
index 0748aeffc..d5a95f2a0 100644
--- a/src/hid_core/resources/npad/npad_vibration.h
+++ b/src/hid_core/resources/npad/npad_vibration.h
@@ -25,6 +25,8 @@ public:
25 Result BeginPermitVibrationSession(u64 aruid); 25 Result BeginPermitVibrationSession(u64 aruid);
26 Result EndPermitVibrationSession(); 26 Result EndPermitVibrationSession();
27 27
28 u64 GetSessionAruid() const;
29
28private: 30private:
29 f32 volume{}; 31 f32 volume{};
30 u64 session_aruid{}; 32 u64 session_aruid{};
diff --git a/src/hid_core/resources/vibration/gc_vibration_device.cpp b/src/hid_core/resources/vibration/gc_vibration_device.cpp
index f01f81b9a..ad42b9d66 100644
--- a/src/hid_core/resources/vibration/gc_vibration_device.cpp
+++ b/src/hid_core/resources/vibration/gc_vibration_device.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "hid_core/frontend/emulated_controller.h"
4#include "hid_core/hid_result.h" 5#include "hid_core/hid_result.h"
5#include "hid_core/resources/npad/npad_types.h" 6#include "hid_core/resources/npad/npad_types.h"
6#include "hid_core/resources/npad/npad_vibration.h" 7#include "hid_core/resources/npad/npad_vibration.h"
@@ -10,24 +11,25 @@ namespace Service::HID {
10 11
11NpadGcVibrationDevice::NpadGcVibrationDevice() {} 12NpadGcVibrationDevice::NpadGcVibrationDevice() {}
12 13
13Result NpadGcVibrationDevice::IncrementRefCounter() { 14Result NpadGcVibrationDevice::Activate() {
14 if (ref_counter == 0 && is_mounted) { 15 if (ref_counter == 0 && is_mounted) {
15 f32 volume = 1.0f; 16 f32 volume = 1.0f;
16 const auto result = vibration_handler->GetVibrationVolume(volume); 17 const auto result = vibration_handler->GetVibrationVolume(volume);
17 if (result.IsSuccess()) { 18 if (result.IsSuccess()) {
18 // TODO: SendVibrationGcErmCommand 19 xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
19 } 20 }
20 } 21 }
22
21 ref_counter++; 23 ref_counter++;
22 return ResultSuccess; 24 return ResultSuccess;
23} 25}
24 26
25Result NpadGcVibrationDevice::DecrementRefCounter() { 27Result NpadGcVibrationDevice::Deactivate() {
26 if (ref_counter == 1 && !is_mounted) { 28 if (ref_counter == 1 && is_mounted) {
27 f32 volume = 1.0f; 29 f32 volume = 1.0f;
28 const auto result = vibration_handler->GetVibrationVolume(volume); 30 const auto result = vibration_handler->GetVibrationVolume(volume);
29 if (result.IsSuccess()) { 31 if (result.IsSuccess()) {
30 // TODO: SendVibrationGcErmCommand 32 xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
31 } 33 }
32 } 34 }
33 35
@@ -38,6 +40,48 @@ Result NpadGcVibrationDevice::DecrementRefCounter() {
38 return ResultSuccess; 40 return ResultSuccess;
39} 41}
40 42
43Result NpadGcVibrationDevice::Mount(IAbstractedPad& abstracted_pad, u32 slot,
44 NpadVibration* handler) {
45 if (!abstracted_pad.internal_flags.is_connected) {
46 return ResultSuccess;
47 }
48
49 // TODO: This device doesn't use a xcd handle instead has an GC adapter handle. This is just to
50 // keep compatibility with the front end.
51 xcd_handle = abstracted_pad.xcd_handle;
52 adapter_slot = slot;
53 vibration_handler = handler;
54 is_mounted = true;
55
56 if (ref_counter == 0) {
57 return ResultSuccess;
58 }
59
60 f32 volume{1.0f};
61 const auto result = vibration_handler->GetVibrationVolume(volume);
62 if (result.IsSuccess()) {
63 xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
64 }
65
66 return ResultSuccess;
67}
68
69Result NpadGcVibrationDevice::Unmount() {
70 if (ref_counter == 0 || !is_mounted) {
71 is_mounted = false;
72 return ResultSuccess;
73 }
74
75 f32 volume{1.0f};
76 const auto result = vibration_handler->GetVibrationVolume(volume);
77 if (result.IsSuccess()) {
78 xcd_handle->SetVibration(adapter_slot, Core::HID::VibrationGcErmCommand::Stop);
79 }
80
81 is_mounted = false;
82 return ResultSuccess;
83}
84
41Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command) { 85Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command) {
42 if (!is_mounted) { 86 if (!is_mounted) {
43 return ResultSuccess; 87 return ResultSuccess;
@@ -55,7 +99,7 @@ Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcEr
55 return ResultSuccess; 99 return ResultSuccess;
56 } 100 }
57 } 101 }
58 // TODO: SendVibrationGcErmCommand 102 xcd_handle->SetVibration(adapter_slot, command);
59 return ResultSuccess; 103 return ResultSuccess;
60} 104}
61 105
diff --git a/src/hid_core/resources/vibration/gc_vibration_device.h b/src/hid_core/resources/vibration/gc_vibration_device.h
index 87abca57d..c624cbb28 100644
--- a/src/hid_core/resources/vibration/gc_vibration_device.h
+++ b/src/hid_core/resources/vibration/gc_vibration_device.h
@@ -20,12 +20,18 @@ class NpadGcVibrationDevice final : public NpadVibrationBase {
20public: 20public:
21 explicit NpadGcVibrationDevice(); 21 explicit NpadGcVibrationDevice();
22 22
23 Result IncrementRefCounter() override; 23 Result Activate() override;
24 Result DecrementRefCounter() override; 24 Result Deactivate() override;
25
26 Result Mount(IAbstractedPad& abstracted_pad, u32 slot, NpadVibration* handler);
27 Result Unmount();
25 28
26 Result SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command); 29 Result SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command);
27 30
28 Result GetActualVibrationGcErmCommand(Core::HID::VibrationGcErmCommand& out_command); 31 Result GetActualVibrationGcErmCommand(Core::HID::VibrationGcErmCommand& out_command);
29 Result SendVibrationNotificationPattern(Core::HID::VibrationGcErmCommand command); 32 Result SendVibrationNotificationPattern(Core::HID::VibrationGcErmCommand command);
33
34private:
35 u32 adapter_slot;
30}; 36};
31} // namespace Service::HID 37} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/n64_vibration_device.cpp b/src/hid_core/resources/vibration/n64_vibration_device.cpp
index 639f87abf..94ad37c8f 100644
--- a/src/hid_core/resources/vibration/n64_vibration_device.cpp
+++ b/src/hid_core/resources/vibration/n64_vibration_device.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "hid_core/frontend/emulated_controller.h"
4#include "hid_core/hid_result.h" 5#include "hid_core/hid_result.h"
5#include "hid_core/resources/npad/npad_types.h" 6#include "hid_core/resources/npad/npad_types.h"
6#include "hid_core/resources/npad/npad_vibration.h" 7#include "hid_core/resources/npad/npad_vibration.h"
@@ -10,12 +11,12 @@ namespace Service::HID {
10 11
11NpadN64VibrationDevice::NpadN64VibrationDevice() {} 12NpadN64VibrationDevice::NpadN64VibrationDevice() {}
12 13
13Result NpadN64VibrationDevice::IncrementRefCounter() { 14Result NpadN64VibrationDevice::Activate() {
14 if (ref_counter == 0 && is_mounted) { 15 if (ref_counter == 0 && is_mounted) {
15 f32 volume = 1.0f; 16 f32 volume = 1.0f;
16 const auto result = vibration_handler->GetVibrationVolume(volume); 17 const auto result = vibration_handler->GetVibrationVolume(volume);
17 if (result.IsSuccess()) { 18 if (result.IsSuccess()) {
18 // TODO: SendVibrationInBool 19 xcd_handle->SetVibration(false);
19 } 20 }
20 } 21 }
21 22
@@ -23,19 +24,12 @@ Result NpadN64VibrationDevice::IncrementRefCounter() {
23 return ResultSuccess; 24 return ResultSuccess;
24} 25}
25 26
26Result NpadN64VibrationDevice::DecrementRefCounter() { 27Result NpadN64VibrationDevice::Deactivate() {
27 if (ref_counter == 1) { 28 if (ref_counter == 1 && is_mounted) {
28 if (!is_mounted) {
29 ref_counter = 0;
30 if (is_mounted != false) {
31 // TODO: SendVibrationInBool
32 }
33 return ResultSuccess;
34 }
35 f32 volume = 1.0f; 29 f32 volume = 1.0f;
36 const auto result = vibration_handler->GetVibrationVolume(volume); 30 const auto result = vibration_handler->GetVibrationVolume(volume);
37 if (result.IsSuccess()) { 31 if (result.IsSuccess()) {
38 // TODO 32 xcd_handle->SetVibration(false);
39 } 33 }
40 } 34 }
41 35
@@ -46,6 +40,43 @@ Result NpadN64VibrationDevice::DecrementRefCounter() {
46 return ResultSuccess; 40 return ResultSuccess;
47} 41}
48 42
43Result NpadN64VibrationDevice::Mount(IAbstractedPad& abstracted_pad, NpadVibration* handler) {
44 if (!abstracted_pad.internal_flags.is_connected) {
45 return ResultSuccess;
46 }
47 xcd_handle = abstracted_pad.xcd_handle;
48 vibration_handler = handler;
49 is_mounted = true;
50
51 if (ref_counter == 0) {
52 return ResultSuccess;
53 }
54
55 f32 volume{1.0f};
56 const auto result = vibration_handler->GetVibrationVolume(volume);
57 if (result.IsSuccess()) {
58 xcd_handle->SetVibration(false);
59 }
60
61 return ResultSuccess;
62}
63
64Result NpadN64VibrationDevice::Unmount() {
65 if (ref_counter == 0 || !is_mounted) {
66 is_mounted = false;
67 return ResultSuccess;
68 }
69
70 f32 volume{1.0f};
71 const auto result = vibration_handler->GetVibrationVolume(volume);
72 if (result.IsSuccess()) {
73 xcd_handle->SetVibration(false);
74 }
75
76 is_mounted = false;
77 return ResultSuccess;
78}
79
49Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) { 80Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) {
50 if (ref_counter < 1) { 81 if (ref_counter < 1) {
51 return ResultVibrationNotInitialized; 82 return ResultVibrationNotInitialized;
@@ -56,7 +87,7 @@ Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) {
56 if (result.IsError()) { 87 if (result.IsError()) {
57 return result; 88 return result;
58 } 89 }
59 // TODO: SendVibrationInBool 90 xcd_handle->SetVibration(false);
60 } 91 }
61 return ResultSuccess; 92 return ResultSuccess;
62} 93}
diff --git a/src/hid_core/resources/vibration/n64_vibration_device.h b/src/hid_core/resources/vibration/n64_vibration_device.h
index 54e6efc1a..09de7701c 100644
--- a/src/hid_core/resources/vibration/n64_vibration_device.h
+++ b/src/hid_core/resources/vibration/n64_vibration_device.h
@@ -14,14 +14,18 @@
14 14
15namespace Service::HID { 15namespace Service::HID {
16class NpadVibration; 16class NpadVibration;
17struct IAbstractedPad;
17 18
18/// Handles Npad request from HID interfaces 19/// Handles Npad request from HID interfaces
19class NpadN64VibrationDevice final : public NpadVibrationBase { 20class NpadN64VibrationDevice final : public NpadVibrationBase {
20public: 21public:
21 explicit NpadN64VibrationDevice(); 22 explicit NpadN64VibrationDevice();
22 23
23 Result IncrementRefCounter() override; 24 Result Activate() override;
24 Result DecrementRefCounter() override; 25 Result Deactivate() override;
26
27 Result Mount(IAbstractedPad& abstracted_pad, NpadVibration* handler);
28 Result Unmount();
25 29
26 Result SendValueInBool(bool is_vibrating); 30 Result SendValueInBool(bool is_vibrating);
27 Result SendVibrationNotificationPattern(u32 pattern); 31 Result SendVibrationNotificationPattern(u32 pattern);
diff --git a/src/hid_core/resources/vibration/vibration_base.cpp b/src/hid_core/resources/vibration/vibration_base.cpp
index 350f349c2..f28d30406 100644
--- a/src/hid_core/resources/vibration/vibration_base.cpp
+++ b/src/hid_core/resources/vibration/vibration_base.cpp
@@ -10,12 +10,12 @@ namespace Service::HID {
10 10
11NpadVibrationBase::NpadVibrationBase() {} 11NpadVibrationBase::NpadVibrationBase() {}
12 12
13Result NpadVibrationBase::IncrementRefCounter() { 13Result NpadVibrationBase::Activate() {
14 ref_counter++; 14 ref_counter++;
15 return ResultSuccess; 15 return ResultSuccess;
16} 16}
17 17
18Result NpadVibrationBase::DecrementRefCounter() { 18Result NpadVibrationBase::Deactivate() {
19 if (ref_counter > 0) { 19 if (ref_counter > 0) {
20 ref_counter--; 20 ref_counter--;
21 } 21 }
diff --git a/src/hid_core/resources/vibration/vibration_base.h b/src/hid_core/resources/vibration/vibration_base.h
index c6c5fc4d9..69c26e669 100644
--- a/src/hid_core/resources/vibration/vibration_base.h
+++ b/src/hid_core/resources/vibration/vibration_base.h
@@ -6,6 +6,10 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/result.h" 7#include "core/hle/result.h"
8 8
9namespace Core::HID {
10class EmulatedController;
11}
12
9namespace Service::HID { 13namespace Service::HID {
10class NpadVibration; 14class NpadVibration;
11 15
@@ -14,13 +18,13 @@ class NpadVibrationBase {
14public: 18public:
15 explicit NpadVibrationBase(); 19 explicit NpadVibrationBase();
16 20
17 virtual Result IncrementRefCounter(); 21 virtual Result Activate();
18 virtual Result DecrementRefCounter(); 22 virtual Result Deactivate();
19 23
20 bool IsVibrationMounted() const; 24 bool IsVibrationMounted() const;
21 25
22protected: 26protected:
23 u64 xcd_handle{}; 27 Core::HID::EmulatedController* xcd_handle{nullptr};
24 s32 ref_counter{}; 28 s32 ref_counter{};
25 bool is_mounted{}; 29 bool is_mounted{};
26 NpadVibration* vibration_handler{nullptr}; 30 NpadVibration* vibration_handler{nullptr};
diff --git a/src/hid_core/resources/vibration/vibration_device.cpp b/src/hid_core/resources/vibration/vibration_device.cpp
index 888c3a7ed..08b14591f 100644
--- a/src/hid_core/resources/vibration/vibration_device.cpp
+++ b/src/hid_core/resources/vibration/vibration_device.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "hid_core/frontend/emulated_controller.h"
4#include "hid_core/hid_result.h" 5#include "hid_core/hid_result.h"
5#include "hid_core/resources/npad/npad_types.h" 6#include "hid_core/resources/npad/npad_types.h"
6#include "hid_core/resources/npad/npad_vibration.h" 7#include "hid_core/resources/npad/npad_vibration.h"
@@ -10,12 +11,30 @@ namespace Service::HID {
10 11
11NpadVibrationDevice::NpadVibrationDevice() {} 12NpadVibrationDevice::NpadVibrationDevice() {}
12 13
13Result NpadVibrationDevice::IncrementRefCounter() { 14Result NpadVibrationDevice::Activate() {
15 if (ref_counter == 0 && is_mounted) {
16 f32 volume = 1.0f;
17 const auto result = vibration_handler->GetVibrationVolume(volume);
18 if (result.IsSuccess()) {
19 xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
20 // TODO: SendNotificationPattern;
21 }
22 }
23
14 ref_counter++; 24 ref_counter++;
15 return ResultSuccess; 25 return ResultSuccess;
16} 26}
17 27
18Result NpadVibrationDevice::DecrementRefCounter() { 28Result NpadVibrationDevice::Deactivate() {
29 if (ref_counter == 1 && is_mounted) {
30 f32 volume = 1.0f;
31 const auto result = vibration_handler->GetVibrationVolume(volume);
32 if (result.IsSuccess()) {
33 xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
34 // TODO: SendNotificationPattern;
35 }
36 }
37
19 if (ref_counter > 0) { 38 if (ref_counter > 0) {
20 ref_counter--; 39 ref_counter--;
21 } 40 }
@@ -23,6 +42,45 @@ Result NpadVibrationDevice::DecrementRefCounter() {
23 return ResultSuccess; 42 return ResultSuccess;
24} 43}
25 44
45Result NpadVibrationDevice::Mount(IAbstractedPad& abstracted_pad, Core::HID::DeviceIndex index,
46 NpadVibration* handler) {
47 if (!abstracted_pad.internal_flags.is_connected) {
48 return ResultSuccess;
49 }
50 xcd_handle = abstracted_pad.xcd_handle;
51 device_index = index;
52 vibration_handler = handler;
53 is_mounted = true;
54
55 if (ref_counter == 0) {
56 return ResultSuccess;
57 }
58
59 f32 volume{1.0f};
60 const auto result = vibration_handler->GetVibrationVolume(volume);
61 if (result.IsSuccess()) {
62 xcd_handle->SetVibration(false);
63 }
64
65 return ResultSuccess;
66}
67
68Result NpadVibrationDevice::Unmount() {
69 if (ref_counter == 0 || !is_mounted) {
70 is_mounted = false;
71 return ResultSuccess;
72 }
73
74 f32 volume{1.0f};
75 const auto result = vibration_handler->GetVibrationVolume(volume);
76 if (result.IsSuccess()) {
77 xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
78 }
79
80 is_mounted = false;
81 return ResultSuccess;
82}
83
26Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue& value) { 84Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue& value) {
27 if (ref_counter == 0) { 85 if (ref_counter == 0) {
28 return ResultVibrationNotInitialized; 86 return ResultVibrationNotInitialized;
@@ -37,7 +95,7 @@ Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue&
37 return result; 95 return result;
38 } 96 }
39 if (volume <= 0.0f) { 97 if (volume <= 0.0f) {
40 // TODO: SendVibrationValue 98 xcd_handle->SetVibration(device_index, Core::HID::DEFAULT_VIBRATION_VALUE);
41 return ResultSuccess; 99 return ResultSuccess;
42 } 100 }
43 101
@@ -45,7 +103,7 @@ Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue&
45 vibration_value.high_amplitude *= volume; 103 vibration_value.high_amplitude *= volume;
46 vibration_value.low_amplitude *= volume; 104 vibration_value.low_amplitude *= volume;
47 105
48 // TODO: SendVibrationValue 106 xcd_handle->SetVibration(device_index, vibration_value);
49 return ResultSuccess; 107 return ResultSuccess;
50} 108}
51 109
@@ -63,11 +121,11 @@ Result NpadVibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u3
63 pattern = 0; 121 pattern = 0;
64 } 122 }
65 123
66 // return xcd_handle->SendVibrationNotificationPattern(pattern); 124 // TODO: SendVibrationNotificationPattern;
67 return ResultSuccess; 125 return ResultSuccess;
68} 126}
69 127
70Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& out_value) { 128Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& out_value) const {
71 if (ref_counter < 1) { 129 if (ref_counter < 1) {
72 return ResultVibrationNotInitialized; 130 return ResultVibrationNotInitialized;
73 } 131 }
@@ -77,7 +135,7 @@ Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& o
77 return ResultSuccess; 135 return ResultSuccess;
78 } 136 }
79 137
80 // TODO: SendVibrationValue 138 out_value = xcd_handle->GetActualVibrationValue(device_index);
81 return ResultSuccess; 139 return ResultSuccess;
82} 140}
83 141
diff --git a/src/hid_core/resources/vibration/vibration_device.h b/src/hid_core/resources/vibration/vibration_device.h
index 3574ad60b..c2f9891d3 100644
--- a/src/hid_core/resources/vibration/vibration_device.h
+++ b/src/hid_core/resources/vibration/vibration_device.h
@@ -12,6 +12,10 @@
12#include "hid_core/resources/npad/npad_types.h" 12#include "hid_core/resources/npad/npad_types.h"
13#include "hid_core/resources/vibration/vibration_base.h" 13#include "hid_core/resources/vibration/vibration_base.h"
14 14
15namespace Core::HID {
16enum class DeviceIndex : u8;
17}
18
15namespace Service::HID { 19namespace Service::HID {
16class NpadVibration; 20class NpadVibration;
17 21
@@ -20,16 +24,20 @@ class NpadVibrationDevice final : public NpadVibrationBase {
20public: 24public:
21 explicit NpadVibrationDevice(); 25 explicit NpadVibrationDevice();
22 26
23 Result IncrementRefCounter(); 27 Result Activate();
24 Result DecrementRefCounter(); 28 Result Deactivate();
29
30 Result Mount(IAbstractedPad& abstracted_pad, Core::HID::DeviceIndex index,
31 NpadVibration* handler);
32 Result Unmount();
25 33
26 Result SendVibrationValue(const Core::HID::VibrationValue& value); 34 Result SendVibrationValue(const Core::HID::VibrationValue& value);
27 Result SendVibrationNotificationPattern(u32 pattern); 35 Result SendVibrationNotificationPattern(u32 pattern);
28 36
29 Result GetActualVibrationValue(Core::HID::VibrationValue& out_value); 37 Result GetActualVibrationValue(Core::HID::VibrationValue& out_value) const;
30 38
31private: 39private:
32 u32 device_index{}; 40 Core::HID::DeviceIndex device_index{};
33}; 41};
34 42
35} // namespace Service::HID 43} // namespace Service::HID
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index d2fbea488..d0a71a15b 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -2,6 +2,8 @@
2# SPDX-License-Identifier: GPL-2.0-or-later 2# SPDX-License-Identifier: GPL-2.0-or-later
3 3
4add_library(input_common STATIC 4add_library(input_common STATIC
5 drivers/android.cpp
6 drivers/android.h
5 drivers/camera.cpp 7 drivers/camera.cpp
6 drivers/camera.h 8 drivers/camera.h
7 drivers/keyboard.cpp 9 drivers/keyboard.cpp
diff --git a/src/input_common/drivers/android.cpp b/src/input_common/drivers/android.cpp
new file mode 100644
index 000000000..b6a03fdc0
--- /dev/null
+++ b/src/input_common/drivers/android.cpp
@@ -0,0 +1,48 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "input_common/drivers/android.h"
5
6namespace InputCommon {
7
8Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
9
10void Android::RegisterController(std::size_t controller_number) {
11 PreSetController(GetIdentifier(controller_number));
12}
13
14void Android::SetButtonState(std::size_t controller_number, int button_id, bool value) {
15 const auto identifier = GetIdentifier(controller_number);
16 SetButton(identifier, button_id, value);
17}
18
19void Android::SetAxisState(std::size_t controller_number, int axis_id, float value) {
20 const auto identifier = GetIdentifier(controller_number);
21 SetAxis(identifier, axis_id, value);
22}
23
24void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x,
25 float gyro_y, float gyro_z, float accel_x, float accel_y,
26 float accel_z) {
27 const auto identifier = GetIdentifier(controller_number);
28 const BasicMotion motion_data{
29 .gyro_x = gyro_x,
30 .gyro_y = gyro_y,
31 .gyro_z = gyro_z,
32 .accel_x = accel_x,
33 .accel_y = accel_y,
34 .accel_z = accel_z,
35 .delta_timestamp = delta_timestamp,
36 };
37 SetMotion(identifier, 0, motion_data);
38}
39
40PadIdentifier Android::GetIdentifier(std::size_t controller_number) const {
41 return {
42 .guid = Common::UUID{},
43 .port = controller_number,
44 .pad = 0,
45 };
46}
47
48} // namespace InputCommon
diff --git a/src/input_common/drivers/android.h b/src/input_common/drivers/android.h
new file mode 100644
index 000000000..3f01817f6
--- /dev/null
+++ b/src/input_common/drivers/android.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "input_common/input_engine.h"
7
8namespace InputCommon {
9
10/**
11 * A virtual controller that is always assigned to the game input
12 */
13class Android final : public InputEngine {
14public:
15 explicit Android(std::string input_engine_);
16
17 /**
18 * Registers controller number to accept new inputs
19 * @param controller_number the controller number that will take this action
20 */
21 void RegisterController(std::size_t controller_number);
22
23 /**
24 * Sets the status of all buttons bound with the key to pressed
25 * @param controller_number the controller number that will take this action
26 * @param button_id the id of the button
27 * @param value indicates if the button is pressed or not
28 */
29 void SetButtonState(std::size_t controller_number, int button_id, bool value);
30
31 /**
32 * Sets the status of a analog input to a specific player index
33 * @param controller_number the controller number that will take this action
34 * @param axis_id the id of the axis to move
35 * @param value the analog position of the axis
36 */
37 void SetAxisState(std::size_t controller_number, int axis_id, float value);
38
39 /**
40 * Sets the status of the motion sensor to a specific player index
41 * @param controller_number the controller number that will take this action
42 * @param delta_timestamp time passed since last reading
43 * @param gyro_x,gyro_y,gyro_z the gyro sensor readings
44 * @param accel_x,accel_y,accel_z the accelerometer reading
45 */
46 void SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x,
47 float gyro_y, float gyro_z, float accel_x, float accel_y, float accel_z);
48
49private:
50 /// Returns the correct identifier corresponding to the player index
51 PadIdentifier GetIdentifier(std::size_t controller_number) const;
52};
53
54} // namespace InputCommon
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
index 8c2ee4eb3..f1a1d7398 100644
--- a/src/input_common/input_mapping.cpp
+++ b/src/input_common/input_mapping.cpp
@@ -210,6 +210,9 @@ bool MappingFactory::IsDriverValid(const MappingData& data) const {
210 if (data.engine == "analog_from_button") { 210 if (data.engine == "analog_from_button") {
211 return false; 211 return false;
212 } 212 }
213 if (data.engine == "virtual_gamepad") {
214 return false;
215 }
213 return true; 216 return true;
214} 217}
215 218
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index c77fc04ee..f8749ebbf 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,6 +4,7 @@
4#include <memory> 4#include <memory>
5#include "common/input.h" 5#include "common/input.h"
6#include "common/param_package.h" 6#include "common/param_package.h"
7#include "input_common/drivers/android.h"
7#include "input_common/drivers/camera.h" 8#include "input_common/drivers/camera.h"
8#include "input_common/drivers/keyboard.h" 9#include "input_common/drivers/keyboard.h"
9#include "input_common/drivers/mouse.h" 10#include "input_common/drivers/mouse.h"
@@ -78,6 +79,7 @@ struct InputSubsystem::Impl {
78 RegisterEngine("cemuhookudp", udp_client); 79 RegisterEngine("cemuhookudp", udp_client);
79 RegisterEngine("tas", tas_input); 80 RegisterEngine("tas", tas_input);
80 RegisterEngine("camera", camera); 81 RegisterEngine("camera", camera);
82 RegisterEngine("android", android);
81 RegisterEngine("virtual_amiibo", virtual_amiibo); 83 RegisterEngine("virtual_amiibo", virtual_amiibo);
82 RegisterEngine("virtual_gamepad", virtual_gamepad); 84 RegisterEngine("virtual_gamepad", virtual_gamepad);
83#ifdef HAVE_SDL2 85#ifdef HAVE_SDL2
@@ -109,6 +111,7 @@ struct InputSubsystem::Impl {
109 UnregisterEngine(udp_client); 111 UnregisterEngine(udp_client);
110 UnregisterEngine(tas_input); 112 UnregisterEngine(tas_input);
111 UnregisterEngine(camera); 113 UnregisterEngine(camera);
114 UnregisterEngine(android);
112 UnregisterEngine(virtual_amiibo); 115 UnregisterEngine(virtual_amiibo);
113 UnregisterEngine(virtual_gamepad); 116 UnregisterEngine(virtual_gamepad);
114#ifdef HAVE_SDL2 117#ifdef HAVE_SDL2
@@ -129,6 +132,8 @@ struct InputSubsystem::Impl {
129 devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end()); 132 devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
130 auto mouse_devices = mouse->GetInputDevices(); 133 auto mouse_devices = mouse->GetInputDevices();
131 devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end()); 134 devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
135 auto android_devices = android->GetInputDevices();
136 devices.insert(devices.end(), android_devices.begin(), android_devices.end());
132#ifdef HAVE_LIBUSB 137#ifdef HAVE_LIBUSB
133 auto gcadapter_devices = gcadapter->GetInputDevices(); 138 auto gcadapter_devices = gcadapter->GetInputDevices();
134 devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end()); 139 devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
@@ -157,6 +162,9 @@ struct InputSubsystem::Impl {
157 if (engine == mouse->GetEngineName()) { 162 if (engine == mouse->GetEngineName()) {
158 return mouse; 163 return mouse;
159 } 164 }
165 if (engine == android->GetEngineName()) {
166 return android;
167 }
160#ifdef HAVE_LIBUSB 168#ifdef HAVE_LIBUSB
161 if (engine == gcadapter->GetEngineName()) { 169 if (engine == gcadapter->GetEngineName()) {
162 return gcadapter; 170 return gcadapter;
@@ -237,6 +245,9 @@ struct InputSubsystem::Impl {
237 if (engine == mouse->GetEngineName()) { 245 if (engine == mouse->GetEngineName()) {
238 return true; 246 return true;
239 } 247 }
248 if (engine == android->GetEngineName()) {
249 return true;
250 }
240#ifdef HAVE_LIBUSB 251#ifdef HAVE_LIBUSB
241 if (engine == gcadapter->GetEngineName()) { 252 if (engine == gcadapter->GetEngineName()) {
242 return true; 253 return true;
@@ -265,6 +276,7 @@ struct InputSubsystem::Impl {
265 void BeginConfiguration() { 276 void BeginConfiguration() {
266 keyboard->BeginConfiguration(); 277 keyboard->BeginConfiguration();
267 mouse->BeginConfiguration(); 278 mouse->BeginConfiguration();
279 android->BeginConfiguration();
268#ifdef HAVE_LIBUSB 280#ifdef HAVE_LIBUSB
269 gcadapter->BeginConfiguration(); 281 gcadapter->BeginConfiguration();
270#endif 282#endif
@@ -278,6 +290,7 @@ struct InputSubsystem::Impl {
278 void EndConfiguration() { 290 void EndConfiguration() {
279 keyboard->EndConfiguration(); 291 keyboard->EndConfiguration();
280 mouse->EndConfiguration(); 292 mouse->EndConfiguration();
293 android->EndConfiguration();
281#ifdef HAVE_LIBUSB 294#ifdef HAVE_LIBUSB
282 gcadapter->EndConfiguration(); 295 gcadapter->EndConfiguration();
283#endif 296#endif
@@ -308,6 +321,7 @@ struct InputSubsystem::Impl {
308 std::shared_ptr<TasInput::Tas> tas_input; 321 std::shared_ptr<TasInput::Tas> tas_input;
309 std::shared_ptr<CemuhookUDP::UDPClient> udp_client; 322 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
310 std::shared_ptr<Camera> camera; 323 std::shared_ptr<Camera> camera;
324 std::shared_ptr<Android> android;
311 std::shared_ptr<VirtualAmiibo> virtual_amiibo; 325 std::shared_ptr<VirtualAmiibo> virtual_amiibo;
312 std::shared_ptr<VirtualGamepad> virtual_gamepad; 326 std::shared_ptr<VirtualGamepad> virtual_gamepad;
313 327
@@ -373,6 +387,14 @@ const Camera* InputSubsystem::GetCamera() const {
373 return impl->camera.get(); 387 return impl->camera.get();
374} 388}
375 389
390Android* InputSubsystem::GetAndroid() {
391 return impl->android.get();
392}
393
394const Android* InputSubsystem::GetAndroid() const {
395 return impl->android.get();
396}
397
376VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() { 398VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() {
377 return impl->virtual_amiibo.get(); 399 return impl->virtual_amiibo.get();
378} 400}
diff --git a/src/input_common/main.h b/src/input_common/main.h
index d64a6cb4c..1d19019ee 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -29,6 +29,7 @@ enum Values : int;
29} 29}
30 30
31namespace InputCommon { 31namespace InputCommon {
32class Android;
32class Camera; 33class Camera;
33class Keyboard; 34class Keyboard;
34class Mouse; 35class Mouse;
@@ -103,6 +104,12 @@ public:
103 /// Retrieves the underlying camera input device. 104 /// Retrieves the underlying camera input device.
104 [[nodiscard]] const Camera* GetCamera() const; 105 [[nodiscard]] const Camera* GetCamera() const;
105 106
107 /// Retrieves the underlying android input device.
108 [[nodiscard]] Android* GetAndroid();
109
110 /// Retrieves the underlying android input device.
111 [[nodiscard]] const Android* GetAndroid() const;
112
106 /// Retrieves the underlying virtual amiibo input device. 113 /// Retrieves the underlying virtual amiibo input device.
107 [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo(); 114 [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo();
108 115
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 800754554..64a4e0e55 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -12,6 +12,11 @@ namespace Shader::Backend::SPIRV {
12namespace { 12namespace {
13class ImageOperands { 13class ImageOperands {
14public: 14public:
15 [[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false;
16 [[maybe_unused]] static constexpr bool ImageGatherOffsetAllowed = true;
17 [[maybe_unused]] static constexpr bool ImageFetchOffsetAllowed = false;
18 [[maybe_unused]] static constexpr bool ImageGradientOffsetAllowed = false;
19
15 explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp, 20 explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
16 Id lod, const IR::Value& offset) { 21 Id lod, const IR::Value& offset) {
17 if (has_bias) { 22 if (has_bias) {
@@ -22,7 +27,7 @@ public:
22 const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod}; 27 const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
23 Add(spv::ImageOperandsMask::Lod, lod_value); 28 Add(spv::ImageOperandsMask::Lod, lod_value);
24 } 29 }
25 AddOffset(ctx, offset); 30 AddOffset(ctx, offset, ImageSampleOffsetAllowed);
26 if (has_lod_clamp) { 31 if (has_lod_clamp) {
27 const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod}; 32 const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod};
28 Add(spv::ImageOperandsMask::MinLod, lod_clamp); 33 Add(spv::ImageOperandsMask::MinLod, lod_clamp);
@@ -55,20 +60,17 @@ public:
55 Add(spv::ImageOperandsMask::ConstOffsets, offsets); 60 Add(spv::ImageOperandsMask::ConstOffsets, offsets);
56 } 61 }
57 62
58 explicit ImageOperands(Id offset, Id lod, Id ms) { 63 explicit ImageOperands(Id lod, Id ms) {
59 if (Sirit::ValidId(lod)) { 64 if (Sirit::ValidId(lod)) {
60 Add(spv::ImageOperandsMask::Lod, lod); 65 Add(spv::ImageOperandsMask::Lod, lod);
61 } 66 }
62 if (Sirit::ValidId(offset)) {
63 Add(spv::ImageOperandsMask::Offset, offset);
64 }
65 if (Sirit::ValidId(ms)) { 67 if (Sirit::ValidId(ms)) {
66 Add(spv::ImageOperandsMask::Sample, ms); 68 Add(spv::ImageOperandsMask::Sample, ms);
67 } 69 }
68 } 70 }
69 71
70 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives, 72 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives,
71 u32 num_derivatives, Id offset, Id lod_clamp) { 73 u32 num_derivatives, const IR::Value& offset, Id lod_clamp) {
72 if (!Sirit::ValidId(derivatives)) { 74 if (!Sirit::ValidId(derivatives)) {
73 throw LogicError("Derivatives must be present"); 75 throw LogicError("Derivatives must be present");
74 } 76 }
@@ -83,16 +85,14 @@ public:
83 const Id derivatives_Y{ctx.OpCompositeConstruct( 85 const Id derivatives_Y{ctx.OpCompositeConstruct(
84 ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})}; 86 ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
85 Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y); 87 Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y);
86 if (Sirit::ValidId(offset)) { 88 AddOffset(ctx, offset, ImageGradientOffsetAllowed);
87 Add(spv::ImageOperandsMask::Offset, offset);
88 }
89 if (has_lod_clamp) { 89 if (has_lod_clamp) {
90 Add(spv::ImageOperandsMask::MinLod, lod_clamp); 90 Add(spv::ImageOperandsMask::MinLod, lod_clamp);
91 } 91 }
92 } 92 }
93 93
94 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2, 94 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2,
95 Id offset, Id lod_clamp) { 95 const IR::Value& offset, Id lod_clamp) {
96 if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) { 96 if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) {
97 throw LogicError("Derivatives must be present"); 97 throw LogicError("Derivatives must be present");
98 } 98 }
@@ -111,9 +111,7 @@ public:
111 const Id derivatives_id2{ctx.OpCompositeConstruct( 111 const Id derivatives_id2{ctx.OpCompositeConstruct(
112 ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})}; 112 ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})};
113 Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2); 113 Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2);
114 if (Sirit::ValidId(offset)) { 114 AddOffset(ctx, offset, ImageGradientOffsetAllowed);
115 Add(spv::ImageOperandsMask::Offset, offset);
116 }
117 if (has_lod_clamp) { 115 if (has_lod_clamp) {
118 Add(spv::ImageOperandsMask::MinLod, lod_clamp); 116 Add(spv::ImageOperandsMask::MinLod, lod_clamp);
119 } 117 }
@@ -132,7 +130,7 @@ public:
132 } 130 }
133 131
134private: 132private:
135 void AddOffset(EmitContext& ctx, const IR::Value& offset) { 133 void AddOffset(EmitContext& ctx, const IR::Value& offset, bool runtime_offset_allowed) {
136 if (offset.IsEmpty()) { 134 if (offset.IsEmpty()) {
137 return; 135 return;
138 } 136 }
@@ -165,7 +163,9 @@ private:
165 break; 163 break;
166 } 164 }
167 } 165 }
168 Add(spv::ImageOperandsMask::Offset, ctx.Def(offset)); 166 if (runtime_offset_allowed) {
167 Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
168 }
169 } 169 }
170 170
171 void Add(spv::ImageOperandsMask new_mask, Id value) { 171 void Add(spv::ImageOperandsMask new_mask, Id value) {
@@ -311,6 +311,37 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info,
311 return coords; 311 return coords;
312 } 312 }
313} 313}
314
315void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords,
316 Id offset) {
317 if (!Sirit::ValidId(offset)) {
318 return;
319 }
320
321 Id result_type{};
322 switch (info.type) {
323 case TextureType::Buffer:
324 case TextureType::Color1D:
325 case TextureType::ColorArray1D: {
326 result_type = ctx.U32[1];
327 break;
328 }
329 case TextureType::Color2D:
330 case TextureType::Color2DRect:
331 case TextureType::ColorArray2D: {
332 result_type = ctx.U32[2];
333 break;
334 }
335 case TextureType::Color3D: {
336 result_type = ctx.U32[3];
337 break;
338 }
339 case TextureType::ColorCube:
340 case TextureType::ColorArrayCube:
341 return;
342 }
343 coords = ctx.OpIAdd(result_type, coords, offset);
344}
314} // Anonymous namespace 345} // Anonymous namespace
315 346
316Id EmitBindlessImageSampleImplicitLod(EmitContext&) { 347Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@@ -496,6 +527,7 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
496Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, 527Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
497 Id lod, Id ms) { 528 Id lod, Id ms) {
498 const auto info{inst->Flags<IR::TextureInstInfo>()}; 529 const auto info{inst->Flags<IR::TextureInstInfo>()};
530 AddOffsetToCoordinates(ctx, info, coords, offset);
499 if (info.type == TextureType::Buffer) { 531 if (info.type == TextureType::Buffer) {
500 lod = Id{}; 532 lod = Id{};
501 } 533 }
@@ -503,7 +535,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
503 // This image is multisampled, lod must be implicit 535 // This image is multisampled, lod must be implicit
504 lod = Id{}; 536 lod = Id{};
505 } 537 }
506 const ImageOperands operands(offset, lod, ms); 538 const ImageOperands operands(lod, ms);
507 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], 539 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
508 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); 540 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
509} 541}
@@ -548,13 +580,13 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
548} 580}
549 581
550Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 582Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
551 Id derivatives, Id offset, Id lod_clamp) { 583 Id derivatives, const IR::Value& offset, Id lod_clamp) {
552 const auto info{inst->Flags<IR::TextureInstInfo>()}; 584 const auto info{inst->Flags<IR::TextureInstInfo>()};
553 const auto operands = 585 const auto operands = info.num_derivatives == 3
554 info.num_derivatives == 3 586 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
555 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, offset, {}, lod_clamp) 587 ctx.Def(offset), {}, lod_clamp)
556 : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset, 588 : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
557 lod_clamp); 589 info.num_derivatives, offset, lod_clamp);
558 return Emit(&EmitContext::OpImageSparseSampleExplicitLod, 590 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
559 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], 591 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
560 Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); 592 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 7d34575c8..5c01b1012 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -543,7 +543,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
543 const IR::Value& skip_mips); 543 const IR::Value& skip_mips);
544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
546 Id derivatives, Id offset, Id lod_clamp); 546 Id derivatives, const IR::Value& offset, Id lod_clamp);
547Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 547Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
548void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); 548void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
549Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index); 549Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 90278052a..93b03b917 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -351,7 +351,7 @@ if (APPLE)
351 351
352 if (NOT USE_SYSTEM_MOLTENVK) 352 if (NOT USE_SYSTEM_MOLTENVK)
353 set(MOLTENVK_PLATFORM "macOS") 353 set(MOLTENVK_PLATFORM "macOS")
354 set(MOLTENVK_VERSION "v1.2.5") 354 set(MOLTENVK_VERSION "v1.2.7")
355 download_moltenvk_external(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION}) 355 download_moltenvk_external(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
356 endif() 356 endif()
357 find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED) 357 find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 5dac9f1e7..400917f9d 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -1650,9 +1650,21 @@ void ConfigureInputPlayer::SaveProfile() {
1650void ConfigureInputPlayer::UpdateInputProfiles() { 1650void ConfigureInputPlayer::UpdateInputProfiles() {
1651 ui->comboProfiles->clear(); 1651 ui->comboProfiles->clear();
1652 1652
1653 for (const auto& profile_name : profiles->GetInputProfileNames()) { 1653 // Set current profile as empty by default
1654 int profile_index = -1;
1655
1656 // Add every available profile and search the player profile to set it as current one
1657 auto& current_profile = Settings::values.players.GetValue()[player_index].profile_name;
1658 std::vector<std::string> profile_names = profiles->GetInputProfileNames();
1659 std::string profile_name;
1660 for (size_t i = 0; i < profile_names.size(); i++) {
1661 profile_name = profile_names[i];
1654 ui->comboProfiles->addItem(QString::fromStdString(profile_name)); 1662 ui->comboProfiles->addItem(QString::fromStdString(profile_name));
1663 if (current_profile == profile_name) {
1664 profile_index = (int)i;
1665 }
1655 } 1666 }
1656 1667
1657 ui->comboProfiles->setCurrentIndex(-1); 1668 LOG_DEBUG(Frontend, "Setting the current input profile to index {}", profile_index);
1669 ui->comboProfiles->setCurrentIndex(profile_index);
1658} 1670}
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 140a7fe5d..568775027 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -122,9 +122,8 @@ void ConfigurePerGameAddons::LoadConfiguration() {
122 122
123 const auto& disabled = Settings::values.disabled_addons[title_id]; 123 const auto& disabled = Settings::values.disabled_addons[title_id];
124 124
125 for (const auto& patch : pm.GetPatchVersionNames(update_raw)) { 125 for (const auto& patch : pm.GetPatches(update_raw)) {
126 const auto name = 126 const auto name = QString::fromStdString(patch.name);
127 QString::fromStdString(patch.first).replace(QStringLiteral("[D] "), QString{});
128 127
129 auto* const first_item = new QStandardItem; 128 auto* const first_item = new QStandardItem;
130 first_item->setText(name); 129 first_item->setText(name);
@@ -136,7 +135,7 @@ void ConfigurePerGameAddons::LoadConfiguration() {
136 first_item->setCheckState(patch_disabled ? Qt::Unchecked : Qt::Checked); 135 first_item->setCheckState(patch_disabled ? Qt::Unchecked : Qt::Checked);
137 136
138 list_items.push_back(QList<QStandardItem*>{ 137 list_items.push_back(QList<QStandardItem*>{
139 first_item, new QStandardItem{QString::fromStdString(patch.second)}}); 138 first_item, new QStandardItem{QString::fromStdString(patch.version)}});
140 item_model->appendRow(list_items.back()); 139 item_model->appendRow(list_items.back());
141 } 140 }
142 141
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
index d898d8acc..6b1f4527b 100644
--- a/src/yuzu/configuration/configure_vibration.cpp
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -116,8 +116,8 @@ void ConfigureVibration::VibrateController(Core::HID::ControllerTriggerType type
116 .high_amplitude = 1.0f, 116 .high_amplitude = 1.0f,
117 .high_frequency = 320.0f, 117 .high_frequency = 320.0f,
118 }; 118 };
119 controller->SetVibration(0, vibration); 119 controller->SetVibration(Core::HID::DeviceIndex::Left, vibration);
120 controller->SetVibration(1, vibration); 120 controller->SetVibration(Core::HID::DeviceIndex::Right, vibration);
121 121
122 // Restore previous values 122 // Restore previous values
123 player.vibration_enabled = old_vibration_enabled; 123 player.vibration_enabled = old_vibration_enabled;
@@ -127,7 +127,7 @@ void ConfigureVibration::VibrateController(Core::HID::ControllerTriggerType type
127void ConfigureVibration::StopVibrations() { 127void ConfigureVibration::StopVibrations() {
128 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { 128 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
129 auto controller = hid_core.GetEmulatedControllerByIndex(i); 129 auto controller = hid_core.GetEmulatedControllerByIndex(i);
130 controller->SetVibration(0, Core::HID::DEFAULT_VIBRATION_VALUE); 130 controller->SetVibration(Core::HID::DeviceIndex::Left, Core::HID::DEFAULT_VIBRATION_VALUE);
131 controller->SetVibration(1, Core::HID::DEFAULT_VIBRATION_VALUE); 131 controller->SetVibration(Core::HID::DeviceIndex::Right, Core::HID::DEFAULT_VIBRATION_VALUE);
132 } 132 }
133} 133}
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 716efbccd..ebebadc94 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/fs/fs.h" 6#include "common/fs/fs.h"
7#include "common/fs/path_util.h" 7#include "common/fs/path_util.h"
8#include "common/logging/log.h"
8#include "frontend_common/config.h" 9#include "frontend_common/config.h"
9#include "yuzu/configuration/input_profiles.h" 10#include "yuzu/configuration/input_profiles.h"
10 11
@@ -113,6 +114,8 @@ bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t pla
113 return false; 114 return false;
114 } 115 }
115 116
117 LOG_INFO(Config, "Loading input profile `{}`", profile_name);
118
116 map_profiles[profile_name]->ReadQtControlPlayerValues(player_index); 119 map_profiles[profile_name]->ReadQtControlPlayerValues(player_index);
117 return true; 120 return true;
118} 121}
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
index 6aca71d7c..1051031f2 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/logging/log.h"
4#include "input_common/main.h" 5#include "input_common/main.h"
5#include "qt_config.h" 6#include "qt_config.h"
6#include "uisettings.h" 7#include "uisettings.h"
@@ -65,7 +66,7 @@ void QtConfig::ReloadAllValues() {
65} 66}
66 67
67void QtConfig::SaveAllValues() { 68void QtConfig::SaveAllValues() {
68 Save(); 69 SaveValues();
69 SaveQtValues(); 70 SaveQtValues();
70} 71}
71 72
@@ -327,7 +328,10 @@ void QtConfig::ReadMultiplayerValues() {
327 328
328void QtConfig::SaveQtValues() { 329void QtConfig::SaveQtValues() {
329 if (global) { 330 if (global) {
331 LOG_DEBUG(Config, "Saving global Qt configuration values");
330 SaveUIValues(); 332 SaveUIValues();
333 } else {
334 LOG_DEBUG(Config, "Saving Qt configuration values");
331 } 335 }
332 SaveQtControlValues(); 336 SaveQtControlValues();
333 337
@@ -545,6 +549,7 @@ void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) {
545void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) { 549void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) {
546 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); 550 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
547 551
552 LOG_DEBUG(Config, "Saving players control configuration values");
548 SavePlayerValues(player_index); 553 SavePlayerValues(player_index);
549 SaveQtPlayerValues(player_index); 554 SaveQtPlayerValues(player_index);
550 555
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index dc006832e..9747e3fb3 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -164,18 +164,19 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
164 QString out; 164 QString out;
165 FileSys::VirtualFile update_raw; 165 FileSys::VirtualFile update_raw;
166 loader.ReadUpdateRaw(update_raw); 166 loader.ReadUpdateRaw(update_raw);
167 for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { 167 for (const auto& patch : patch_manager.GetPatches(update_raw)) {
168 const bool is_update = kv.first == "Update" || kv.first == "[D] Update"; 168 const bool is_update = patch.name == "Update";
169 if (!updatable && is_update) { 169 if (!updatable && is_update) {
170 continue; 170 continue;
171 } 171 }
172 172
173 const QString type = QString::fromStdString(kv.first); 173 const QString type =
174 QString::fromStdString(patch.enabled ? patch.name : "[D] " + patch.name);
174 175
175 if (kv.second.empty()) { 176 if (patch.version.empty()) {
176 out.append(QStringLiteral("%1\n").arg(type)); 177 out.append(QStringLiteral("%1\n").arg(type));
177 } else { 178 } else {
178 auto ver = kv.second; 179 auto ver = patch.version;
179 180
180 // Display container name for packed updates 181 // Display container name for packed updates
181 if (is_update && ver == "PACKED") { 182 if (is_update && ver == "PACKED") {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 33756febf..05bd4174c 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -47,6 +47,7 @@
47#include "core/hle/service/am/applet_oe.h" 47#include "core/hle/service/am/applet_oe.h"
48#include "core/hle/service/am/applets/applets.h" 48#include "core/hle/service/am/applets/applets.h"
49#include "core/hle/service/set/system_settings_server.h" 49#include "core/hle/service/set/system_settings_server.h"
50#include "frontend_common/content_manager.h"
50#include "hid_core/frontend/emulated_controller.h" 51#include "hid_core/frontend/emulated_controller.h"
51#include "hid_core/hid_core.h" 52#include "hid_core/hid_core.h"
52#include "yuzu/multiplayer/state.h" 53#include "yuzu/multiplayer/state.h"
@@ -518,12 +519,21 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
518 continue; 519 continue;
519 } 520 }
520 521
522 int user_arg_idx = ++i;
521 bool argument_ok; 523 bool argument_ok;
522 const std::size_t selected_user = args[++i].toUInt(&argument_ok); 524 std::size_t selected_user = args[user_arg_idx].toUInt(&argument_ok);
523 525
524 if (!argument_ok) { 526 if (!argument_ok) {
525 LOG_ERROR(Frontend, "Invalid user argument"); 527 // try to look it up by username, only finds the first username that matches.
526 continue; 528 const std::string user_arg_str = args[user_arg_idx].toStdString();
529 const auto user_idx = system->GetProfileManager().GetUserIndex(user_arg_str);
530
531 if (user_idx == std::nullopt) {
532 LOG_ERROR(Frontend, "Invalid user argument");
533 continue;
534 }
535
536 selected_user = user_idx.value();
527 } 537 }
528 538
529 if (!system->GetProfileManager().UserExistsIndex(selected_user)) { 539 if (!system->GetProfileManager().UserExistsIndex(selected_user)) {
@@ -532,6 +542,8 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
532 } 542 }
533 543
534 Settings::values.current_user = static_cast<s32>(selected_user); 544 Settings::values.current_user = static_cast<s32>(selected_user);
545
546 user_flag_cmd_line = true;
535 continue; 547 continue;
536 } 548 }
537 549
@@ -1942,7 +1954,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1942 1954
1943 Settings::LogSettings(); 1955 Settings::LogSettings();
1944 1956
1945 if (UISettings::values.select_user_on_boot) { 1957 if (UISettings::values.select_user_on_boot && !user_flag_cmd_line) {
1946 const Core::Frontend::ProfileSelectParameters parameters{ 1958 const Core::Frontend::ProfileSelectParameters parameters{
1947 .mode = Service::AM::Applets::UiMode::UserSelector, 1959 .mode = Service::AM::Applets::UiMode::UserSelector,
1948 .invalid_uid_list = {}, 1960 .invalid_uid_list = {},
@@ -1954,6 +1966,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1954 } 1966 }
1955 } 1967 }
1956 1968
1969 // If the user specifies -u (successfully) on the cmd line, don't prompt for a user on first
1970 // game startup only. If the user stops emulation and starts a new one, go back to the expected
1971 // behavior of asking.
1972 user_flag_cmd_line = false;
1973
1957 if (!LoadROM(filename, program_id, program_index, launch_type)) { 1974 if (!LoadROM(filename, program_id, program_index, launch_type)) {
1958 return; 1975 return;
1959 } 1976 }
@@ -2460,10 +2477,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
2460} 2477}
2461 2478
2462void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) { 2479void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) {
2463 const auto& fs_controller = system->GetFileSystemController(); 2480 const auto res =
2464 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || 2481 ContentManager::RemoveBaseContent(system->GetFileSystemController(), program_id);
2465 fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
2466
2467 if (res) { 2482 if (res) {
2468 QMessageBox::information(this, tr("Successfully Removed"), 2483 QMessageBox::information(this, tr("Successfully Removed"),
2469 tr("Successfully removed the installed base game.")); 2484 tr("Successfully removed the installed base game."));
@@ -2475,11 +2490,7 @@ void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) {
2475} 2490}
2476 2491
2477void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) { 2492void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
2478 const auto update_id = program_id | 0x800; 2493 const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
2479 const auto& fs_controller = system->GetFileSystemController();
2480 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
2481 fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
2482
2483 if (res) { 2494 if (res) {
2484 QMessageBox::information(this, tr("Successfully Removed"), 2495 QMessageBox::information(this, tr("Successfully Removed"),
2485 tr("Successfully removed the installed update.")); 2496 tr("Successfully removed the installed update."));
@@ -2490,22 +2501,7 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
2490} 2501}
2491 2502
2492void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) { 2503void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
2493 u32 count{}; 2504 const size_t count = ContentManager::RemoveAllDLC(system.get(), program_id);
2494 const auto& fs_controller = system->GetFileSystemController();
2495 const auto dlc_entries = system->GetContentProvider().ListEntriesFilter(
2496 FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
2497
2498 for (const auto& entry : dlc_entries) {
2499 if (FileSys::GetBaseTitleID(entry.title_id) == program_id) {
2500 const auto res =
2501 fs_controller.GetUserNANDContents()->RemoveExistingEntry(entry.title_id) ||
2502 fs_controller.GetSDMCContents()->RemoveExistingEntry(entry.title_id);
2503 if (res) {
2504 ++count;
2505 }
2506 }
2507 }
2508
2509 if (count == 0) { 2505 if (count == 0) {
2510 QMessageBox::warning(this, GetGameListErrorRemoving(type), 2506 QMessageBox::warning(this, GetGameListErrorRemoving(type),
2511 tr("There are no DLC installed for this title.")); 2507 tr("There are no DLC installed for this title."));
@@ -3274,12 +3270,21 @@ void GMainWindow::OnMenuInstallToNAND() {
3274 install_progress->setLabelText( 3270 install_progress->setLabelText(
3275 tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName())); 3271 tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName()));
3276 3272
3277 QFuture<InstallResult> future; 3273 QFuture<ContentManager::InstallResult> future;
3278 InstallResult result; 3274 ContentManager::InstallResult result;
3279 3275
3280 if (file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { 3276 if (file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
3281 3277 const auto progress_callback = [this](size_t size, size_t progress) {
3282 future = QtConcurrent::run([this, &file] { return InstallNSP(file); }); 3278 emit UpdateInstallProgress();
3279 if (install_progress->wasCanceled()) {
3280 return true;
3281 }
3282 return false;
3283 };
3284 future = QtConcurrent::run([this, &file, progress_callback] {
3285 return ContentManager::InstallNSP(system.get(), vfs.get(), file.toStdString(),
3286 progress_callback);
3287 });
3283 3288
3284 while (!future.isFinished()) { 3289 while (!future.isFinished()) {
3285 QCoreApplication::processEvents(); 3290 QCoreApplication::processEvents();
@@ -3295,16 +3300,16 @@ void GMainWindow::OnMenuInstallToNAND() {
3295 std::this_thread::sleep_for(std::chrono::milliseconds(10)); 3300 std::this_thread::sleep_for(std::chrono::milliseconds(10));
3296 3301
3297 switch (result) { 3302 switch (result) {
3298 case InstallResult::Success: 3303 case ContentManager::InstallResult::Success:
3299 new_files.append(QFileInfo(file).fileName()); 3304 new_files.append(QFileInfo(file).fileName());
3300 break; 3305 break;
3301 case InstallResult::Overwrite: 3306 case ContentManager::InstallResult::Overwrite:
3302 overwritten_files.append(QFileInfo(file).fileName()); 3307 overwritten_files.append(QFileInfo(file).fileName());
3303 break; 3308 break;
3304 case InstallResult::Failure: 3309 case ContentManager::InstallResult::Failure:
3305 failed_files.append(QFileInfo(file).fileName()); 3310 failed_files.append(QFileInfo(file).fileName());
3306 break; 3311 break;
3307 case InstallResult::BaseInstallAttempted: 3312 case ContentManager::InstallResult::BaseInstallAttempted:
3308 failed_files.append(QFileInfo(file).fileName()); 3313 failed_files.append(QFileInfo(file).fileName());
3309 detected_base_install = true; 3314 detected_base_install = true;
3310 break; 3315 break;
@@ -3338,96 +3343,7 @@ void GMainWindow::OnMenuInstallToNAND() {
3338 ui->action_Install_File_NAND->setEnabled(true); 3343 ui->action_Install_File_NAND->setEnabled(true);
3339} 3344}
3340 3345
3341InstallResult GMainWindow::InstallNSP(const QString& filename) { 3346ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) {
3342 const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
3343 const FileSys::VirtualFile& dest, std::size_t block_size) {
3344 if (src == nullptr || dest == nullptr) {
3345 return false;
3346 }
3347 if (!dest->Resize(src->GetSize())) {
3348 return false;
3349 }
3350
3351 std::vector<u8> buffer(CopyBufferSize);
3352
3353 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
3354 if (install_progress->wasCanceled()) {
3355 dest->Resize(0);
3356 return false;
3357 }
3358
3359 emit UpdateInstallProgress();
3360
3361 const auto read = src->Read(buffer.data(), buffer.size(), i);
3362 dest->Write(buffer.data(), read, i);
3363 }
3364 return true;
3365 };
3366
3367 std::shared_ptr<FileSys::NSP> nsp;
3368 if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
3369 nsp = std::make_shared<FileSys::NSP>(
3370 vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
3371 if (nsp->IsExtractedType()) {
3372 return InstallResult::Failure;
3373 }
3374 } else {
3375 return InstallResult::Failure;
3376 }
3377
3378 if (nsp->GetStatus() != Loader::ResultStatus::Success) {
3379 return InstallResult::Failure;
3380 }
3381 const auto res = system->GetFileSystemController().GetUserNANDContents()->InstallEntry(
3382 *nsp, true, qt_raw_copy);
3383 switch (res) {
3384 case FileSys::InstallResult::Success:
3385 return InstallResult::Success;
3386 case FileSys::InstallResult::OverwriteExisting:
3387 return InstallResult::Overwrite;
3388 case FileSys::InstallResult::ErrorBaseInstall:
3389 return InstallResult::BaseInstallAttempted;
3390 default:
3391 return InstallResult::Failure;
3392 }
3393}
3394
3395InstallResult GMainWindow::InstallNCA(const QString& filename) {
3396 const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
3397 const FileSys::VirtualFile& dest, std::size_t block_size) {
3398 if (src == nullptr || dest == nullptr) {
3399 return false;
3400 }
3401 if (!dest->Resize(src->GetSize())) {
3402 return false;
3403 }
3404
3405 std::vector<u8> buffer(CopyBufferSize);
3406
3407 for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
3408 if (install_progress->wasCanceled()) {
3409 dest->Resize(0);
3410 return false;
3411 }
3412
3413 emit UpdateInstallProgress();
3414
3415 const auto read = src->Read(buffer.data(), buffer.size(), i);
3416 dest->Write(buffer.data(), read, i);
3417 }
3418 return true;
3419 };
3420
3421 const auto nca =
3422 std::make_shared<FileSys::NCA>(vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
3423 const auto id = nca->GetStatus();
3424
3425 // Game updates necessary are missing base RomFS
3426 if (id != Loader::ResultStatus::Success &&
3427 id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
3428 return InstallResult::Failure;
3429 }
3430
3431 const QStringList tt_options{tr("System Application"), 3347 const QStringList tt_options{tr("System Application"),
3432 tr("System Archive"), 3348 tr("System Archive"),
3433 tr("System Application Update"), 3349 tr("System Application Update"),
@@ -3448,7 +3364,7 @@ InstallResult GMainWindow::InstallNCA(const QString& filename) {
3448 if (!ok || index == -1) { 3364 if (!ok || index == -1) {
3449 QMessageBox::warning(this, tr("Failed to Install"), 3365 QMessageBox::warning(this, tr("Failed to Install"),
3450 tr("The title type you selected for the NCA is invalid.")); 3366 tr("The title type you selected for the NCA is invalid."));
3451 return InstallResult::Failure; 3367 return ContentManager::InstallResult::Failure;
3452 } 3368 }
3453 3369
3454 // If index is equal to or past Game, add the jump in TitleType. 3370 // If index is equal to or past Game, add the jump in TitleType.
@@ -3462,15 +3378,15 @@ InstallResult GMainWindow::InstallNCA(const QString& filename) {
3462 auto* registered_cache = is_application ? fs_controller.GetUserNANDContents() 3378 auto* registered_cache = is_application ? fs_controller.GetUserNANDContents()
3463 : fs_controller.GetSystemNANDContents(); 3379 : fs_controller.GetSystemNANDContents();
3464 3380
3465 const auto res = registered_cache->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), 3381 const auto progress_callback = [this](size_t size, size_t progress) {
3466 true, qt_raw_copy); 3382 emit UpdateInstallProgress();
3467 if (res == FileSys::InstallResult::Success) { 3383 if (install_progress->wasCanceled()) {
3468 return InstallResult::Success; 3384 return true;
3469 } else if (res == FileSys::InstallResult::OverwriteExisting) { 3385 }
3470 return InstallResult::Overwrite; 3386 return false;
3471 } else { 3387 };
3472 return InstallResult::Failure; 3388 return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache,
3473 } 3389 static_cast<FileSys::TitleType>(index), progress_callback);
3474} 3390}
3475 3391
3476void GMainWindow::OnMenuRecentFile() { 3392void GMainWindow::OnMenuRecentFile() {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 366e806d5..280fae5c3 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -16,6 +16,7 @@
16#include "common/announce_multiplayer_room.h" 16#include "common/announce_multiplayer_room.h"
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "configuration/qt_config.h" 18#include "configuration/qt_config.h"
19#include "frontend_common/content_manager.h"
19#include "input_common/drivers/tas_input.h" 20#include "input_common/drivers/tas_input.h"
20#include "yuzu/compatibility_list.h" 21#include "yuzu/compatibility_list.h"
21#include "yuzu/hotkeys.h" 22#include "yuzu/hotkeys.h"
@@ -124,13 +125,6 @@ enum class EmulatedDirectoryTarget {
124 SDMC, 125 SDMC,
125}; 126};
126 127
127enum class InstallResult {
128 Success,
129 Overwrite,
130 Failure,
131 BaseInstallAttempted,
132};
133
134enum class ReinitializeKeyBehavior { 128enum class ReinitializeKeyBehavior {
135 NoWarning, 129 NoWarning,
136 Warning, 130 Warning,
@@ -427,8 +421,7 @@ private:
427 void RemoveCacheStorage(u64 program_id); 421 void RemoveCacheStorage(u64 program_id);
428 bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, 422 bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id,
429 u64* selected_title_id, u8* selected_content_record_type); 423 u64* selected_title_id, u8* selected_content_record_type);
430 InstallResult InstallNSP(const QString& filename); 424 ContentManager::InstallResult InstallNCA(const QString& filename);
431 InstallResult InstallNCA(const QString& filename);
432 void MigrateConfigFiles(); 425 void MigrateConfigFiles();
433 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, 426 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
434 std::string_view gpu_vendor = {}); 427 std::string_view gpu_vendor = {});
@@ -523,6 +516,8 @@ private:
523 std::unique_ptr<EmuThread> emu_thread; 516 std::unique_ptr<EmuThread> emu_thread;
524 // The path to the game currently running 517 // The path to the game currently running
525 QString current_game_path; 518 QString current_game_path;
519 // Whether a user was set on the command line (skips UserSelector if it's forced to show up)
520 bool user_flag_cmd_line = false;
526 521
527 bool auto_paused = false; 522 bool auto_paused = false;
528 bool auto_muted = false; 523 bool auto_muted = false;
diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp
index e81bf5d45..995114510 100644
--- a/src/yuzu_cmd/sdl_config.cpp
+++ b/src/yuzu_cmd/sdl_config.cpp
@@ -5,6 +5,7 @@
5#define SDL_MAIN_HANDLED 5#define SDL_MAIN_HANDLED
6#include <SDL.h> 6#include <SDL.h>
7 7
8#include "common/logging/log.h"
8#include "input_common/main.h" 9#include "input_common/main.h"
9#include "sdl_config.h" 10#include "sdl_config.h"
10 11
@@ -64,7 +65,7 @@ void SdlConfig::ReloadAllValues() {
64} 65}
65 66
66void SdlConfig::SaveAllValues() { 67void SdlConfig::SaveAllValues() {
67 Save(); 68 SaveValues();
68 SaveSdlValues(); 69 SaveSdlValues();
69} 70}
70 71
@@ -177,6 +178,7 @@ void SdlConfig::ReadHidbusValues() {
177} 178}
178 179
179void SdlConfig::SaveSdlValues() { 180void SdlConfig::SaveSdlValues() {
181 LOG_DEBUG(Config, "Saving SDL configuration values");
180 SaveSdlControlValues(); 182 SaveSdlControlValues();
181 183
182 WriteToIni(); 184 WriteToIni();