summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/android/app/build.gradle.kts1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt76
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt24
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt34
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt42
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt72
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt128
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameDir.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt61
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt126
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt39
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt44
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt11
-rw-r--r--src/android/app/src/main/jni/android_config.cpp120
-rw-r--r--src/android/app/src/main/jni/android_config.h41
-rw-r--r--src/android/app/src/main/jni/android_settings.cpp (renamed from src/android/app/src/main/jni/uisettings.cpp)2
-rw-r--r--src/android/app/src/main/jni/android_settings.h (renamed from src/android/app/src/main/jni/uisettings.h)8
-rw-r--r--src/android/app/src/main/jni/config.cpp330
-rw-r--r--src/android/app/src/main/jni/config.h47
-rw-r--r--src/android/app/src/main/jni/default_ini.h511
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp16
-rw-r--r--src/android/app/src/main/jni/id_cache.h2
-rw-r--r--src/android/app/src/main/jni/native.cpp26
-rw-r--r--src/android/app/src/main/jni/native_config.cpp75
-rw-r--r--src/android/app/src/main/res/layout/card_folder.xml70
-rw-r--r--src/android/app/src/main/res/layout/dialog_add_folder.xml45
-rw-r--r--src/android/app/src/main/res/layout/dialog_folder_properties.xml30
-rw-r--r--src/android/app/src/main/res/layout/fragment_folders.xml48
-rw-r--r--src/android/app/src/main/res/layout/fragment_search.xml1
-rw-r--r--src/android/app/src/main/res/navigation/home_navigation.xml7
-rw-r--r--src/android/app/src/main/res/values/arrays.xml18
-rw-r--r--src/android/app/src/main/res/values/dimens.xml2
-rw-r--r--src/android/app/src/main/res/values/strings.xml14
-rw-r--r--src/common/CMakeLists.txt19
-rw-r--r--src/common/free_region_manager.h55
-rw-r--r--src/common/host_memory.cpp219
-rw-r--r--src/common/host_memory.h15
-rw-r--r--src/common/linux/gamemode.cpp40
-rw-r--r--src/common/linux/gamemode.h24
-rw-r--r--src/common/settings.cpp23
-rw-r--r--src/common/settings.h58
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/common/signal_chain.cpp42
-rw-r--r--src/common/signal_chain.h19
-rw-r--r--src/common/wall_clock.cpp4
-rw-r--r--src/core/CMakeLists.txt25
-rw-r--r--src/core/arm/arm_interface.cpp2
-rw-r--r--src/core/arm/arm_interface.h3
-rw-r--r--src/core/arm/nce/arm_nce.cpp400
-rw-r--r--src/core/arm/nce/arm_nce.h108
-rw-r--r--src/core/arm/nce/arm_nce.s222
-rw-r--r--src/core/arm/nce/arm_nce_asm_definitions.h29
-rw-r--r--src/core/arm/nce/guest_context.h50
-rw-r--r--src/core/arm/nce/instructions.h147
-rw-r--r--src/core/arm/nce/patcher.cpp474
-rw-r--r--src/core/arm/nce/patcher.h98
-rw-r--r--src/core/cpu_manager.cpp2
-rw-r--r--src/core/device_memory.cpp3
-rw-r--r--src/core/file_sys/patch_manager.cpp4
-rw-r--r--src/core/file_sys/romfs.cpp19
-rw-r--r--src/core/file_sys/romfs_factory.cpp2
-rw-r--r--src/core/frontend/emu_window.h10
-rw-r--r--src/core/hid/emulated_controller.cpp67
-rw-r--r--src/core/hid/emulated_controller.h1
-rw-r--r--src/core/hid/hid_core.cpp5
-rw-r--r--src/core/hid/hid_types.h87
-rw-r--r--src/core/hid/input_interpreter.cpp4
-rw-r--r--src/core/hid/input_interpreter.h4
-rw-r--r--src/core/hle/kernel/code_set.h14
-rw-r--r--src/core/hle/kernel/k_address_space_info.cpp4
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp33
-rw-r--r--src/core/hle/kernel/k_page_table_base.h3
-rw-r--r--src/core/hle/kernel/k_process.cpp23
-rw-r--r--src/core/hle/kernel/k_process.h14
-rw-r--r--src/core/hle/kernel/k_process_page_table.h9
-rw-r--r--src/core/hle/kernel/k_thread.h16
-rw-r--r--src/core/hle/kernel/physical_core.cpp14
-rw-r--r--src/core/hle/service/am/am.cpp91
-rw-r--r--src/core/hle/service/am/am.h3
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp5
-rw-r--r--src/core/hle/service/am/applets/applet_controller.h2
-rw-r--r--src/core/hle/service/btm/btm.cpp56
-rw-r--r--src/core/hle/service/friend/friend.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.cpp42
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.h43
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp10
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h6
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp54
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h6
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp10
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h6
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h6
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp576
-rw-r--r--src/core/hle/service/hid/controllers/npad.h198
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp86
-rw-r--r--src/core/hle/service/hid/controllers/palma.h8
-rw-r--r--src/core/hle/service/hid/controllers/seven_six_axis.cpp (renamed from src/core/hle/service/hid/controllers/console_sixaxis.cpp)38
-rw-r--r--src/core/hle/service/hid/controllers/seven_six_axis.h (renamed from src/core/hle/service/hid/controllers/console_sixaxis.h)31
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.cpp413
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.h111
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp23
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h10
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h6
-rw-r--r--src/core/hle/service/hid/hid_server.cpp459
-rw-r--r--src/core/hle/service/hid/hid_server.h1
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp647
-rw-r--r--src/core/hle/service/hid/hid_system_server.h23
-rw-r--r--src/core/hle/service/hid/hid_util.h146
-rw-r--r--src/core/hle/service/hid/irs.cpp5
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp155
-rw-r--r--src/core/hle/service/hid/resource_manager.h119
-rw-r--r--src/core/hle/service/ldn/ldn.cpp10
-rw-r--r--src/core/hle/service/mii/types/ver3_store_data.cpp2
-rw-r--r--src/core/hle/service/nfc/common/device.cpp70
-rw-r--r--src/core/hle/service/nfc/common/device.h1
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp3
-rw-r--r--src/core/hle/service/set/set_sys.cpp100
-rw-r--r--src/core/hle/service/set/set_sys.h36
-rw-r--r--src/core/hle/service/time/clock_types.h5
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp63
-rw-r--r--src/core/loader/kip.cpp3
-rw-r--r--src/core/loader/nca.cpp6
-rw-r--r--src/core/loader/nro.cpp63
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp67
-rw-r--r--src/core/loader/nso.h7
-rw-r--r--src/core/memory.cpp71
-rw-r--r--src/core/memory.h18
-rw-r--r--src/core/memory/cheat_engine.cpp5
-rw-r--r--src/frontend_common/CMakeLists.txt10
-rw-r--r--src/frontend_common/config.cpp1010
-rw-r--r--src/frontend_common/config.h211
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp9
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp10
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp2
-rw-r--r--src/shader_recompiler/host_translate_info.h1
-rw-r--r--src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp13
-rw-r--r--src/shader_recompiler/ir_opt/passes.h2
-rw-r--r--src/shader_recompiler/profile.h3
-rw-r--r--src/tests/common/host_memory.cpp71
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h117
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h4
-rw-r--r--src/video_core/buffer_cache/usage_tracker.h79
-rw-r--r--src/video_core/host1x/codecs/codec.cpp329
-rw-r--r--src/video_core/host1x/codecs/codec.h39
-rw-r--r--src/video_core/host1x/codecs/h264.cpp4
-rw-r--r--src/video_core/host1x/codecs/h264.h1
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.cpp419
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.h213
-rw-r--r--src/video_core/host1x/nvdec.cpp2
-rw-r--r--src/video_core/host1x/nvdec.h2
-rw-r--r--src/video_core/host1x/vic.cpp62
-rw-r--r--src/video_core/host1x/vic.h4
-rw-r--r--src/video_core/query_cache/query_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h27
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp18
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_staging_buffer_pool.cpp66
-rw-r--r--src/video_core/renderer_opengl/gl_staging_buffer_pool.h22
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp40
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h23
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp29
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp26
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp28
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h21
-rw-r--r--src/video_core/renderer_vulkan/vk_smaa.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp24
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h5
-rw-r--r--src/video_core/texture_cache/slot_vector.h4
-rw-r--r--src/video_core/texture_cache/texture_cache.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp80
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h11
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h4
-rw-r--r--src/yuzu/CMakeLists.txt57
-rw-r--r--src/yuzu/bootmanager.cpp56
-rw-r--r--src/yuzu/bootmanager.h6
-rw-r--r--src/yuzu/configuration/config.cpp1309
-rw-r--r--src/yuzu/configuration/config.h179
-rw-r--r--src/yuzu/configuration/configure_camera.cpp2
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp12
-rw-r--r--src/yuzu/configuration/configure_cpu.h1
-rw-r--r--src/yuzu/configuration/configure_cpu.ui30
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui86
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp1
-rw-r--r--src/yuzu/configuration/configure_general.cpp43
-rw-r--r--src/yuzu/configuration/configure_general.ui27
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp36
-rw-r--r--src/yuzu/configuration/configure_input_per_game.cpp6
-rw-r--r--src/yuzu/configuration/configure_input_per_game.h5
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp11
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp7
-rw-r--r--src/yuzu/configuration/configure_per_game.h5
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp1
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp4
-rw-r--r--src/yuzu/configuration/configure_system.cpp1
-rw-r--r--src/yuzu/configuration/configure_system.ui2
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.cpp2
-rw-r--r--src/yuzu/configuration/configure_ui.cpp7
-rw-r--r--src/yuzu/configuration/input_profiles.cpp10
-rw-r--r--src/yuzu/configuration/input_profiles.h4
-rw-r--r--src/yuzu/configuration/qt_config.cpp549
-rw-r--r--src/yuzu/configuration/qt_config.h55
-rw-r--r--src/yuzu/configuration/shared_translation.cpp522
-rw-r--r--src/yuzu/configuration/shared_translation.h43
-rw-r--r--src/yuzu/configuration/shared_widget.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.cpp6
-rw-r--r--src/yuzu/game_list.cpp7
-rw-r--r--src/yuzu/game_list_p.h8
-rw-r--r--src/yuzu/game_list_worker.cpp18
-rw-r--r--src/yuzu/hotkeys.cpp28
-rw-r--r--src/yuzu/hotkeys.h16
-rw-r--r--src/yuzu/main.cpp269
-rw-r--r--src/yuzu/main.h14
-rw-r--r--src/yuzu/main.ui22
-rw-r--r--src/yuzu/uisettings.cpp65
-rw-r--r--src/yuzu/uisettings.h79
-rw-r--r--src/yuzu/util/util.cpp3
-rw-r--r--src/yuzu_cmd/CMakeLists.txt9
-rw-r--r--src/yuzu_cmd/config.cpp279
-rw-r--r--src/yuzu_cmd/config.h38
-rw-r--r--src/yuzu_cmd/default_ini.h553
-rw-r--r--src/yuzu_cmd/sdl_config.cpp257
-rw-r--r--src/yuzu_cmd/sdl_config.h49
-rw-r--r--src/yuzu_cmd/yuzu.cpp17
257 files changed, 10284 insertions, 6337 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d2ca4904a..e04d2418b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -187,6 +187,7 @@ add_subdirectory(audio_core)
187add_subdirectory(video_core) 187add_subdirectory(video_core)
188add_subdirectory(network) 188add_subdirectory(network)
189add_subdirectory(input_common) 189add_subdirectory(input_common)
190add_subdirectory(frontend_common)
190add_subdirectory(shader_recompiler) 191add_subdirectory(shader_recompiler)
191 192
192if (YUZU_ROOM) 193if (YUZU_ROOM)
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 021b070e0..5721327e7 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -219,7 +219,6 @@ dependencies {
219 implementation("io.coil-kt:coil:2.2.2") 219 implementation("io.coil-kt:coil:2.2.2")
220 implementation("androidx.core:core-splashscreen:1.0.1") 220 implementation("androidx.core:core-splashscreen:1.0.1")
221 implementation("androidx.window:window:1.2.0-beta03") 221 implementation("androidx.window:window:1.2.0-beta03")
222 implementation("org.ini4j:ini4j:0.5.4")
223 implementation("androidx.constraintlayout:constraintlayout:2.1.4") 222 implementation("androidx.constraintlayout:constraintlayout:2.1.4")
224 implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") 223 implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
225 implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") 224 implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
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 9ebd6c732..e0f01127c 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
@@ -230,8 +230,6 @@ object NativeLibrary {
230 */ 230 */
231 external fun onTouchReleased(finger_id: Int) 231 external fun onTouchReleased(finger_id: Int)
232 232
233 external fun reloadSettings()
234
235 external fun initGameIni(gameID: String?) 233 external fun initGameIni(gameID: String?)
236 234
237 external fun setAppDirectory(directory: String) 235 external fun setAppDirectory(directory: String)
@@ -302,6 +300,11 @@ object NativeLibrary {
302 external fun getPerfStats(): DoubleArray 300 external fun getPerfStats(): DoubleArray
303 301
304 /** 302 /**
303 * Returns the current CPU backend.
304 */
305 external fun getCpuBackend(): String
306
307 /**
305 * Notifies the core emulation that the orientation has changed. 308 * Notifies the core emulation that the orientation has changed.
306 */ 309 */
307 external fun notifyOrientationChange(layout_option: Int, rotation: Int) 310 external fun notifyOrientationChange(layout_option: Int, rotation: Int)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
new file mode 100644
index 000000000..ab657a7b9
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
@@ -0,0 +1,76 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.adapters
5
6import android.net.Uri
7import android.text.TextUtils
8import android.view.LayoutInflater
9import android.view.ViewGroup
10import androidx.fragment.app.FragmentActivity
11import androidx.recyclerview.widget.AsyncDifferConfig
12import androidx.recyclerview.widget.DiffUtil
13import androidx.recyclerview.widget.ListAdapter
14import androidx.recyclerview.widget.RecyclerView
15import org.yuzu.yuzu_emu.databinding.CardFolderBinding
16import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
17import org.yuzu.yuzu_emu.model.GameDir
18import org.yuzu.yuzu_emu.model.GamesViewModel
19
20class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
21 ListAdapter<GameDir, FolderAdapter.FolderViewHolder>(
22 AsyncDifferConfig.Builder(DiffCallback()).build()
23 ) {
24 override fun onCreateViewHolder(
25 parent: ViewGroup,
26 viewType: Int
27 ): FolderAdapter.FolderViewHolder {
28 CardFolderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
29 .also { return FolderViewHolder(it) }
30 }
31
32 override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) =
33 holder.bind(currentList[position])
34
35 inner class FolderViewHolder(val binding: CardFolderBinding) :
36 RecyclerView.ViewHolder(binding.root) {
37 private lateinit var gameDir: GameDir
38
39 fun bind(gameDir: GameDir) {
40 this.gameDir = gameDir
41
42 binding.apply {
43 path.text = Uri.parse(gameDir.uriString).path
44 path.postDelayed(
45 {
46 path.isSelected = true
47 path.ellipsize = TextUtils.TruncateAt.MARQUEE
48 },
49 3000
50 )
51
52 buttonEdit.setOnClickListener {
53 GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir)
54 .show(
55 activity.supportFragmentManager,
56 GameFolderPropertiesDialogFragment.TAG
57 )
58 }
59
60 buttonDelete.setOnClickListener {
61 gamesViewModel.removeFolder(this@FolderViewHolder.gameDir)
62 }
63 }
64 }
65 }
66
67 private class DiffCallback : DiffUtil.ItemCallback<GameDir>() {
68 override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
69 return oldItem == newItem
70 }
71
72 override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
73 return oldItem == newItem
74 }
75 }
76}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index 151362124..ef10b209f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -10,6 +10,7 @@ enum class IntSetting(
10 override val category: Settings.Category, 10 override val category: Settings.Category,
11 override val androidDefault: Int? = null 11 override val androidDefault: Int? = null
12) : AbstractIntSetting { 12) : AbstractIntSetting {
13 CPU_BACKEND("cpu_backend", Settings.Category.Cpu),
13 CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu), 14 CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu),
14 REGION_INDEX("region_index", Settings.Category.System), 15 REGION_INDEX("region_index", Settings.Category.System),
15 LANGUAGE_INDEX("language_index", Settings.Category.System), 16 LANGUAGE_INDEX("language_index", Settings.Category.System),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 2bf0e1b0d..e3cd66185 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -3,33 +3,9 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import android.text.TextUtils
7import android.widget.Toast
8import org.yuzu.yuzu_emu.R 6import org.yuzu.yuzu_emu.R
9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
11 7
12object Settings { 8object Settings {
13 private val context get() = YuzuApplication.appContext
14
15 fun saveSettings(gameId: String = "") {
16 if (TextUtils.isEmpty(gameId)) {
17 Toast.makeText(
18 context,
19 context.getString(R.string.ini_saved),
20 Toast.LENGTH_SHORT
21 ).show()
22 SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
23 } else {
24 // TODO: Save custom game settings
25 Toast.makeText(
26 context,
27 context.getString(R.string.gameid_saved, gameId),
28 Toast.LENGTH_SHORT
29 ).show()
30 }
31 }
32
33 enum class Category { 9 enum class Category {
34 Android, 10 Android,
35 Audio, 11 Audio,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index b3b3fc209..e198b18a0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -73,12 +73,21 @@ abstract class SettingsItem(
73 R.string.frame_limit_slider, 73 R.string.frame_limit_slider,
74 R.string.frame_limit_slider_description, 74 R.string.frame_limit_slider_description,
75 1, 75 1,
76 200, 76 400,
77 "%" 77 "%"
78 ) 78 )
79 ) 79 )
80 put( 80 put(
81 SingleChoiceSetting( 81 SingleChoiceSetting(
82 IntSetting.CPU_BACKEND,
83 R.string.cpu_backend,
84 0,
85 R.array.cpuBackendArm64Names,
86 R.array.cpuBackendArm64Values
87 )
88 )
89 put(
90 SingleChoiceSetting(
82 IntSetting.CPU_ACCURACY, 91 IntSetting.CPU_ACCURACY,
83 R.string.cpu_accuracy, 92 R.string.cpu_accuracy,
84 0, 93 0,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index c73edd50e..64bfc6dd0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -19,13 +19,13 @@ import androidx.lifecycle.repeatOnLifecycle
19import androidx.navigation.fragment.NavHostFragment 19import androidx.navigation.fragment.NavHostFragment
20import androidx.navigation.navArgs 20import androidx.navigation.navArgs
21import com.google.android.material.color.MaterialColors 21import com.google.android.material.color.MaterialColors
22import kotlinx.coroutines.CoroutineScope
23import kotlinx.coroutines.Dispatchers
22import kotlinx.coroutines.flow.collectLatest 24import kotlinx.coroutines.flow.collectLatest
23import kotlinx.coroutines.launch 25import kotlinx.coroutines.launch
24import org.yuzu.yuzu_emu.NativeLibrary
25import java.io.IOException 26import java.io.IOException
26import org.yuzu.yuzu_emu.R 27import org.yuzu.yuzu_emu.R
27import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 28import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
28import org.yuzu.yuzu_emu.features.settings.model.Settings
29import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 29import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
30import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment 30import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
31import org.yuzu.yuzu_emu.model.SettingsViewModel 31import org.yuzu.yuzu_emu.model.SettingsViewModel
@@ -54,10 +54,6 @@ class SettingsActivity : AppCompatActivity() {
54 54
55 WindowCompat.setDecorFitsSystemWindows(window, false) 55 WindowCompat.setDecorFitsSystemWindows(window, false)
56 56
57 if (savedInstanceState != null) {
58 settingsViewModel.shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE)
59 }
60
61 if (InsetsHelper.getSystemGestureType(applicationContext) != 57 if (InsetsHelper.getSystemGestureType(applicationContext) !=
62 InsetsHelper.GESTURE_NAVIGATION 58 InsetsHelper.GESTURE_NAVIGATION
63 ) { 59 ) {
@@ -128,12 +124,6 @@ class SettingsActivity : AppCompatActivity() {
128 } 124 }
129 } 125 }
130 126
131 override fun onSaveInstanceState(outState: Bundle) {
132 // Critical: If super method is not called, rotations will be busted.
133 super.onSaveInstanceState(outState)
134 outState.putBoolean(KEY_SHOULD_SAVE, settingsViewModel.shouldSave)
135 }
136
137 override fun onStart() { 127 override fun onStart() {
138 super.onStart() 128 super.onStart()
139 // TODO: Load custom settings contextually 129 // TODO: Load custom settings contextually
@@ -142,16 +132,10 @@ class SettingsActivity : AppCompatActivity() {
142 } 132 }
143 } 133 }
144 134
145 /**
146 * If this is called, the user has left the settings screen (potentially through the
147 * home button) and will expect their changes to be persisted. So we kick off an
148 * IntentService which will do so on a background thread.
149 */
150 override fun onStop() { 135 override fun onStop() {
151 super.onStop() 136 super.onStop()
152 if (isFinishing && settingsViewModel.shouldSave) { 137 CoroutineScope(Dispatchers.IO).launch {
153 Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") 138 NativeConfig.saveSettings()
154 Settings.saveSettings()
155 } 139 }
156 } 140 }
157 141
@@ -161,15 +145,13 @@ class SettingsActivity : AppCompatActivity() {
161 } 145 }
162 146
163 fun onSettingsReset() { 147 fun onSettingsReset() {
164 // Prevents saving to a non-existent settings file
165 settingsViewModel.shouldSave = false
166
167 // Delete settings file because the user may have changed values that do not exist in the UI 148 // Delete settings file because the user may have changed values that do not exist in the UI
149 NativeConfig.unloadConfig()
168 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) 150 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
169 if (!settingsFile.delete()) { 151 if (!settingsFile.delete()) {
170 throw IOException("Failed to delete $settingsFile") 152 throw IOException("Failed to delete $settingsFile")
171 } 153 }
172 NativeLibrary.reloadSettings() 154 NativeConfig.initializeConfig()
173 155
174 Toast.makeText( 156 Toast.makeText(
175 applicationContext, 157 applicationContext,
@@ -194,8 +176,4 @@ class SettingsActivity : AppCompatActivity() {
194 windowInsets 176 windowInsets
195 } 177 }
196 } 178 }
197
198 companion object {
199 private const val KEY_SHOULD_SAVE = "should_save"
200 }
201} 179}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index a7a029fc1..af2c1e582 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -105,7 +105,6 @@ class SettingsAdapter(
105 fun onBooleanClick(item: SwitchSetting, checked: Boolean) { 105 fun onBooleanClick(item: SwitchSetting, checked: Boolean) {
106 item.checked = checked 106 item.checked = checked
107 settingsViewModel.setShouldReloadSettingsList(true) 107 settingsViewModel.setShouldReloadSettingsList(true)
108 settingsViewModel.shouldSave = true
109 } 108 }
110 109
111 fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { 110 fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) {
@@ -161,7 +160,6 @@ class SettingsAdapter(
161 epochTime += timePicker.hour.toLong() * 60 * 60 160 epochTime += timePicker.hour.toLong() * 60 * 60
162 epochTime += timePicker.minute.toLong() * 60 161 epochTime += timePicker.minute.toLong() * 60
163 if (item.value != epochTime) { 162 if (item.value != epochTime) {
164 settingsViewModel.shouldSave = true
165 notifyItemChanged(position) 163 notifyItemChanged(position)
166 item.value = epochTime 164 item.value = epochTime
167 } 165 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 8b71e32f3..7425728c6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -269,6 +269,7 @@ class SettingsFragmentPresenter(
269 add(BooleanSetting.RENDERER_DEBUG.key) 269 add(BooleanSetting.RENDERER_DEBUG.key)
270 270
271 add(HeaderSetting(R.string.cpu)) 271 add(HeaderSetting(R.string.cpu))
272 add(IntSetting.CPU_BACKEND.key)
272 add(IntSetting.CPU_ACCURACY.key) 273 add(IntSetting.CPU_ACCURACY.key)
273 add(BooleanSetting.CPU_DEBUG_MODE.key) 274 add(BooleanSetting.CPU_DEBUG_MODE.key)
274 add(SettingsItem.FASTMEM_COMBINED) 275 add(SettingsItem.FASTMEM_COMBINED)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
index 2b04d666a..3ae5b4653 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -3,15 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.utils 4package org.yuzu.yuzu_emu.features.settings.utils
5 5
6import android.widget.Toast
7import java.io.* 6import java.io.*
8import org.ini4j.Wini
9import org.yuzu.yuzu_emu.R
10import org.yuzu.yuzu_emu.YuzuApplication
11import org.yuzu.yuzu_emu.features.settings.model.*
12import org.yuzu.yuzu_emu.utils.DirectoryInitialization 7import org.yuzu.yuzu_emu.utils.DirectoryInitialization
13import org.yuzu.yuzu_emu.utils.Log
14import org.yuzu.yuzu_emu.utils.NativeConfig
15 8
16/** 9/**
17 * Contains static methods for interacting with .ini files in which settings are stored. 10 * Contains static methods for interacting with .ini files in which settings are stored.
@@ -19,41 +12,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
19object SettingsFile { 12object SettingsFile {
20 const val FILE_NAME_CONFIG = "config" 13 const val FILE_NAME_CONFIG = "config"
21 14
22 /**
23 * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
24 * telling why it failed.
25 *
26 * @param fileName The target filename without a path or extension.
27 */
28 fun saveFile(fileName: String) {
29 val ini = getSettingsFile(fileName)
30 try {
31 val wini = Wini(ini)
32 for (specificCategory in Settings.Category.values()) {
33 val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
34 for (setting in Settings.settingsList) {
35 if (setting.key!!.isEmpty()) continue
36
37 val settingCategoryHeader =
38 NativeConfig.getConfigHeader(setting.category.ordinal)
39 val iniSetting: String? = wini.get(categoryHeader, setting.key)
40 if (iniSetting != null || settingCategoryHeader == categoryHeader) {
41 wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
42 }
43 }
44 }
45 wini.store()
46 } catch (e: IOException) {
47 Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
48 val context = YuzuApplication.appContext
49 Toast.makeText(
50 context,
51 context.getString(R.string.error_saving, fileName, e.message),
52 Toast.LENGTH_SHORT
53 ).show()
54 }
55 }
56
57 fun getSettingsFile(fileName: String): File = 15 fun getSettingsFile(fileName: String): File =
58 File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") 16 File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
59} 17}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
new file mode 100644
index 000000000..dec2b7cf1
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.fragments
5
6import android.app.Dialog
7import android.content.DialogInterface
8import android.net.Uri
9import android.os.Bundle
10import androidx.fragment.app.DialogFragment
11import androidx.fragment.app.activityViewModels
12import com.google.android.material.dialog.MaterialAlertDialogBuilder
13import org.yuzu.yuzu_emu.R
14import org.yuzu.yuzu_emu.databinding.DialogAddFolderBinding
15import org.yuzu.yuzu_emu.model.GameDir
16import org.yuzu.yuzu_emu.model.GamesViewModel
17
18class AddGameFolderDialogFragment : DialogFragment() {
19 private val gamesViewModel: GamesViewModel by activityViewModels()
20
21 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
22 val binding = DialogAddFolderBinding.inflate(layoutInflater)
23 val folderUriString = requireArguments().getString(FOLDER_URI_STRING)
24 if (folderUriString == null) {
25 dismiss()
26 }
27 binding.path.text = Uri.parse(folderUriString).path
28
29 return MaterialAlertDialogBuilder(requireContext())
30 .setTitle(R.string.add_game_folder)
31 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
32 val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked)
33 gamesViewModel.addFolder(newGameDir)
34 }
35 .setNegativeButton(android.R.string.cancel, null)
36 .setView(binding.root)
37 .show()
38 }
39
40 companion object {
41 const val TAG = "AddGameFolderDialogFragment"
42
43 private const val FOLDER_URI_STRING = "FolderUriString"
44
45 fun newInstance(folderUriString: String): AddGameFolderDialogFragment {
46 val args = Bundle()
47 args.putString(FOLDER_URI_STRING, folderUriString)
48 val fragment = AddGameFolderDialogFragment()
49 fragment.arguments = args
50 return fragment
51 }
52 }
53}
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 c32fa0d7e..734c1d5ca 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
@@ -414,8 +414,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
414 perfStatsUpdater = { 414 perfStatsUpdater = {
415 if (emulationViewModel.emulationStarted.value) { 415 if (emulationViewModel.emulationStarted.value) {
416 val perfStats = NativeLibrary.getPerfStats() 416 val perfStats = NativeLibrary.getPerfStats()
417 val cpuBackend = NativeLibrary.getCpuBackend()
417 if (_binding != null) { 418 if (_binding != null) {
418 binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) 419 binding.showFpsText.text =
420 String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend)
419 } 421 }
420 perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) 422 perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
421 } 423 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt
new file mode 100644
index 000000000..b6c2e4635
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFolderPropertiesDialogFragment.kt
@@ -0,0 +1,72 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.fragments
5
6import android.app.Dialog
7import android.content.DialogInterface
8import android.os.Bundle
9import androidx.fragment.app.DialogFragment
10import androidx.fragment.app.activityViewModels
11import com.google.android.material.dialog.MaterialAlertDialogBuilder
12import org.yuzu.yuzu_emu.R
13import org.yuzu.yuzu_emu.databinding.DialogFolderPropertiesBinding
14import org.yuzu.yuzu_emu.model.GameDir
15import org.yuzu.yuzu_emu.model.GamesViewModel
16import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
17
18class GameFolderPropertiesDialogFragment : DialogFragment() {
19 private val gamesViewModel: GamesViewModel by activityViewModels()
20
21 private var deepScan = false
22
23 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
24 val binding = DialogFolderPropertiesBinding.inflate(layoutInflater)
25 val gameDir = requireArguments().parcelable<GameDir>(GAME_DIR)!!
26
27 // Restore checkbox state
28 binding.deepScanSwitch.isChecked =
29 savedInstanceState?.getBoolean(DEEP_SCAN) ?: gameDir.deepScan
30
31 // Ensure that we can get the checkbox state even if the view is destroyed
32 deepScan = binding.deepScanSwitch.isChecked
33 binding.deepScanSwitch.setOnClickListener {
34 deepScan = binding.deepScanSwitch.isChecked
35 }
36
37 return MaterialAlertDialogBuilder(requireContext())
38 .setView(binding.root)
39 .setTitle(R.string.game_folder_properties)
40 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
41 val folderIndex = gamesViewModel.folders.value.indexOf(gameDir)
42 if (folderIndex != -1) {
43 gamesViewModel.folders.value[folderIndex].deepScan =
44 binding.deepScanSwitch.isChecked
45 gamesViewModel.updateGameDirs()
46 }
47 }
48 .setNegativeButton(android.R.string.cancel, null)
49 .show()
50 }
51
52 override fun onSaveInstanceState(outState: Bundle) {
53 super.onSaveInstanceState(outState)
54 outState.putBoolean(DEEP_SCAN, deepScan)
55 }
56
57 companion object {
58 const val TAG = "GameFolderPropertiesDialogFragment"
59
60 private const val GAME_DIR = "GameDir"
61
62 private const val DEEP_SCAN = "DeepScan"
63
64 fun newInstance(gameDir: GameDir): GameFolderPropertiesDialogFragment {
65 val args = Bundle()
66 args.putParcelable(GAME_DIR, gameDir)
67 val fragment = GameFolderPropertiesDialogFragment()
68 fragment.arguments = args
69 return fragment
70 }
71 }
72}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
new file mode 100644
index 000000000..341a37fdb
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
@@ -0,0 +1,128 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.fragments
5
6import android.content.Intent
7import android.os.Bundle
8import android.view.LayoutInflater
9import android.view.View
10import android.view.ViewGroup
11import androidx.core.view.ViewCompat
12import androidx.core.view.WindowInsetsCompat
13import androidx.core.view.updatePadding
14import androidx.fragment.app.Fragment
15import androidx.fragment.app.activityViewModels
16import androidx.lifecycle.Lifecycle
17import androidx.lifecycle.lifecycleScope
18import androidx.lifecycle.repeatOnLifecycle
19import androidx.navigation.findNavController
20import androidx.recyclerview.widget.GridLayoutManager
21import com.google.android.material.transition.MaterialSharedAxis
22import kotlinx.coroutines.launch
23import org.yuzu.yuzu_emu.R
24import org.yuzu.yuzu_emu.adapters.FolderAdapter
25import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
26import org.yuzu.yuzu_emu.model.GamesViewModel
27import org.yuzu.yuzu_emu.model.HomeViewModel
28import org.yuzu.yuzu_emu.ui.main.MainActivity
29
30class GameFoldersFragment : Fragment() {
31 private var _binding: FragmentFoldersBinding? = null
32 private val binding get() = _binding!!
33
34 private val homeViewModel: HomeViewModel by activityViewModels()
35 private val gamesViewModel: GamesViewModel by activityViewModels()
36
37 override fun onCreate(savedInstanceState: Bundle?) {
38 super.onCreate(savedInstanceState)
39 enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
40 returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
41 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
42
43 gamesViewModel.onOpenGameFoldersFragment()
44 }
45
46 override fun onCreateView(
47 inflater: LayoutInflater,
48 container: ViewGroup?,
49 savedInstanceState: Bundle?
50 ): View {
51 _binding = FragmentFoldersBinding.inflate(inflater)
52 return binding.root
53 }
54
55 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
56 super.onViewCreated(view, savedInstanceState)
57 homeViewModel.setNavigationVisibility(visible = false, animated = true)
58 homeViewModel.setStatusBarShadeVisibility(visible = false)
59
60 binding.toolbarFolders.setNavigationOnClickListener {
61 binding.root.findNavController().popBackStack()
62 }
63
64 binding.listFolders.apply {
65 layoutManager = GridLayoutManager(
66 requireContext(),
67 resources.getInteger(R.integer.grid_columns)
68 )
69 adapter = FolderAdapter(requireActivity(), gamesViewModel)
70 }
71
72 viewLifecycleOwner.lifecycleScope.launch {
73 repeatOnLifecycle(Lifecycle.State.CREATED) {
74 gamesViewModel.folders.collect {
75 (binding.listFolders.adapter as FolderAdapter).submitList(it)
76 }
77 }
78 }
79
80 val mainActivity = requireActivity() as MainActivity
81 binding.buttonAdd.setOnClickListener {
82 mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
83 }
84
85 setInsets()
86 }
87
88 override fun onStop() {
89 super.onStop()
90 gamesViewModel.onCloseGameFoldersFragment()
91 }
92
93 private fun setInsets() =
94 ViewCompat.setOnApplyWindowInsetsListener(
95 binding.root
96 ) { _: View, windowInsets: WindowInsetsCompat ->
97 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
98 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
99
100 val leftInsets = barInsets.left + cutoutInsets.left
101 val rightInsets = barInsets.right + cutoutInsets.right
102
103 val mlpToolbar = binding.toolbarFolders.layoutParams as ViewGroup.MarginLayoutParams
104 mlpToolbar.leftMargin = leftInsets
105 mlpToolbar.rightMargin = rightInsets
106 binding.toolbarFolders.layoutParams = mlpToolbar
107
108 val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
109 val mlpFab =
110 binding.buttonAdd.layoutParams as ViewGroup.MarginLayoutParams
111 mlpFab.leftMargin = leftInsets + fabSpacing
112 mlpFab.rightMargin = rightInsets + fabSpacing
113 mlpFab.bottomMargin = barInsets.bottom + fabSpacing
114 binding.buttonAdd.layoutParams = mlpFab
115
116 val mlpListFolders = binding.listFolders.layoutParams as ViewGroup.MarginLayoutParams
117 mlpListFolders.leftMargin = leftInsets
118 mlpListFolders.rightMargin = rightInsets
119 binding.listFolders.layoutParams = mlpListFolders
120
121 binding.listFolders.updatePadding(
122 bottom = barInsets.bottom +
123 resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
124 )
125
126 windowInsets
127 }
128}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 4720daec4..3addc2e63 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -127,18 +127,13 @@ class HomeSettingsFragment : Fragment() {
127 ) 127 )
128 add( 128 add(
129 HomeSetting( 129 HomeSetting(
130 R.string.select_games_folder, 130 R.string.manage_game_folders,
131 R.string.select_games_folder_description, 131 R.string.select_games_folder_description,
132 R.drawable.ic_add, 132 R.drawable.ic_add,
133 { 133 {
134 mainActivity.getGamesDirectory.launch( 134 binding.root.findNavController()
135 Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data 135 .navigate(R.id.action_homeSettingsFragment_to_gameFoldersFragment)
136 ) 136 }
137 },
138 { true },
139 0,
140 0,
141 homeViewModel.gamesDir
142 ) 137 )
143 ) 138 )
144 add( 139 add(
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 ec116ab62..6940fc757 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
@@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
21import org.yuzu.yuzu_emu.model.HomeViewModel 21import org.yuzu.yuzu_emu.model.HomeViewModel
22import org.yuzu.yuzu_emu.model.Installable 22import org.yuzu.yuzu_emu.model.Installable
23import org.yuzu.yuzu_emu.ui.main.MainActivity 23import org.yuzu.yuzu_emu.ui.main.MainActivity
24import java.time.LocalDateTime
25import java.time.format.DateTimeFormatter
24 26
25class InstallableFragment : Fragment() { 27class InstallableFragment : Fragment() {
26 private var _binding: FragmentInstallablesBinding? = null 28 private var _binding: FragmentInstallablesBinding? = null
@@ -78,7 +80,15 @@ class InstallableFragment : Fragment() {
78 R.string.manage_save_data, 80 R.string.manage_save_data,
79 R.string.import_export_saves_description, 81 R.string.import_export_saves_description,
80 install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, 82 install = { mainActivity.importSaves.launch(arrayOf("application/zip")) },
81 export = { mainActivity.exportSave() } 83 export = {
84 mainActivity.exportSaves.launch(
85 "yuzu saves - ${
86 LocalDateTime.now().format(
87 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
88 )
89 }.zip"
90 )
91 }
82 ) 92 )
83 } else { 93 } else {
84 Installable( 94 Installable(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
index d18ec6974..b88d2c038 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
@@ -52,7 +52,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
52 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> 52 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
53 settingsViewModel.clickedItem!!.setting.reset() 53 settingsViewModel.clickedItem!!.setting.reset()
54 settingsViewModel.setAdapterItemChanged(position) 54 settingsViewModel.setAdapterItemChanged(position)
55 settingsViewModel.shouldSave = true
56 } 55 }
57 .setNegativeButton(android.R.string.cancel, null) 56 .setNegativeButton(android.R.string.cancel, null)
58 .create() 57 .create()
@@ -137,24 +136,17 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
137 is SingleChoiceSetting -> { 136 is SingleChoiceSetting -> {
138 val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting 137 val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting
139 val value = getValueForSingleChoiceSelection(scSetting, which) 138 val value = getValueForSingleChoiceSelection(scSetting, which)
140 if (scSetting.selectedValue != value) {
141 settingsViewModel.shouldSave = true
142 }
143 scSetting.selectedValue = value 139 scSetting.selectedValue = value
144 } 140 }
145 141
146 is StringSingleChoiceSetting -> { 142 is StringSingleChoiceSetting -> {
147 val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting 143 val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting
148 val value = scSetting.getValueAt(which) 144 val value = scSetting.getValueAt(which)
149 if (scSetting.selectedValue != value) settingsViewModel.shouldSave = true
150 scSetting.selectedValue = value 145 scSetting.selectedValue = value
151 } 146 }
152 147
153 is SliderSetting -> { 148 is SliderSetting -> {
154 val sliderSetting = settingsViewModel.clickedItem as SliderSetting 149 val sliderSetting = settingsViewModel.clickedItem as SliderSetting
155 if (sliderSetting.selectedValue != settingsViewModel.sliderProgress.value) {
156 settingsViewModel.shouldSave = true
157 }
158 sliderSetting.selectedValue = settingsViewModel.sliderProgress.value 150 sliderSetting.selectedValue = settingsViewModel.sliderProgress.value
159 } 151 }
160 } 152 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index c66bb635a..c4277735d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -42,7 +42,7 @@ import org.yuzu.yuzu_emu.model.SetupPage
42import org.yuzu.yuzu_emu.model.StepState 42import org.yuzu.yuzu_emu.model.StepState
43import org.yuzu.yuzu_emu.ui.main.MainActivity 43import org.yuzu.yuzu_emu.ui.main.MainActivity
44import org.yuzu.yuzu_emu.utils.DirectoryInitialization 44import org.yuzu.yuzu_emu.utils.DirectoryInitialization
45import org.yuzu.yuzu_emu.utils.GameHelper 45import org.yuzu.yuzu_emu.utils.NativeConfig
46import org.yuzu.yuzu_emu.utils.ViewUtils 46import org.yuzu.yuzu_emu.utils.ViewUtils
47 47
48class SetupFragment : Fragment() { 48class SetupFragment : Fragment() {
@@ -184,11 +184,7 @@ class SetupFragment : Fragment() {
184 R.string.add_games_warning_description, 184 R.string.add_games_warning_description,
185 R.string.add_games_warning_help, 185 R.string.add_games_warning_help,
186 { 186 {
187 val preferences = 187 if (NativeConfig.getGameDirs().isNotEmpty()) {
188 PreferenceManager.getDefaultSharedPreferences(
189 YuzuApplication.appContext
190 )
191 if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
192 StepState.COMPLETE 188 StepState.COMPLETE
193 } else { 189 } else {
194 StepState.INCOMPLETE 190 StepState.INCOMPLETE
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
index de84b2adb..2fa3ab31b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt
@@ -18,8 +18,8 @@ class Game(
18 val version: String = "", 18 val version: String = "",
19 val isHomebrew: Boolean = false 19 val isHomebrew: Boolean = false
20) : Parcelable { 20) : Parcelable {
21 val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime" 21 val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
22 val keyLastPlayedTime get() = "${programId}_LastPlayed" 22 val keyLastPlayedTime get() = "${path}_LastPlayed"
23 23
24 override fun equals(other: Any?): Boolean { 24 override fun equals(other: Any?): Boolean {
25 if (other !is Game) { 25 if (other !is Game) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameDir.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameDir.kt
new file mode 100644
index 000000000..274bc1c7b
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GameDir.kt
@@ -0,0 +1,13 @@
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 android.os.Parcelable
7import kotlinx.parcelize.Parcelize
8
9@Parcelize
10data class GameDir(
11 val uriString: String,
12 var deepScan: Boolean
13) : Parcelable
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index 8512ed17c..752d98c10 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -12,6 +12,7 @@ import java.util.Locale
12import kotlinx.coroutines.Dispatchers 12import kotlinx.coroutines.Dispatchers
13import kotlinx.coroutines.flow.MutableStateFlow 13import kotlinx.coroutines.flow.MutableStateFlow
14import kotlinx.coroutines.flow.StateFlow 14import kotlinx.coroutines.flow.StateFlow
15import kotlinx.coroutines.flow.asStateFlow
15import kotlinx.coroutines.launch 16import kotlinx.coroutines.launch
16import kotlinx.coroutines.withContext 17import kotlinx.coroutines.withContext
17import kotlinx.serialization.decodeFromString 18import kotlinx.serialization.decodeFromString
@@ -20,6 +21,7 @@ import org.yuzu.yuzu_emu.NativeLibrary
20import org.yuzu.yuzu_emu.YuzuApplication 21import org.yuzu.yuzu_emu.YuzuApplication
21import org.yuzu.yuzu_emu.utils.GameHelper 22import org.yuzu.yuzu_emu.utils.GameHelper
22import org.yuzu.yuzu_emu.utils.GameMetadata 23import org.yuzu.yuzu_emu.utils.GameMetadata
24import org.yuzu.yuzu_emu.utils.NativeConfig
23 25
24class GamesViewModel : ViewModel() { 26class GamesViewModel : ViewModel() {
25 val games: StateFlow<List<Game>> get() = _games 27 val games: StateFlow<List<Game>> get() = _games
@@ -40,6 +42,9 @@ class GamesViewModel : ViewModel() {
40 val searchFocused: StateFlow<Boolean> get() = _searchFocused 42 val searchFocused: StateFlow<Boolean> get() = _searchFocused
41 private val _searchFocused = MutableStateFlow(false) 43 private val _searchFocused = MutableStateFlow(false)
42 44
45 private val _folders = MutableStateFlow(mutableListOf<GameDir>())
46 val folders = _folders.asStateFlow()
47
43 init { 48 init {
44 // Ensure keys are loaded so that ROM metadata can be decrypted. 49 // Ensure keys are loaded so that ROM metadata can be decrypted.
45 NativeLibrary.reloadKeys() 50 NativeLibrary.reloadKeys()
@@ -50,6 +55,7 @@ class GamesViewModel : ViewModel() {
50 55
51 viewModelScope.launch { 56 viewModelScope.launch {
52 withContext(Dispatchers.IO) { 57 withContext(Dispatchers.IO) {
58 getGameDirs()
53 if (storedGames!!.isNotEmpty()) { 59 if (storedGames!!.isNotEmpty()) {
54 val deserializedGames = mutableSetOf<Game>() 60 val deserializedGames = mutableSetOf<Game>()
55 storedGames.forEach { 61 storedGames.forEach {
@@ -104,7 +110,7 @@ class GamesViewModel : ViewModel() {
104 _searchFocused.value = searchFocused 110 _searchFocused.value = searchFocused
105 } 111 }
106 112
107 fun reloadGames(directoryChanged: Boolean) { 113 fun reloadGames(directoriesChanged: Boolean) {
108 if (isReloading.value) { 114 if (isReloading.value) {
109 return 115 return
110 } 116 }
@@ -116,10 +122,61 @@ class GamesViewModel : ViewModel() {
116 setGames(GameHelper.getGames()) 122 setGames(GameHelper.getGames())
117 _isReloading.value = false 123 _isReloading.value = false
118 124
119 if (directoryChanged) { 125 if (directoriesChanged) {
120 setShouldSwapData(true) 126 setShouldSwapData(true)
121 } 127 }
122 } 128 }
123 } 129 }
124 } 130 }
131
132 fun addFolder(gameDir: GameDir) =
133 viewModelScope.launch {
134 withContext(Dispatchers.IO) {
135 NativeConfig.addGameDir(gameDir)
136 getGameDirs()
137 }
138 }
139
140 fun removeFolder(gameDir: GameDir) =
141 viewModelScope.launch {
142 withContext(Dispatchers.IO) {
143 val gameDirs = _folders.value.toMutableList()
144 val removedDirIndex = gameDirs.indexOf(gameDir)
145 if (removedDirIndex != -1) {
146 gameDirs.removeAt(removedDirIndex)
147 NativeConfig.setGameDirs(gameDirs.toTypedArray())
148 getGameDirs()
149 }
150 }
151 }
152
153 fun updateGameDirs() =
154 viewModelScope.launch {
155 withContext(Dispatchers.IO) {
156 NativeConfig.setGameDirs(_folders.value.toTypedArray())
157 getGameDirs()
158 }
159 }
160
161 fun onOpenGameFoldersFragment() =
162 viewModelScope.launch {
163 withContext(Dispatchers.IO) {
164 getGameDirs()
165 }
166 }
167
168 fun onCloseGameFoldersFragment() =
169 viewModelScope.launch {
170 withContext(Dispatchers.IO) {
171 getGameDirs(true)
172 }
173 }
174
175 private fun getGameDirs(reloadList: Boolean = false) {
176 val gameDirs = NativeConfig.getGameDirs()
177 _folders.value = gameDirs.toMutableList()
178 if (reloadList) {
179 reloadGames(true)
180 }
181 }
125} 182}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index 756f76721..251b5a667 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -3,15 +3,9 @@
3 3
4package org.yuzu.yuzu_emu.model 4package org.yuzu.yuzu_emu.model
5 5
6import android.net.Uri
7import androidx.fragment.app.FragmentActivity
8import androidx.lifecycle.ViewModel 6import androidx.lifecycle.ViewModel
9import androidx.lifecycle.ViewModelProvider
10import androidx.preference.PreferenceManager
11import kotlinx.coroutines.flow.MutableStateFlow 7import kotlinx.coroutines.flow.MutableStateFlow
12import kotlinx.coroutines.flow.StateFlow 8import kotlinx.coroutines.flow.StateFlow
13import org.yuzu.yuzu_emu.YuzuApplication
14import org.yuzu.yuzu_emu.utils.GameHelper
15 9
16class HomeViewModel : ViewModel() { 10class HomeViewModel : ViewModel() {
17 val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible 11 val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible
@@ -23,14 +17,6 @@ class HomeViewModel : ViewModel() {
23 val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward 17 val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward
24 private val _shouldPageForward = MutableStateFlow(false) 18 private val _shouldPageForward = MutableStateFlow(false)
25 19
26 val gamesDir: StateFlow<String> get() = _gamesDir
27 private val _gamesDir = MutableStateFlow(
28 Uri.parse(
29 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
30 .getString(GameHelper.KEY_GAME_PATH, "")
31 ).path ?: ""
32 )
33
34 var navigatedToSetup = false 20 var navigatedToSetup = false
35 21
36 fun setNavigationVisibility(visible: Boolean, animated: Boolean) { 22 fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
@@ -50,9 +36,4 @@ class HomeViewModel : ViewModel() {
50 fun setShouldPageForward(pageForward: Boolean) { 36 fun setShouldPageForward(pageForward: Boolean) {
51 _shouldPageForward.value = pageForward 37 _shouldPageForward.value = pageForward
52 } 38 }
53
54 fun setGamesDir(activity: FragmentActivity, dir: String) {
55 ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
56 _gamesDir.value = dir
57 }
58} 39}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
index 6f947674e..ccc981e95 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
@@ -13,8 +13,6 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
13class SettingsViewModel : ViewModel() { 13class SettingsViewModel : ViewModel() {
14 var game: Game? = null 14 var game: Game? = null
15 15
16 var shouldSave = false
17
18 var clickedItem: SettingsItem? = null 16 var clickedItem: SettingsItem? = null
19 17
20 val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate 18 val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate
@@ -73,6 +71,5 @@ class SettingsViewModel : ViewModel() {
73 71
74 fun clear() { 72 fun clear() {
75 game = null 73 game = null
76 shouldSave = false
77 } 74 }
78} 75}
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 211b7cf69..16323a316 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
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.ui.main
6import android.content.Intent 6import android.content.Intent
7import android.net.Uri 7import android.net.Uri
8import android.os.Bundle 8import android.os.Bundle
9import android.provider.DocumentsContract
10import android.view.View 9import android.view.View
11import android.view.ViewGroup.MarginLayoutParams 10import android.view.ViewGroup.MarginLayoutParams
12import android.view.WindowManager 11import android.view.WindowManager
@@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
20import androidx.core.view.ViewCompat 19import androidx.core.view.ViewCompat
21import androidx.core.view.WindowCompat 20import androidx.core.view.WindowCompat
22import androidx.core.view.WindowInsetsCompat 21import androidx.core.view.WindowInsetsCompat
23import androidx.documentfile.provider.DocumentFile
24import androidx.lifecycle.Lifecycle 22import androidx.lifecycle.Lifecycle
25import androidx.lifecycle.lifecycleScope 23import androidx.lifecycle.lifecycleScope
26import androidx.lifecycle.repeatOnLifecycle 24import androidx.lifecycle.repeatOnLifecycle
@@ -41,8 +39,8 @@ import org.yuzu.yuzu_emu.NativeLibrary
41import org.yuzu.yuzu_emu.R 39import org.yuzu.yuzu_emu.R
42import org.yuzu.yuzu_emu.activities.EmulationActivity 40import org.yuzu.yuzu_emu.activities.EmulationActivity
43import org.yuzu.yuzu_emu.databinding.ActivityMainBinding 41import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
44import org.yuzu.yuzu_emu.features.DocumentProvider
45import org.yuzu.yuzu_emu.features.settings.model.Settings 42import org.yuzu.yuzu_emu.features.settings.model.Settings
43import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
46import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 44import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
47import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 45import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
48import org.yuzu.yuzu_emu.getPublicFilesDir 46import org.yuzu.yuzu_emu.getPublicFilesDir
@@ -53,9 +51,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel
53import org.yuzu.yuzu_emu.utils.* 51import org.yuzu.yuzu_emu.utils.*
54import java.io.BufferedInputStream 52import java.io.BufferedInputStream
55import java.io.BufferedOutputStream 53import java.io.BufferedOutputStream
56import java.io.FileOutputStream
57import java.time.LocalDateTime
58import java.time.format.DateTimeFormatter
59import java.util.zip.ZipEntry 54import java.util.zip.ZipEntry
60import java.util.zip.ZipInputStream 55import java.util.zip.ZipInputStream
61 56
@@ -73,7 +68,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
73 68
74 // Get first subfolder in saves folder (should be the user folder) 69 // Get first subfolder in saves folder (should be the user folder)
75 val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" 70 val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
76 private var lastZipCreated: File? = null
77 71
78 override fun onCreate(savedInstanceState: Bundle?) { 72 override fun onCreate(savedInstanceState: Bundle?) {
79 val splashScreen = installSplashScreen() 73 val splashScreen = installSplashScreen()
@@ -259,6 +253,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
259 super.onResume() 253 super.onResume()
260 } 254 }
261 255
256 override fun onStop() {
257 super.onStop()
258 CoroutineScope(Dispatchers.IO).launch {
259 NativeConfig.saveSettings()
260 }
261 }
262
262 override fun onDestroy() { 263 override fun onDestroy() {
263 EmulationActivity.stopForegroundService(this) 264 EmulationActivity.stopForegroundService(this)
264 super.onDestroy() 265 super.onDestroy()
@@ -300,20 +301,19 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
300 Intent.FLAG_GRANT_READ_URI_PERMISSION 301 Intent.FLAG_GRANT_READ_URI_PERMISSION
301 ) 302 )
302 303
303 // When a new directory is picked, we currently will reset the existing games 304 val uriString = result.toString()
304 // database. This effectively means that only one game directory is supported. 305 val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString }
305 PreferenceManager.getDefaultSharedPreferences(applicationContext).edit() 306 if (folder != null) {
306 .putString(GameHelper.KEY_GAME_PATH, result.toString()) 307 Toast.makeText(
307 .apply() 308 applicationContext,
308 309 R.string.folder_already_added,
309 Toast.makeText( 310 Toast.LENGTH_SHORT
310 applicationContext, 311 ).show()
311 R.string.games_dir_selected, 312 return
312 Toast.LENGTH_LONG 313 }
313 ).show()
314 314
315 gamesViewModel.reloadGames(true) 315 AddGameFolderDialogFragment.newInstance(uriString)
316 homeViewModel.setGamesDir(this, result.path!!) 316 .show(supportFragmentManager, AddGameFolderDialogFragment.TAG)
317 } 317 }
318 318
319 val getProdKey = 319 val getProdKey =
@@ -632,6 +632,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
632 } 632 }
633 633
634 // Clear existing user data 634 // Clear existing user data
635 NativeConfig.unloadConfig()
635 File(DirectoryInitialization.userDirectory!!).deleteRecursively() 636 File(DirectoryInitialization.userDirectory!!).deleteRecursively()
636 637
637 // Copy archive to internal storage 638 // Copy archive to internal storage
@@ -650,6 +651,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
650 651
651 // Reinitialize relevant data 652 // Reinitialize relevant data
652 NativeLibrary.initializeSystem(true) 653 NativeLibrary.initializeSystem(true)
654 NativeConfig.initializeConfig()
653 gamesViewModel.reloadGames(false) 655 gamesViewModel.reloadGames(false)
654 656
655 return@newInstance getString(R.string.user_data_import_success) 657 return@newInstance getString(R.string.user_data_import_success)
@@ -657,74 +659,30 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
657 } 659 }
658 660
659 /** 661 /**
660 * Zips the save files located in the given folder path and creates a new zip file with the current date and time.
661 * @return true if the zip file is successfully created, false otherwise.
662 */
663 private fun zipSave(): Boolean {
664 try {
665 val tempFolder = File(getPublicFilesDir().canonicalPath, "temp")
666 tempFolder.mkdirs()
667 val saveFolder = File(savesFolderRoot)
668 val outputZipFile = File(
669 tempFolder,
670 "yuzu saves - ${
671 LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
672 }.zip"
673 )
674 outputZipFile.createNewFile()
675 val result = FileUtil.zipFromInternalStorage(
676 saveFolder,
677 savesFolderRoot,
678 BufferedOutputStream(FileOutputStream(outputZipFile))
679 )
680 if (result == TaskState.Failed) {
681 return false
682 }
683 lastZipCreated = outputZipFile
684 } catch (e: Exception) {
685 return false
686 }
687 return true
688 }
689
690 /**
691 * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. 662 * Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
692 */ 663 */
693 fun exportSave() { 664 val exportSaves = registerForActivityResult(
694 CoroutineScope(Dispatchers.IO).launch { 665 ActivityResultContracts.CreateDocument("application/zip")
695 val wasZipCreated = zipSave() 666 ) { result ->
696 val lastZipFile = lastZipCreated 667 if (result == null) {
697 if (!wasZipCreated || lastZipFile == null) { 668 return@registerForActivityResult
698 withContext(Dispatchers.Main) { 669 }
699 Toast.makeText(
700 this@MainActivity,
701 getString(R.string.export_save_failed),
702 Toast.LENGTH_LONG
703 ).show()
704 }
705 return@launch
706 }
707 670
708 withContext(Dispatchers.Main) { 671 IndeterminateProgressDialogFragment.newInstance(
709 val file = DocumentFile.fromSingleUri( 672 this,
710 this@MainActivity, 673 R.string.save_files_exporting,
711 DocumentsContract.buildDocumentUri( 674 false
712 DocumentProvider.AUTHORITY, 675 ) {
713 "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" 676 val zipResult = FileUtil.zipFromInternalStorage(
714 ) 677 File(savesFolderRoot),
715 )!! 678 savesFolderRoot,
716 val intent = Intent(Intent.ACTION_SEND) 679 BufferedOutputStream(contentResolver.openOutputStream(result))
717 .setDataAndType(file.uri, "application/zip") 680 )
718 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 681 return@newInstance when (zipResult) {
719 .putExtra(Intent.EXTRA_STREAM, file.uri) 682 TaskState.Completed -> getString(R.string.export_success)
720 startForResultExportSave.launch( 683 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
721 Intent.createChooser(
722 intent,
723 getString(R.string.share_save_file)
724 )
725 )
726 } 684 }
727 } 685 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
728 } 686 }
729 687
730 private val startForResultExportSave = 688 private val startForResultExportSave =
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
index 5e9a1176a..21270fc84 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
@@ -16,6 +16,7 @@ object DirectoryInitialization {
16 if (!areDirectoriesReady) { 16 if (!areDirectoriesReady) {
17 initializeInternalStorage() 17 initializeInternalStorage()
18 NativeLibrary.initializeSystem(false) 18 NativeLibrary.initializeSystem(false)
19 NativeConfig.initializeConfig()
19 areDirectoriesReady = true 20 areDirectoriesReady = true
20 } 21 }
21 } 22 }
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 8c3268e9c..bbe7bfa92 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
@@ -364,6 +364,27 @@ object FileUtil {
364 .lowercase() 364 .lowercase()
365 } 365 }
366 366
367 fun isTreeUriValid(uri: Uri): Boolean {
368 val resolver = context.contentResolver
369 val columns = arrayOf(
370 DocumentsContract.Document.COLUMN_DOCUMENT_ID,
371 DocumentsContract.Document.COLUMN_DISPLAY_NAME,
372 DocumentsContract.Document.COLUMN_MIME_TYPE
373 )
374 return try {
375 val docId: String = if (isRootTreeUri(uri)) {
376 DocumentsContract.getTreeDocumentId(uri)
377 } else {
378 DocumentsContract.getDocumentId(uri)
379 }
380 val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, docId)
381 resolver.query(childrenUri, columns, null, null, null)
382 true
383 } catch (_: Exception) {
384 false
385 }
386 }
387
367 @Throws(IOException::class) 388 @Throws(IOException::class)
368 fun getStringFromFile(file: File): String = 389 fun getStringFromFile(file: File): String =
369 String(file.readBytes(), StandardCharsets.UTF_8) 390 String(file.readBytes(), StandardCharsets.UTF_8)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index e6aca6b44..55010dc59 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -11,10 +11,11 @@ import kotlinx.serialization.json.Json
11import org.yuzu.yuzu_emu.NativeLibrary 11import org.yuzu.yuzu_emu.NativeLibrary
12import org.yuzu.yuzu_emu.YuzuApplication 12import org.yuzu.yuzu_emu.YuzuApplication
13import org.yuzu.yuzu_emu.model.Game 13import org.yuzu.yuzu_emu.model.Game
14import org.yuzu.yuzu_emu.model.GameDir
14import org.yuzu.yuzu_emu.model.MinimalDocumentFile 15import org.yuzu.yuzu_emu.model.MinimalDocumentFile
15 16
16object GameHelper { 17object GameHelper {
17 const val KEY_GAME_PATH = "game_path" 18 private const val KEY_OLD_GAME_PATH = "game_path"
18 const val KEY_GAMES = "Games" 19 const val KEY_GAMES = "Games"
19 20
20 private lateinit var preferences: SharedPreferences 21 private lateinit var preferences: SharedPreferences
@@ -22,15 +23,43 @@ object GameHelper {
22 fun getGames(): List<Game> { 23 fun getGames(): List<Game> {
23 val games = mutableListOf<Game>() 24 val games = mutableListOf<Game>()
24 val context = YuzuApplication.appContext 25 val context = YuzuApplication.appContext
25 val gamesDir =
26 PreferenceManager.getDefaultSharedPreferences(context).getString(KEY_GAME_PATH, "")
27 val gamesUri = Uri.parse(gamesDir)
28 preferences = PreferenceManager.getDefaultSharedPreferences(context) 26 preferences = PreferenceManager.getDefaultSharedPreferences(context)
29 27
28 val gameDirs = mutableListOf<GameDir>()
29 val oldGamesDir = preferences.getString(KEY_OLD_GAME_PATH, "") ?: ""
30 if (oldGamesDir.isNotEmpty()) {
31 gameDirs.add(GameDir(oldGamesDir, true))
32 preferences.edit().remove(KEY_OLD_GAME_PATH).apply()
33 }
34 gameDirs.addAll(NativeConfig.getGameDirs())
35
30 // Ensure keys are loaded so that ROM metadata can be decrypted. 36 // Ensure keys are loaded so that ROM metadata can be decrypted.
31 NativeLibrary.reloadKeys() 37 NativeLibrary.reloadKeys()
32 38
33 addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3) 39 val badDirs = mutableListOf<Int>()
40 gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
41 val gameDirUri = Uri.parse(gameDir.uriString)
42 val isValid = FileUtil.isTreeUriValid(gameDirUri)
43 if (isValid) {
44 addGamesRecursive(
45 games,
46 FileUtil.listFiles(gameDirUri),
47 if (gameDir.deepScan) 3 else 1
48 )
49 } else {
50 badDirs.add(index)
51 }
52 }
53
54 // Remove all game dirs with insufficient permissions from config
55 if (badDirs.isNotEmpty()) {
56 var offset = 0
57 badDirs.forEach {
58 gameDirs.removeAt(it - offset)
59 offset++
60 }
61 }
62 NativeConfig.setGameDirs(gameDirs.toTypedArray())
34 63
35 // Cache list of games found on disk 64 // Cache list of games found on disk
36 val serializedGames = mutableSetOf<String>() 65 val serializedGames = mutableSetOf<String>()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
index 47bde5081..e63382e1d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
@@ -27,6 +27,8 @@ object InputHandler {
27 0x054C -> getInputDS5ButtonKey(event.keyCode) 27 0x054C -> getInputDS5ButtonKey(event.keyCode)
28 0x057E -> getInputJoyconButtonKey(event.keyCode) 28 0x057E -> getInputJoyconButtonKey(event.keyCode)
29 0x1532 -> getInputRazerButtonKey(event.keyCode) 29 0x1532 -> getInputRazerButtonKey(event.keyCode)
30 0x3537 -> getInputRedmagicButtonKey(event.keyCode)
31 0x358A -> getInputBackboneLabsButtonKey(event.keyCode)
30 else -> getInputGenericButtonKey(event.keyCode) 32 else -> getInputGenericButtonKey(event.keyCode)
31 } 33 }
32 34
@@ -227,6 +229,42 @@ object InputHandler {
227 } 229 }
228 } 230 }
229 231
232 private fun getInputRedmagicButtonKey(key: Int): Int {
233 return when (key) {
234 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
235 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
236 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
237 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
238 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
239 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
240 KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
241 KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
242 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
243 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
244 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
245 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
246 else -> -1
247 }
248 }
249
250 private fun getInputBackboneLabsButtonKey(key: Int): Int {
251 return when (key) {
252 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
253 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
254 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
255 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
256 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
257 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
258 KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
259 KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
260 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
261 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
262 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
263 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
264 else -> -1
265 }
266 }
267
230 private fun getInputGenericButtonKey(key: Int): Int { 268 private fun getInputGenericButtonKey(key: Int): Int {
231 return when (key) { 269 return when (key) {
232 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A 270 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
index 9425f8b99..f4e1bb13f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -3,7 +3,33 @@
3 3
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import org.yuzu.yuzu_emu.model.GameDir
7
6object NativeConfig { 8object NativeConfig {
9 /**
10 * Creates a Config object and opens the emulation config.
11 */
12 @Synchronized
13 external fun initializeConfig()
14
15 /**
16 * Destroys the stored config object. This automatically saves the existing config.
17 */
18 @Synchronized
19 external fun unloadConfig()
20
21 /**
22 * Reads values saved to the config file and saves them.
23 */
24 @Synchronized
25 external fun reloadSettings()
26
27 /**
28 * Saves settings values in memory to disk.
29 */
30 @Synchronized
31 external fun saveSettings()
32
7 external fun getBoolean(key: String, getDefault: Boolean): Boolean 33 external fun getBoolean(key: String, getDefault: Boolean): Boolean
8 external fun setBoolean(key: String, value: Boolean) 34 external fun setBoolean(key: String, value: Boolean)
9 35
@@ -30,4 +56,22 @@ object NativeConfig {
30 external fun getConfigHeader(category: Int): String 56 external fun getConfigHeader(category: Int): String
31 57
32 external fun getPairedSettingKey(key: String): String 58 external fun getPairedSettingKey(key: String): String
59
60 /**
61 * Gets every [GameDir] in AndroidSettings::values.game_dirs
62 */
63 @Synchronized
64 external fun getGameDirs(): Array<GameDir>
65
66 /**
67 * Clears the AndroidSettings::values.game_dirs array and replaces them with the provided array
68 */
69 @Synchronized
70 external fun setGameDirs(dirs: Array<GameDir>)
71
72 /**
73 * Adds a single [GameDir] to the AndroidSettings::values.game_dirs array
74 */
75 @Synchronized
76 external fun addGameDir(dir: GameDir)
33} 77}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 88a570f68..2acc93da8 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -6,9 +6,6 @@ add_library(yuzu-android SHARED
6 android_common/android_common.h 6 android_common/android_common.h
7 applets/software_keyboard.cpp 7 applets/software_keyboard.cpp
8 applets/software_keyboard.h 8 applets/software_keyboard.h
9 config.cpp
10 config.h
11 default_ini.h
12 emu_window/emu_window.cpp 9 emu_window/emu_window.cpp
13 emu_window/emu_window.h 10 emu_window/emu_window.h
14 id_cache.cpp 11 id_cache.cpp
@@ -16,15 +13,17 @@ add_library(yuzu-android SHARED
16 native.cpp 13 native.cpp
17 native.h 14 native.h
18 native_config.cpp 15 native_config.cpp
19 uisettings.cpp 16 android_settings.cpp
20 game_metadata.cpp 17 game_metadata.cpp
21 native_log.cpp 18 native_log.cpp
19 android_config.cpp
20 android_config.h
22) 21)
23 22
24set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) 23set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
25 24
26target_link_libraries(yuzu-android PRIVATE audio_core common core input_common) 25target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common)
27target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log) 26target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
28if (ARCHITECTURE_arm64) 27if (ARCHITECTURE_arm64)
29 target_link_libraries(yuzu-android PRIVATE adrenotools) 28 target_link_libraries(yuzu-android PRIVATE adrenotools)
30endif() 29endif()
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
new file mode 100644
index 000000000..767d8ea83
--- /dev/null
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -0,0 +1,120 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "android_config.h"
5#include "android_settings.h"
6#include "common/settings_setting.h"
7
8AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type)
9 : Config(config_type) {
10 Initialize(config_name);
11 if (config_type != ConfigType::InputProfile) {
12 ReadAndroidValues();
13 SaveAndroidValues();
14 }
15}
16
17AndroidConfig::~AndroidConfig() {
18 if (global) {
19 AndroidConfig::SaveAllValues();
20 }
21}
22
23void AndroidConfig::ReloadAllValues() {
24 Reload();
25 ReadAndroidValues();
26 SaveAndroidValues();
27}
28
29void AndroidConfig::SaveAllValues() {
30 Save();
31 SaveAndroidValues();
32}
33
34void AndroidConfig::ReadAndroidValues() {
35 if (global) {
36 ReadAndroidUIValues();
37 ReadUIValues();
38 }
39}
40
41void AndroidConfig::ReadAndroidUIValues() {
42 BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
43
44 ReadCategory(Settings::Category::Android);
45
46 EndGroup();
47}
48
49void AndroidConfig::ReadUIValues() {
50 BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
51
52 ReadPathValues();
53
54 EndGroup();
55}
56
57void AndroidConfig::ReadPathValues() {
58 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
59
60 const int gamedirs_size = BeginArray(std::string("gamedirs"));
61 for (int i = 0; i < gamedirs_size; ++i) {
62 SetArrayIndex(i);
63 AndroidSettings::GameDir game_dir;
64 game_dir.path = ReadStringSetting(std::string("path"));
65 game_dir.deep_scan =
66 ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false));
67 AndroidSettings::values.game_dirs.push_back(game_dir);
68 }
69 EndArray();
70
71 EndGroup();
72}
73
74void AndroidConfig::SaveAndroidValues() {
75 if (global) {
76 SaveAndroidUIValues();
77 SaveUIValues();
78 }
79
80 WriteToIni();
81}
82
83void AndroidConfig::SaveAndroidUIValues() {
84 BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
85
86 WriteCategory(Settings::Category::Android);
87
88 EndGroup();
89}
90
91void AndroidConfig::SaveUIValues() {
92 BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
93
94 SavePathValues();
95
96 EndGroup();
97}
98
99void AndroidConfig::SavePathValues() {
100 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
101
102 BeginArray(std::string("gamedirs"));
103 for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
104 SetArrayIndex(i);
105 const auto& game_dir = AndroidSettings::values.game_dirs[i];
106 WriteSetting(std::string("path"), game_dir.path);
107 WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false));
108 }
109 EndArray();
110
111 EndGroup();
112}
113
114std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
115 auto& map = Settings::values.linkage.by_category;
116 if (map.contains(category)) {
117 return Settings::values.linkage.by_category[category];
118 }
119 return AndroidSettings::values.linkage.by_category[category];
120}
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h
new file mode 100644
index 000000000..f490be016
--- /dev/null
+++ b/src/android/app/src/main/jni/android_config.h
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "frontend_common/config.h"
7
8class AndroidConfig final : public Config {
9public:
10 explicit AndroidConfig(const std::string& config_name = "config",
11 ConfigType config_type = ConfigType::GlobalConfig);
12 ~AndroidConfig() override;
13
14 void ReloadAllValues() override;
15 void SaveAllValues() override;
16
17protected:
18 void ReadAndroidValues();
19 void ReadAndroidUIValues();
20 void ReadHidbusValues() override {}
21 void ReadDebugControlValues() override {}
22 void ReadPathValues() override;
23 void ReadShortcutValues() override {}
24 void ReadUIValues() override;
25 void ReadUIGamelistValues() override {}
26 void ReadUILayoutValues() override {}
27 void ReadMultiplayerValues() override {}
28
29 void SaveAndroidValues();
30 void SaveAndroidUIValues();
31 void SaveHidbusValues() override {}
32 void SaveDebugControlValues() override {}
33 void SavePathValues() override;
34 void SaveShortcutValues() override {}
35 void SaveUIValues() override;
36 void SaveUIGamelistValues() override {}
37 void SaveUILayoutValues() override {}
38 void SaveMultiplayerValues() override {}
39
40 std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
41};
diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/android_settings.cpp
index f2f0bad50..16023a6b0 100644
--- a/src/android/app/src/main/jni/uisettings.cpp
+++ b/src/android/app/src/main/jni/android_settings.cpp
@@ -1,7 +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 "uisettings.h" 4#include "android_settings.h"
5 5
6namespace AndroidSettings { 6namespace AndroidSettings {
7 7
diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/android_settings.h
index 37bc33918..fc0523206 100644
--- a/src/android/app/src/main/jni/uisettings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -9,9 +9,17 @@
9 9
10namespace AndroidSettings { 10namespace AndroidSettings {
11 11
12struct GameDir {
13 std::string path;
14 bool deep_scan = false;
15};
16
12struct Values { 17struct Values {
13 Settings::Linkage linkage; 18 Settings::Linkage linkage;
14 19
20 // Path settings
21 std::vector<GameDir> game_dirs;
22
15 // Android 23 // Android
16 Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture", 24 Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture",
17 Settings::Category::Android}; 25 Settings::Category::Android};
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
deleted file mode 100644
index 81120ab0f..000000000
--- a/src/android/app/src/main/jni/config.cpp
+++ /dev/null
@@ -1,330 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <memory>
5#include <optional>
6#include <sstream>
7
8#include <INIReader.h>
9#include "common/fs/file.h"
10#include "common/fs/fs.h"
11#include "common/fs/path_util.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "common/settings_enums.h"
15#include "core/hle/service/acc/profile_manager.h"
16#include "input_common/main.h"
17#include "jni/config.h"
18#include "jni/default_ini.h"
19#include "uisettings.h"
20
21namespace FS = Common::FS;
22
23Config::Config(const std::string& config_name, ConfigType config_type)
24 : type(config_type), global{config_type == ConfigType::GlobalConfig} {
25 Initialize(config_name);
26}
27
28Config::~Config() = default;
29
30bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 void(FS::CreateParentDir(config_loc));
32 config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
33 const auto config_loc_str = FS::PathToUTF8String(config_loc);
34 if (config->ParseError() < 0) {
35 if (retry) {
36 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
37 config_loc_str);
38
39 void(FS::CreateParentDir(config_loc));
40 void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents));
41
42 config = std::make_unique<INIReader>(config_loc_str);
43
44 return LoadINI(default_contents, false);
45 }
46 LOG_ERROR(Config, "Failed.");
47 return false;
48 }
49 LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
50 return true;
51}
52
53template <>
54void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
55 std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault());
56 if (setting_value.empty()) {
57 setting_value = setting.GetDefault();
58 }
59 setting = std::move(setting_value);
60}
61
62template <>
63void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
64 setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
65}
66
67template <typename Type, bool ranged>
68void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
69 setting = static_cast<Type>(
70 config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault())));
71}
72
73void Config::ReadValues() {
74 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
75 ReadSetting("ControlsGeneral", Settings::values.touch_device);
76 ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
77 ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
78 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
79 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
80 ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
81 Settings::values.touchscreen.enabled =
82 config->GetBoolean("ControlsGeneral", "touch_enabled", true);
83 Settings::values.touchscreen.rotation_angle =
84 config->GetInteger("ControlsGeneral", "touch_angle", 0);
85 Settings::values.touchscreen.diameter_x =
86 config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
87 Settings::values.touchscreen.diameter_y =
88 config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
89
90 int num_touch_from_button_maps =
91 config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
92 if (num_touch_from_button_maps > 0) {
93 for (int i = 0; i < num_touch_from_button_maps; ++i) {
94 Settings::TouchFromButtonMap map;
95 map.name = config->Get("ControlsGeneral",
96 std::string("touch_from_button_maps_") + std::to_string(i) +
97 std::string("_name"),
98 "default");
99 const int num_touch_maps = config->GetInteger(
100 "ControlsGeneral",
101 std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
102 0);
103 map.buttons.reserve(num_touch_maps);
104
105 for (int j = 0; j < num_touch_maps; ++j) {
106 std::string touch_mapping =
107 config->Get("ControlsGeneral",
108 std::string("touch_from_button_maps_") + std::to_string(i) +
109 std::string("_bind_") + std::to_string(j),
110 "");
111 map.buttons.emplace_back(std::move(touch_mapping));
112 }
113
114 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
115 }
116 } else {
117 Settings::values.touch_from_button_maps.emplace_back(
118 Settings::TouchFromButtonMap{"default", {}});
119 num_touch_from_button_maps = 1;
120 }
121 Settings::values.touch_from_button_map_index = std::clamp(
122 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
123
124 ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
125
126 // Data Storage
127 ReadSetting("Data Storage", Settings::values.use_virtual_sd);
128 FS::SetYuzuPath(FS::YuzuPath::NANDDir,
129 config->Get("Data Storage", "nand_directory",
130 FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
131 FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
132 config->Get("Data Storage", "sdmc_directory",
133 FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
134 FS::SetYuzuPath(FS::YuzuPath::LoadDir,
135 config->Get("Data Storage", "load_directory",
136 FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
137 FS::SetYuzuPath(FS::YuzuPath::DumpDir,
138 config->Get("Data Storage", "dump_directory",
139 FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
140 ReadSetting("Data Storage", Settings::values.gamecard_inserted);
141 ReadSetting("Data Storage", Settings::values.gamecard_current_game);
142 ReadSetting("Data Storage", Settings::values.gamecard_path);
143
144 // System
145 ReadSetting("System", Settings::values.current_user);
146 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
147 Service::Account::MAX_USERS - 1);
148
149 // Disable docked mode by default on Android
150 Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
151 ? Settings::ConsoleMode::Docked
152 : Settings::ConsoleMode::Handheld);
153
154 const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
155 if (rng_seed_enabled) {
156 Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
157 } else {
158 Settings::values.rng_seed.SetValue(0);
159 }
160 Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled);
161
162 const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
163 if (custom_rtc_enabled) {
164 Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
165 } else {
166 Settings::values.custom_rtc = 0;
167 }
168 Settings::values.custom_rtc_enabled = custom_rtc_enabled;
169
170 ReadSetting("System", Settings::values.language_index);
171 ReadSetting("System", Settings::values.region_index);
172 ReadSetting("System", Settings::values.time_zone_index);
173 ReadSetting("System", Settings::values.sound_index);
174
175 // Core
176 ReadSetting("Core", Settings::values.use_multi_core);
177 ReadSetting("Core", Settings::values.memory_layout_mode);
178
179 // Cpu
180 ReadSetting("Cpu", Settings::values.cpu_accuracy);
181 ReadSetting("Cpu", Settings::values.cpu_debug_mode);
182 ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
183 ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
184 ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
185 ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
186 ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
187 ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
188 ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
189 ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
190 ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
191 ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
192 ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
193 ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
194 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
195 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
196 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
197 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
198 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
199 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
200
201 // Renderer
202 ReadSetting("Renderer", Settings::values.renderer_backend);
203 ReadSetting("Renderer", Settings::values.renderer_debug);
204 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
205 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
206 ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
207 ReadSetting("Renderer", Settings::values.vulkan_device);
208
209 ReadSetting("Renderer", Settings::values.resolution_setup);
210 ReadSetting("Renderer", Settings::values.scaling_filter);
211 ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
212 ReadSetting("Renderer", Settings::values.anti_aliasing);
213 ReadSetting("Renderer", Settings::values.fullscreen_mode);
214 ReadSetting("Renderer", Settings::values.aspect_ratio);
215 ReadSetting("Renderer", Settings::values.max_anisotropy);
216 ReadSetting("Renderer", Settings::values.use_speed_limit);
217 ReadSetting("Renderer", Settings::values.speed_limit);
218 ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
219 ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
220 ReadSetting("Renderer", Settings::values.vsync_mode);
221 ReadSetting("Renderer", Settings::values.shader_backend);
222 ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
223 ReadSetting("Renderer", Settings::values.nvdec_emulation);
224 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
225 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
226
227 ReadSetting("Renderer", Settings::values.bg_red);
228 ReadSetting("Renderer", Settings::values.bg_green);
229 ReadSetting("Renderer", Settings::values.bg_blue);
230
231 // Use GPU accuracy normal by default on Android
232 Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger(
233 "Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal)));
234
235 // Use GPU default anisotropic filtering on Android
236 Settings::values.max_anisotropy =
237 static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1));
238
239 // Disable ASTC compute by default on Android
240 Settings::values.accelerate_astc.SetValue(
241 config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu
242 : Settings::AstcDecodeMode::Cpu);
243
244 // Enable asynchronous presentation by default on Android
245 Settings::values.async_presentation =
246 config->GetBoolean("Renderer", "async_presentation", true);
247
248 // Disable force_max_clock by default on Android
249 Settings::values.renderer_force_max_clock =
250 config->GetBoolean("Renderer", "force_max_clock", false);
251
252 // Disable use_reactive_flushing by default on Android
253 Settings::values.use_reactive_flushing =
254 config->GetBoolean("Renderer", "use_reactive_flushing", false);
255
256 // Audio
257 ReadSetting("Audio", Settings::values.sink_id);
258 ReadSetting("Audio", Settings::values.audio_output_device_id);
259 ReadSetting("Audio", Settings::values.volume);
260
261 // Miscellaneous
262 // log_filter has a different default here than from common
263 Settings::values.log_filter = "*:Info";
264 ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
265
266 // Debugging
267 Settings::values.record_frame_times =
268 config->GetBoolean("Debugging", "record_frame_times", false);
269 ReadSetting("Debugging", Settings::values.dump_exefs);
270 ReadSetting("Debugging", Settings::values.dump_nso);
271 ReadSetting("Debugging", Settings::values.enable_fs_access_log);
272 ReadSetting("Debugging", Settings::values.reporting_services);
273 ReadSetting("Debugging", Settings::values.quest_flag);
274 ReadSetting("Debugging", Settings::values.use_debug_asserts);
275 ReadSetting("Debugging", Settings::values.use_auto_stub);
276 ReadSetting("Debugging", Settings::values.disable_macro_jit);
277 ReadSetting("Debugging", Settings::values.disable_macro_hle);
278 ReadSetting("Debugging", Settings::values.use_gdbstub);
279 ReadSetting("Debugging", Settings::values.gdbstub_port);
280
281 const auto title_list = config->Get("AddOns", "title_ids", "");
282 std::stringstream ss(title_list);
283 std::string line;
284 while (std::getline(ss, line, '|')) {
285 const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
286 const auto disabled_list = config->Get("AddOns", "disabled_" + line, "");
287
288 std::stringstream inner_ss(disabled_list);
289 std::string inner_line;
290 std::vector<std::string> out;
291 while (std::getline(inner_ss, inner_line, '|')) {
292 out.push_back(inner_line);
293 }
294
295 Settings::values.disabled_addons.insert_or_assign(title_id, out);
296 }
297
298 // Web Service
299 ReadSetting("WebService", Settings::values.enable_telemetry);
300 ReadSetting("WebService", Settings::values.web_api_url);
301 ReadSetting("WebService", Settings::values.yuzu_username);
302 ReadSetting("WebService", Settings::values.yuzu_token);
303
304 // Network
305 ReadSetting("Network", Settings::values.network_interface);
306
307 // Android
308 ReadSetting("Android", AndroidSettings::values.picture_in_picture);
309 ReadSetting("Android", AndroidSettings::values.screen_layout);
310}
311
312void Config::Initialize(const std::string& config_name) {
313 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
314 const auto config_file = fmt::format("{}.ini", config_name);
315
316 switch (type) {
317 case ConfigType::GlobalConfig:
318 config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
319 break;
320 case ConfigType::PerGameConfig:
321 config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
322 break;
323 case ConfigType::InputProfile:
324 config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
325 LoadINI(DefaultINI::android_config_file);
326 return;
327 }
328 LoadINI(DefaultINI::android_config_file);
329 ReadValues();
330}
diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h
deleted file mode 100644
index e1e8f47ed..000000000
--- a/src/android/app/src/main/jni/config.h
+++ /dev/null
@@ -1,47 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <filesystem>
7#include <memory>
8#include <optional>
9#include <string>
10
11#include "common/settings.h"
12
13class INIReader;
14
15class Config {
16 bool LoadINI(const std::string& default_contents = "", bool retry = true);
17
18public:
19 enum class ConfigType {
20 GlobalConfig,
21 PerGameConfig,
22 InputProfile,
23 };
24
25 explicit Config(const std::string& config_name = "config",
26 ConfigType config_type = ConfigType::GlobalConfig);
27 ~Config();
28
29 void Initialize(const std::string& config_name);
30
31private:
32 /**
33 * Applies a value read from the config to a Setting.
34 *
35 * @param group The name of the INI group
36 * @param setting The yuzu setting to modify
37 */
38 template <typename Type, bool ranged>
39 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
40
41 void ReadValues();
42
43 const ConfigType type;
44 std::unique_ptr<INIReader> config;
45 std::string config_loc;
46 const bool global;
47};
diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h
deleted file mode 100644
index d81422a74..000000000
--- a/src/android/app/src/main/jni/default_ini.h
+++ /dev/null
@@ -1,511 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace DefaultINI {
7
8const char* android_config_file = R"(
9
10[ControlsP0]
11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
13# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
15
16# Indicates if this player should be connected at boot
17connected=
18
19# for button input, the following devices are available:
20# - "keyboard" (default) for keyboard input. Required parameters:
21# - "code": the code of the key to bind
22# - "sdl" for joystick input using SDL. Required parameters:
23# - "guid": SDL identification GUID of the joystick
24# - "port": the index of the joystick to bind
25# - "button"(optional): the index of the button to bind
26# - "hat"(optional): the index of the hat to bind as direction buttons
27# - "axis"(optional): the index of the axis to bind
28# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
29# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
30# triggered if the axis value crosses
31# - "direction"(only used for axis): "+" means the button is triggered when the axis value
32# is greater than the threshold; "-" means the button is triggered when the axis value
33# is smaller than the threshold
34button_a=
35button_b=
36button_x=
37button_y=
38button_lstick=
39button_rstick=
40button_l=
41button_r=
42button_zl=
43button_zr=
44button_plus=
45button_minus=
46button_dleft=
47button_dup=
48button_dright=
49button_ddown=
50button_lstick_left=
51button_lstick_up=
52button_lstick_right=
53button_lstick_down=
54button_sl=
55button_sr=
56button_home=
57button_screenshot=
58
59# for analog input, the following devices are available:
60# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
61# - "up", "down", "left", "right": sub-devices for each direction.
62# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
63# - "modifier": sub-devices as a modifier.
64# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
65# Must be in range of 0.0-1.0. Defaults to 0.5
66# - "sdl" for joystick input using SDL. Required parameters:
67# - "guid": SDL identification GUID of the joystick
68# - "port": the index of the joystick to bind
69# - "axis_x": the index of the axis to bind as x-axis (default to 0)
70# - "axis_y": the index of the axis to bind as y-axis (default to 1)
71lstick=
72rstick=
73
74# for motion input, the following devices are available:
75# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
76# - "code": the code of the key to bind
77# - "sdl" for motion input using SDL. Required parameters:
78# - "guid": SDL identification GUID of the joystick
79# - "port": the index of the joystick to bind
80# - "motion": the index of the motion sensor to bind
81# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
82# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
83# - "port": the port of the cemu hook server
84# - "pad": the index of the joystick
85# - "motion": the index of the motion sensor of the joystick to bind
86motionleft=
87motionright=
88
89[ControlsGeneral]
90# To use the debug_pad, prepend `debug_pad_` before each button setting above.
91# i.e. debug_pad_button_a=
92
93# Enable debug pad inputs to the guest
94# 0 (default): Disabled, 1: Enabled
95debug_pad_enabled =
96
97# Whether to enable or disable vibration
98# 0: Disabled, 1 (default): Enabled
99vibration_enabled=
100
101# Whether to enable or disable accurate vibrations
102# 0 (default): Disabled, 1: Enabled
103enable_accurate_vibrations=
104
105# Enables controller motion inputs
106# 0: Disabled, 1 (default): Enabled
107motion_enabled =
108
109# Defines the udp device's touch screen coordinate system for cemuhookudp devices
110# - "min_x", "min_y", "max_x", "max_y"
111touch_device=
112
113# for mapping buttons to touch inputs.
114#touch_from_button_map=1
115#touch_from_button_maps_0_name=default
116#touch_from_button_maps_0_count=2
117#touch_from_button_maps_0_bind_0=foo
118#touch_from_button_maps_0_bind_1=bar
119# etc.
120
121# List of Cemuhook UDP servers, delimited by ','.
122# Default: 127.0.0.1:26760
123# Example: 127.0.0.1:26760,123.4.5.67:26761
124udp_input_servers =
125
126# Enable controlling an axis via a mouse input.
127# 0 (default): Off, 1: On
128mouse_panning =
129
130# Set mouse sensitivity.
131# Default: 1.0
132mouse_panning_sensitivity =
133
134# Emulate an analog control stick from keyboard inputs.
135# 0 (default): Disabled, 1: Enabled
136emulate_analog_keyboard =
137
138# Enable mouse inputs to the guest
139# 0 (default): Disabled, 1: Enabled
140mouse_enabled =
141
142# Enable keyboard inputs to the guest
143# 0 (default): Disabled, 1: Enabled
144keyboard_enabled =
145
146[Core]
147# Whether to use multi-core for CPU emulation
148# 0: Disabled, 1 (default): Enabled
149use_multi_core =
150
151# Enable unsafe extended guest system memory layout (8GB DRAM)
152# 0 (default): Disabled, 1: Enabled
153use_unsafe_extended_memory_layout =
154
155[Cpu]
156# Adjusts various optimizations.
157# Auto-select mode enables choice unsafe optimizations.
158# Accurate enables only safe optimizations.
159# Unsafe allows any unsafe optimizations.
160# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
161cpu_accuracy =
162
163# Allow disabling safe optimizations.
164# 0 (default): Disabled, 1: Enabled
165cpu_debug_mode =
166
167# Enable inline page tables optimization (faster guest memory access)
168# 0: Disabled, 1 (default): Enabled
169cpuopt_page_tables =
170
171# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
172# 0: Disabled, 1 (default): Enabled
173cpuopt_block_linking =
174
175# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
176# 0: Disabled, 1 (default): Enabled
177cpuopt_return_stack_buffer =
178
179# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
180# 0: Disabled, 1 (default): Enabled
181cpuopt_fast_dispatcher =
182
183# Enable context elimination CPU Optimization (reduce host memory use for guest context)
184# 0: Disabled, 1 (default): Enabled
185cpuopt_context_elimination =
186
187# Enable constant propagation CPU optimization (basic IR optimization)
188# 0: Disabled, 1 (default): Enabled
189cpuopt_const_prop =
190
191# Enable miscellaneous CPU optimizations (basic IR optimization)
192# 0: Disabled, 1 (default): Enabled
193cpuopt_misc_ir =
194
195# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
196# 0: Disabled, 1 (default): Enabled
197cpuopt_reduce_misalign_checks =
198
199# Enable Host MMU Emulation (faster guest memory access)
200# 0: Disabled, 1 (default): Enabled
201cpuopt_fastmem =
202
203# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
204# 0: Disabled, 1 (default): Enabled
205cpuopt_fastmem_exclusives =
206
207# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
208# 0: Disabled, 1 (default): Enabled
209cpuopt_recompile_exclusives =
210
211# Enable optimization to ignore invalid memory accesses (faster guest memory access)
212# 0: Disabled, 1 (default): Enabled
213cpuopt_ignore_memory_aborts =
214
215# Enable unfuse FMA (improve performance on CPUs without FMA)
216# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
217# 0: Disabled, 1 (default): Enabled
218cpuopt_unsafe_unfuse_fma =
219
220# Enable faster FRSQRTE and FRECPE
221# Only enabled if cpu_accuracy is set to Unsafe.
222# 0: Disabled, 1 (default): Enabled
223cpuopt_unsafe_reduce_fp_error =
224
225# Enable faster ASIMD instructions (32 bits only)
226# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
227# 0: Disabled, 1 (default): Enabled
228cpuopt_unsafe_ignore_standard_fpcr =
229
230# Enable inaccurate NaN handling
231# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
232# 0: Disabled, 1 (default): Enabled
233cpuopt_unsafe_inaccurate_nan =
234
235# Disable address space checks (64 bits only)
236# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
237# 0: Disabled, 1 (default): Enabled
238cpuopt_unsafe_fastmem_check =
239
240# Enable faster exclusive instructions
241# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
242# 0: Disabled, 1 (default): Enabled
243cpuopt_unsafe_ignore_global_monitor =
244
245[Renderer]
246# Which backend API to use.
247# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null
248backend =
249
250# Whether to enable asynchronous presentation (Vulkan only)
251# 0: Off, 1 (default): On
252async_presentation =
253
254# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).
255# 0 (default): Disabled, 1: Enabled
256force_max_clock =
257
258# Enable graphics API debugging mode.
259# 0 (default): Disabled, 1: Enabled
260debug =
261
262# Enable shader feedback.
263# 0 (default): Disabled, 1: Enabled
264renderer_shader_feedback =
265
266# Enable Nsight Aftermath crash dumps
267# 0 (default): Disabled, 1: Enabled
268nsight_aftermath =
269
270# Disable shader loop safety checks, executing the shader without loop logic changes
271# 0 (default): Disabled, 1: Enabled
272disable_shader_loop_safety_checks =
273
274# Which Vulkan physical device to use (defaults to 0)
275vulkan_device =
276
277# 0: 0.5x (360p/540p) [EXPERIMENTAL]
278# 1: 0.75x (540p/810p) [EXPERIMENTAL]
279# 2 (default): 1x (720p/1080p)
280# 3: 2x (1440p/2160p)
281# 4: 3x (2160p/3240p)
282# 5: 4x (2880p/4320p)
283# 6: 5x (3600p/5400p)
284# 7: 6x (4320p/6480p)
285resolution_setup =
286
287# Pixel filter to use when up- or down-sampling rendered frames.
288# 0: Nearest Neighbor
289# 1 (default): Bilinear
290# 2: Bicubic
291# 3: Gaussian
292# 4: ScaleForce
293# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only]
294scaling_filter =
295
296# Anti-Aliasing (AA)
297# 0 (default): None, 1: FXAA
298anti_aliasing =
299
300# Whether to use fullscreen or borderless window mode
301# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
302fullscreen_mode =
303
304# Aspect ratio
305# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
306aspect_ratio =
307
308# Anisotropic filtering
309# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
310max_anisotropy =
311
312# Whether to enable VSync or not.
313# OpenGL: Values other than 0 enable VSync
314# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
315# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
316# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
317# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
318# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
319# 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed
320use_vsync =
321
322# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
323# not available and GLASM is selected, GLSL will be used.
324# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
325shader_backend =
326
327# Whether to allow asynchronous shader building.
328# 0 (default): Off, 1: On
329use_asynchronous_shaders =
330
331# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
332# 0 (default): Off, 1: On
333use_reactive_flushing =
334
335# NVDEC emulation.
336# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
337nvdec_emulation =
338
339# Accelerate ASTC texture decoding.
340# 0 (default): Off, 1: On
341accelerate_astc =
342
343# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
344# 0: Off, 1: On (default)
345use_speed_limit =
346
347# Limits the speed of the game to run no faster than this value as a percentage of target speed
348# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
349speed_limit =
350
351# Whether to use disk based shader cache
352# 0: Off, 1 (default): On
353use_disk_shader_cache =
354
355# Which gpu accuracy level to use
356# 0 (default): Normal, 1: High, 2: Extreme (Very slow)
357gpu_accuracy =
358
359# Whether to use asynchronous GPU emulation
360# 0 : Off (slow), 1 (default): On (fast)
361use_asynchronous_gpu_emulation =
362
363# Inform the guest that GPU operations completed more quickly than they did.
364# 0: Off, 1 (default): On
365use_fast_gpu_time =
366
367# Force unmodified buffers to be flushed, which can cost performance.
368# 0: Off (default), 1: On
369use_pessimistic_flushes =
370
371# Whether to use garbage collection or not for GPU caches.
372# 0 (default): Off, 1: On
373use_caches_gc =
374
375# The clear color for the renderer. What shows up on the sides of the bottom screen.
376# Must be in range of 0-255. Defaults to 0 for all.
377bg_red =
378bg_blue =
379bg_green =
380
381[Audio]
382# Which audio output engine to use.
383# auto (default): Auto-select
384# cubeb: Cubeb audio engine (if available)
385# sdl2: SDL2 audio engine (if available)
386# null: No audio output
387output_engine =
388
389# Which audio device to use.
390# auto (default): Auto-select
391output_device =
392
393# Output volume.
394# 100 (default): 100%, 0; mute
395volume =
396
397[Data Storage]
398# Whether to create a virtual SD card.
399# 1: Yes, 0 (default): No
400use_virtual_sd =
401
402# Whether or not to enable gamecard emulation
403# 1: Yes, 0 (default): No
404gamecard_inserted =
405
406# Whether or not the gamecard should be emulated as the current game
407# If 'gamecard_inserted' is 0 this setting is irrelevant
408# 1: Yes, 0 (default): No
409gamecard_current_game =
410
411# Path to an XCI file to use as the gamecard
412# If 'gamecard_inserted' is 0 this setting is irrelevant
413# If 'gamecard_current_game' is 1 this setting is irrelevant
414gamecard_path =
415
416[System]
417# Whether the system is docked
418# 1 (default): Yes, 0: No
419use_docked_mode =
420
421# Sets the seed for the RNG generator built into the switch
422# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
423rng_seed_enabled =
424rng_seed =
425
426# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
427# This will auto-increment, with the time set being the time the game is started
428# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
429custom_rtc_enabled =
430custom_rtc =
431
432# Sets the systems language index
433# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
434# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
435# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
436language_index =
437
438# The system region that yuzu will use during emulation
439# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
440region_index =
441
442# The system time zone that yuzu will use during emulation
443# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
444time_zone_index =
445
446# Sets the sound output mode.
447# 0: Mono, 1 (default): Stereo, 2: Surround
448sound_index =
449
450[Miscellaneous]
451# A filter which removes logs below a certain logging level.
452# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
453log_filter = *:Trace
454
455# Use developer keys
456# 0 (default): Disabled, 1: Enabled
457use_dev_keys =
458
459[Debugging]
460# Record frame time data, can be found in the log directory. Boolean value
461record_frame_times =
462# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
463dump_exefs=false
464# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
465dump_nso=false
466# Determines whether or not yuzu will save the filesystem access log.
467enable_fs_access_log=false
468# Enables verbose reporting services
469reporting_services =
470# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
471# false: Retail/Normal Mode (default), true: Kiosk Mode
472quest_flag =
473# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
474# false: Disabled (default), true: Enabled
475use_debug_asserts =
476# Determines whether unimplemented HLE service calls should be automatically stubbed.
477# false: Disabled (default), true: Enabled
478use_auto_stub =
479# Enables/Disables the macro JIT compiler
480disable_macro_jit=false
481# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
482# false: Disabled (default), true: Enabled
483use_gdbstub=false
484# The port to use for the GDB server, if it is enabled.
485gdbstub_port=6543
486
487[WebService]
488# Whether or not to enable telemetry
489# 0: No, 1 (default): Yes
490enable_telemetry =
491# URL for Web API
492web_api_url = https://api.yuzu-emu.org
493# Username and token for yuzu Web Service
494# See https://profile.yuzu-emu.org/ for more info
495yuzu_username =
496yuzu_token =
497
498[Network]
499# Name of the network interface device to use with yuzu LAN play.
500# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
501# e.g. On Windows: 'Ethernet', 'Wi-Fi'
502network_interface =
503
504[AddOns]
505# Used to disable add-ons
506# List of title IDs of games that will have add-ons disabled (separated by '|'):
507title_ids =
508# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
509# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
510)";
511} // namespace DefaultINI
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index 960abf95a..a56ed5662 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -13,6 +13,8 @@ static JavaVM* s_java_vm;
13static jclass s_native_library_class; 13static jclass s_native_library_class;
14static jclass s_disk_cache_progress_class; 14static jclass s_disk_cache_progress_class;
15static jclass s_load_callback_stage_class; 15static jclass s_load_callback_stage_class;
16static jclass s_game_dir_class;
17static jmethodID s_game_dir_constructor;
16static jmethodID s_exit_emulation_activity; 18static jmethodID s_exit_emulation_activity;
17static jmethodID s_disk_cache_load_progress; 19static jmethodID s_disk_cache_load_progress;
18static jmethodID s_on_emulation_started; 20static jmethodID s_on_emulation_started;
@@ -53,6 +55,14 @@ jclass GetDiskCacheLoadCallbackStageClass() {
53 return s_load_callback_stage_class; 55 return s_load_callback_stage_class;
54} 56}
55 57
58jclass GetGameDirClass() {
59 return s_game_dir_class;
60}
61
62jmethodID GetGameDirConstructor() {
63 return s_game_dir_constructor;
64}
65
56jmethodID GetExitEmulationActivity() { 66jmethodID GetExitEmulationActivity() {
57 return s_exit_emulation_activity; 67 return s_exit_emulation_activity;
58} 68}
@@ -90,6 +100,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
90 s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass( 100 s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
91 "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); 101 "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
92 102
103 const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
104 s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
105 s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>", "(Ljava/lang/String;Z)V");
106 env->DeleteLocalRef(game_dir_class);
107
93 // Initialize methods 108 // Initialize methods
94 s_exit_emulation_activity = 109 s_exit_emulation_activity =
95 env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); 110 env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
@@ -120,6 +135,7 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
120 env->DeleteGlobalRef(s_native_library_class); 135 env->DeleteGlobalRef(s_native_library_class);
121 env->DeleteGlobalRef(s_disk_cache_progress_class); 136 env->DeleteGlobalRef(s_disk_cache_progress_class);
122 env->DeleteGlobalRef(s_load_callback_stage_class); 137 env->DeleteGlobalRef(s_load_callback_stage_class);
138 env->DeleteGlobalRef(s_game_dir_class);
123 139
124 // UnInitialize applets 140 // UnInitialize applets
125 SoftwareKeyboard::CleanupJNI(env); 141 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 b76158928..855649efa 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -13,6 +13,8 @@ JNIEnv* GetEnvForThread();
13jclass GetNativeLibraryClass(); 13jclass GetNativeLibraryClass();
14jclass GetDiskCacheProgressClass(); 14jclass GetDiskCacheProgressClass();
15jclass GetDiskCacheLoadCallbackStageClass(); 15jclass GetDiskCacheLoadCallbackStageClass();
16jclass GetGameDirClass();
17jmethodID GetGameDirConstructor();
16jmethodID GetExitEmulationActivity(); 18jmethodID GetExitEmulationActivity();
17jmethodID GetDiskCacheLoadProgress(); 19jmethodID GetDiskCacheLoadProgress();
18jmethodID GetOnEmulationStarted(); 20jmethodID GetOnEmulationStarted();
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 64663b084..3d795b57f 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -52,8 +52,8 @@
52#include "core/hle/service/am/applets/applets.h" 52#include "core/hle/service/am/applets/applets.h"
53#include "core/hle/service/filesystem/filesystem.h" 53#include "core/hle/service/filesystem/filesystem.h"
54#include "core/loader/loader.h" 54#include "core/loader/loader.h"
55#include "frontend_common/config.h"
55#include "jni/android_common/android_common.h" 56#include "jni/android_common/android_common.h"
56#include "jni/config.h"
57#include "jni/id_cache.h" 57#include "jni/id_cache.h"
58#include "jni/native.h" 58#include "jni/native.h"
59#include "video_core/renderer_base.h" 59#include "video_core/renderer_base.h"
@@ -123,9 +123,6 @@ int EmulationSession::InstallFileToNand(std::string filename, std::string file_e
123 ErrorFilenameExtension = 4, 123 ErrorFilenameExtension = 4,
124 }; 124 };
125 125
126 m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
127 m_system.GetFileSystemController().CreateFactories(*m_vfs);
128
129 [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; 126 [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
130 if (file_extension == "nsp") { 127 if (file_extension == "nsp") {
131 nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); 128 nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
@@ -664,8 +661,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
664 661
665void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, 662void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
666 jboolean reload) { 663 jboolean reload) {
667 // Create the default config.ini.
668 Config{};
669 // Initialize the emulated system. 664 // Initialize the emulated system.
670 if (!reload) { 665 if (!reload) {
671 EmulationSession::GetInstance().System().Initialize(); 666 EmulationSession::GetInstance().System().Initialize();
@@ -680,17 +675,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl
680void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( 675void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
681 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} 676 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
682 677
683void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
684 Config{};
685}
686
687void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
688 jstring j_game_id) {
689 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
690
691 env->ReleaseStringUTFChars(j_game_id, game_id.data());
692}
693
694jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { 678jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
695 jdoubleArray j_stats = env->NewDoubleArray(4); 679 jdoubleArray j_stats = env->NewDoubleArray(4);
696 680
@@ -707,6 +691,14 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl
707 return j_stats; 691 return j_stats;
708} 692}
709 693
694jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) {
695 if (Settings::IsNceEnabled()) {
696 return ToJString(env, "NCE");
697 }
698
699 return ToJString(env, "JIT");
700}
701
710void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env, 702void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
711 jclass clazz, 703 jclass clazz,
712 jstring j_path) {} 704 jstring j_path) {}
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 8a704960c..763b2164c 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -5,11 +5,15 @@
5 5
6#include <jni.h> 6#include <jni.h>
7 7
8#include "android_config.h"
9#include "android_settings.h"
8#include "common/logging/log.h" 10#include "common/logging/log.h"
9#include "common/settings.h" 11#include "common/settings.h"
12#include "frontend_common/config.h"
10#include "jni/android_common/android_common.h" 13#include "jni/android_common/android_common.h"
11#include "jni/config.h" 14#include "jni/id_cache.h"
12#include "uisettings.h" 15
16std::unique_ptr<AndroidConfig> config;
13 17
14template <typename T> 18template <typename T>
15Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { 19Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
@@ -28,6 +32,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
28 32
29extern "C" { 33extern "C" {
30 34
35void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) {
36 config = std::make_unique<AndroidConfig>();
37}
38
39void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) {
40 config.reset();
41}
42
43void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) {
44 config->AndroidConfig::ReloadAllValues();
45}
46
47void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) {
48 config->AndroidConfig::SaveAllValues();
49}
50
31jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, 51jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
32 jstring jkey, jboolean getDefault) { 52 jstring jkey, jboolean getDefault) {
33 auto setting = getSetting<bool>(env, jkey); 53 auto setting = getSetting<bool>(env, jkey);
@@ -234,4 +254,55 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* e
234 return ToJString(env, setting->PairedSetting()->GetLabel()); 254 return ToJString(env, setting->PairedSetting()->GetLabel());
235} 255}
236 256
257jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) {
258 jclass gameDirClass = IDCache::GetGameDirClass();
259 jmethodID gameDirConstructor = IDCache::GetGameDirConstructor();
260 jobjectArray jgameDirArray =
261 env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr);
262 for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
263 jobject jgameDir =
264 env->NewObject(gameDirClass, gameDirConstructor,
265 ToJString(env, AndroidSettings::values.game_dirs[i].path),
266 static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan));
267 env->SetObjectArrayElement(jgameDirArray, i, jgameDir);
268 }
269 return jgameDirArray;
270}
271
272void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject obj,
273 jobjectArray gameDirs) {
274 AndroidSettings::values.game_dirs.clear();
275 int size = env->GetArrayLength(gameDirs);
276
277 if (size == 0) {
278 return;
279 }
280
281 jobject dir = env->GetObjectArrayElement(gameDirs, 0);
282 jclass gameDirClass = IDCache::GetGameDirClass();
283 jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
284 jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
285 for (int i = 0; i < size; ++i) {
286 dir = env->GetObjectArrayElement(gameDirs, i);
287 jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField));
288 jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField);
289 std::string uriString = GetJString(env, juriString);
290 AndroidSettings::values.game_dirs.push_back(
291 AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
292 }
293}
294
295void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj,
296 jobject gameDir) {
297 jclass gameDirClass = IDCache::GetGameDirClass();
298 jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
299 jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
300
301 jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField));
302 jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField);
303 std::string uriString = GetJString(env, juriString);
304 AndroidSettings::values.game_dirs.push_back(
305 AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
306}
307
237} // extern "C" 308} // extern "C"
diff --git a/src/android/app/src/main/res/layout/card_folder.xml b/src/android/app/src/main/res/layout/card_folder.xml
new file mode 100644
index 000000000..4e0c04b6b
--- /dev/null
+++ b/src/android/app/src/main/res/layout/card_folder.xml
@@ -0,0 +1,70 @@
1<?xml version="1.0" encoding="utf-8"?>
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"
4 xmlns:tools="http://schemas.android.com/tools"
5 style="?attr/materialCardViewOutlinedStyle"
6 android:layout_width="match_parent"
7 android:layout_height="wrap_content"
8 android:layout_marginHorizontal="16dp"
9 android:layout_marginVertical="12dp"
10 android:focusable="true">
11
12 <androidx.constraintlayout.widget.ConstraintLayout
13 android:layout_width="match_parent"
14 android:layout_height="wrap_content"
15 android:orientation="horizontal"
16 android:padding="16dp"
17 android:layout_gravity="center_vertical"
18 android:animateLayoutChanges="true">
19
20 <com.google.android.material.textview.MaterialTextView
21 android:id="@+id/path"
22 style="@style/TextAppearance.Material3.BodyLarge"
23 android:layout_width="0dp"
24 android:layout_height="wrap_content"
25 android:layout_gravity="center_vertical|start"
26 android:ellipsize="none"
27 android:marqueeRepeatLimit="marquee_forever"
28 android:requiresFadingEdge="horizontal"
29 android:singleLine="true"
30 android:textAlignment="viewStart"
31 app:layout_constraintBottom_toBottomOf="parent"
32 app:layout_constraintEnd_toStartOf="@+id/button_layout"
33 app:layout_constraintStart_toStartOf="parent"
34 app:layout_constraintTop_toTopOf="parent"
35 tools:text="@string/select_gpu_driver_default" />
36
37 <LinearLayout
38 android:id="@+id/button_layout"
39 android:layout_width="wrap_content"
40 android:layout_height="wrap_content"
41 android:orientation="horizontal"
42 app:layout_constraintBottom_toBottomOf="parent"
43 app:layout_constraintEnd_toEndOf="parent"
44 app:layout_constraintTop_toTopOf="parent">
45
46 <Button
47 android:id="@+id/button_edit"
48 style="@style/Widget.Material3.Button.IconButton"
49 android:layout_width="wrap_content"
50 android:layout_height="wrap_content"
51 android:contentDescription="@string/delete"
52 android:tooltipText="@string/edit"
53 app:icon="@drawable/ic_edit"
54 app:iconTint="?attr/colorControlNormal" />
55
56 <Button
57 android:id="@+id/button_delete"
58 style="@style/Widget.Material3.Button.IconButton"
59 android:layout_width="wrap_content"
60 android:layout_height="wrap_content"
61 android:contentDescription="@string/delete"
62 android:tooltipText="@string/delete"
63 app:icon="@drawable/ic_delete"
64 app:iconTint="?attr/colorControlNormal" />
65
66 </LinearLayout>
67
68 </androidx.constraintlayout.widget.ConstraintLayout>
69
70</com.google.android.material.card.MaterialCardView>
diff --git a/src/android/app/src/main/res/layout/dialog_add_folder.xml b/src/android/app/src/main/res/layout/dialog_add_folder.xml
new file mode 100644
index 000000000..01f95e868
--- /dev/null
+++ b/src/android/app/src/main/res/layout/dialog_add_folder.xml
@@ -0,0 +1,45 @@
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools"
4 android:layout_width="match_parent"
5 android:layout_height="wrap_content"
6 android:padding="24dp"
7 android:orientation="vertical">
8
9 <com.google.android.material.textview.MaterialTextView
10 android:id="@+id/path"
11 style="@style/TextAppearance.Material3.BodyLarge"
12 android:layout_width="match_parent"
13 android:layout_height="0dp"
14 android:layout_gravity="center_vertical|start"
15 android:layout_weight="1"
16 android:ellipsize="marquee"
17 android:marqueeRepeatLimit="marquee_forever"
18 android:requiresFadingEdge="horizontal"
19 android:singleLine="true"
20 android:textAlignment="viewStart"
21 tools:text="folder/folder/folder/folder" />
22
23 <LinearLayout
24 android:layout_width="match_parent"
25 android:layout_height="wrap_content"
26 android:orientation="horizontal"
27 android:paddingTop="8dp">
28
29 <com.google.android.material.textview.MaterialTextView
30 style="@style/TextAppearance.Material3.BodyMedium"
31 android:layout_width="0dp"
32 android:layout_height="wrap_content"
33 android:layout_gravity="center_vertical|start"
34 android:layout_weight="1"
35 android:text="@string/deep_scan"
36 android:textAlignment="viewStart" />
37
38 <com.google.android.material.checkbox.MaterialCheckBox
39 android:id="@+id/deep_scan_switch"
40 android:layout_width="wrap_content"
41 android:layout_height="wrap_content" />
42
43 </LinearLayout>
44
45</LinearLayout>
diff --git a/src/android/app/src/main/res/layout/dialog_folder_properties.xml b/src/android/app/src/main/res/layout/dialog_folder_properties.xml
new file mode 100644
index 000000000..248d048cb
--- /dev/null
+++ b/src/android/app/src/main/res/layout/dialog_folder_properties.xml
@@ -0,0 +1,30 @@
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="wrap_content"
5 android:padding="24dp"
6 android:orientation="vertical">
7
8 <LinearLayout
9 android:id="@+id/deep_scan_layout"
10 android:layout_width="match_parent"
11 android:layout_height="wrap_content"
12 android:orientation="horizontal">
13
14 <com.google.android.material.textview.MaterialTextView
15 style="@style/TextAppearance.Material3.BodyMedium"
16 android:layout_width="0dp"
17 android:layout_height="wrap_content"
18 android:layout_gravity="center_vertical|start"
19 android:layout_weight="1"
20 android:text="@string/deep_scan"
21 android:textAlignment="viewStart" />
22
23 <com.google.android.material.checkbox.MaterialCheckBox
24 android:id="@+id/deep_scan_switch"
25 android:layout_width="wrap_content"
26 android:layout_height="wrap_content" />
27
28 </LinearLayout>
29
30</LinearLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_folders.xml b/src/android/app/src/main/res/layout/fragment_folders.xml
new file mode 100644
index 000000000..74f2f3754
--- /dev/null
+++ b/src/android/app/src/main/res/layout/fragment_folders.xml
@@ -0,0 +1,48 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 android:id="@+id/coordinator_folders"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 android:background="?attr/colorSurface">
8
9 <androidx.coordinatorlayout.widget.CoordinatorLayout
10 android:layout_width="match_parent"
11 android:layout_height="match_parent">
12
13 <com.google.android.material.appbar.AppBarLayout
14 android:id="@+id/appbar_folders"
15 android:layout_width="match_parent"
16 android:layout_height="wrap_content"
17 android:fitsSystemWindows="true"
18 app:liftOnScrollTargetViewId="@id/list_folders">
19
20 <com.google.android.material.appbar.MaterialToolbar
21 android:id="@+id/toolbar_folders"
22 android:layout_width="match_parent"
23 android:layout_height="?attr/actionBarSize"
24 app:navigationIcon="@drawable/ic_back"
25 app:title="@string/game_folders" />
26
27 </com.google.android.material.appbar.AppBarLayout>
28
29 <androidx.recyclerview.widget.RecyclerView
30 android:id="@+id/list_folders"
31 android:layout_width="match_parent"
32 android:layout_height="wrap_content"
33 android:clipToPadding="false"
34 app:layout_behavior="@string/appbar_scrolling_view_behavior" />
35
36 </androidx.coordinatorlayout.widget.CoordinatorLayout>
37
38 <com.google.android.material.floatingactionbutton.FloatingActionButton
39 android:id="@+id/button_add"
40 android:layout_width="wrap_content"
41 android:layout_height="wrap_content"
42 android:layout_gravity="bottom|end"
43 android:contentDescription="@string/add_games"
44 app:srcCompat="@drawable/ic_add"
45 app:layout_constraintBottom_toBottomOf="parent"
46 app:layout_constraintEnd_toEndOf="parent" />
47
48</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_search.xml b/src/android/app/src/main/res/layout/fragment_search.xml
index b8d54d947..efdfd7047 100644
--- a/src/android/app/src/main/res/layout/fragment_search.xml
+++ b/src/android/app/src/main/res/layout/fragment_search.xml
@@ -127,6 +127,7 @@
127 android:layout_height="wrap_content" 127 android:layout_height="wrap_content"
128 android:clipToPadding="false" 128 android:clipToPadding="false"
129 android:paddingVertical="4dp" 129 android:paddingVertical="4dp"
130 app:checkedChip="@id/chip_recently_played"
130 app:chipSpacingHorizontal="12dp" 131 app:chipSpacingHorizontal="12dp"
131 app:singleLine="true" 132 app:singleLine="true"
132 app:singleSelection="true"> 133 app:singleSelection="true">
diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml
index 6d4c1f86d..cf70b4bc4 100644
--- a/src/android/app/src/main/res/navigation/home_navigation.xml
+++ b/src/android/app/src/main/res/navigation/home_navigation.xml
@@ -28,6 +28,9 @@
28 <action 28 <action
29 android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment" 29 android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment"
30 app:destination="@id/appletLauncherFragment" /> 30 app:destination="@id/appletLauncherFragment" />
31 <action
32 android:id="@+id/action_homeSettingsFragment_to_gameFoldersFragment"
33 app:destination="@id/gameFoldersFragment" />
31 </fragment> 34 </fragment>
32 35
33 <fragment 36 <fragment
@@ -117,5 +120,9 @@
117 android:id="@+id/cabinetLauncherDialogFragment" 120 android:id="@+id/cabinetLauncherDialogFragment"
118 android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment" 121 android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment"
119 android:label="CabinetLauncherDialogFragment" /> 122 android:label="CabinetLauncherDialogFragment" />
123 <fragment
124 android:id="@+id/gameFoldersFragment"
125 android:name="org.yuzu.yuzu_emu.fragments.GameFoldersFragment"
126 android:label="GameFoldersFragment" />
120 127
121</navigation> 128</navigation>
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 51bcc49a3..ab435dce9 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -175,6 +175,24 @@
175 <item>2</item> 175 <item>2</item>
176 </integer-array> 176 </integer-array>
177 177
178 <string-array name="cpuBackendArm64Names">
179 <item>@string/cpu_backend_dynarmic</item>
180 <item>@string/cpu_backend_nce</item>
181 </string-array>
182
183 <integer-array name="cpuBackendArm64Values">
184 <item>0</item>
185 <item>1</item>
186 </integer-array>
187
188 <string-array name="cpuBackendX86Names">
189 <item>@string/cpu_backend_dynarmic</item>
190 </string-array>
191
192 <integer-array name="cpuBackendX86Values">
193 <item>0</item>
194 </integer-array>
195
178 <string-array name="cpuAccuracyNames"> 196 <string-array name="cpuAccuracyNames">
179 <item>@string/auto</item> 197 <item>@string/auto</item>
180 <item>@string/cpu_accuracy_accurate</item> 198 <item>@string/cpu_accuracy_accurate</item>
diff --git a/src/android/app/src/main/res/values/dimens.xml b/src/android/app/src/main/res/values/dimens.xml
index ef855ea6f..380d14213 100644
--- a/src/android/app/src/main/res/values/dimens.xml
+++ b/src/android/app/src/main/res/values/dimens.xml
@@ -13,7 +13,7 @@
13 <dimen name="menu_width">256dp</dimen> 13 <dimen name="menu_width">256dp</dimen>
14 <dimen name="card_width">165dp</dimen> 14 <dimen name="card_width">165dp</dimen>
15 <dimen name="icon_inset">24dp</dimen> 15 <dimen name="icon_inset">24dp</dimen>
16 <dimen name="spacing_bottom_list_fab">72dp</dimen> 16 <dimen name="spacing_bottom_list_fab">76dp</dimen>
17 <dimen name="spacing_fab">24dp</dimen> 17 <dimen name="spacing_fab">24dp</dimen>
18 18
19 <dimen name="dialog_margin">20dp</dimen> 19 <dimen name="dialog_margin">20dp</dimen>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 98c3f20f8..a6ccef8a1 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -38,6 +38,7 @@
38 <string name="empty_gamelist">No files were found or no game directory has been selected yet.</string> 38 <string name="empty_gamelist">No files were found or no game directory has been selected yet.</string>
39 <string name="search_and_filter_games">Search and filter games</string> 39 <string name="search_and_filter_games">Search and filter games</string>
40 <string name="select_games_folder">Select games folder</string> 40 <string name="select_games_folder">Select games folder</string>
41 <string name="manage_game_folders">Manage game folders</string>
41 <string name="select_games_folder_description">Allows yuzu to populate the games list</string> 42 <string name="select_games_folder_description">Allows yuzu to populate the games list</string>
42 <string name="add_games_warning">Skip selecting games folder?</string> 43 <string name="add_games_warning">Skip selecting games folder?</string>
43 <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> 44 <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string>
@@ -91,6 +92,7 @@
91 <string name="manage_save_data">Manage save data</string> 92 <string name="manage_save_data">Manage save data</string>
92 <string name="manage_save_data_description">Save data found. Please select an option below.</string> 93 <string name="manage_save_data_description">Save data found. Please select an option below.</string>
93 <string name="import_export_saves_description">Import or export save files</string> 94 <string name="import_export_saves_description">Import or export save files</string>
95 <string name="save_files_exporting">Exporting save files…</string>
94 <string name="save_file_imported_success">Imported successfully</string> 96 <string name="save_file_imported_success">Imported successfully</string>
95 <string name="save_file_invalid_zip_structure">Invalid save directory structure</string> 97 <string name="save_file_invalid_zip_structure">Invalid save directory structure</string>
96 <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> 98 <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
@@ -123,6 +125,11 @@
123 <string name="manage_yuzu_data_description">Import/export firmware, keys, user data, and more!</string> 125 <string name="manage_yuzu_data_description">Import/export firmware, keys, user data, and more!</string>
124 <string name="share_save_file">Share save file</string> 126 <string name="share_save_file">Share save file</string>
125 <string name="export_save_failed">Failed to export save</string> 127 <string name="export_save_failed">Failed to export save</string>
128 <string name="game_folders">Game folders</string>
129 <string name="deep_scan">Deep scan</string>
130 <string name="add_game_folder">Add game folder</string>
131 <string name="folder_already_added">This folder was already added!</string>
132 <string name="game_folder_properties">Game folder properties</string>
126 133
127 <!-- Applet launcher strings --> 134 <!-- Applet launcher strings -->
128 <string name="applets">Applet launcher</string> 135 <string name="applets">Applet launcher</string>
@@ -184,6 +191,7 @@
184 <string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string> 191 <string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string>
185 <string name="frame_limit_slider">Limit speed percent</string> 192 <string name="frame_limit_slider">Limit speed percent</string>
186 <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string> 193 <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string>
194 <string name="cpu_backend">CPU backend</string>
187 <string name="cpu_accuracy">CPU accuracy</string> 195 <string name="cpu_accuracy">CPU accuracy</string>
188 <string name="value_with_units">%1$s%2$s</string> 196 <string name="value_with_units">%1$s%2$s</string>
189 197
@@ -256,6 +264,8 @@
256 <string name="cancelling">Cancelling</string> 264 <string name="cancelling">Cancelling</string>
257 <string name="install">Install</string> 265 <string name="install">Install</string>
258 <string name="delete">Delete</string> 266 <string name="delete">Delete</string>
267 <string name="edit">Edit</string>
268 <string name="export_success">Exported successfully</string>
259 269
260 <!-- GPU driver installation --> 270 <!-- GPU driver installation -->
261 <string name="select_gpu_driver">Select GPU driver</string> 271 <string name="select_gpu_driver">Select GPU driver</string>
@@ -414,6 +424,10 @@
414 <string name="ratio_force_sixteen_ten">Force 16:10</string> 424 <string name="ratio_force_sixteen_ten">Force 16:10</string>
415 <string name="ratio_stretch">Stretch to window</string> 425 <string name="ratio_stretch">Stretch to window</string>
416 426
427 <!-- CPU Backend -->
428 <string name="cpu_backend_dynarmic">Dynarmic (Slow)</string>
429 <string name="cpu_backend_nce">Native code execution (NCE)</string>
430
417 <!-- CPU Accuracy --> 431 <!-- CPU Accuracy -->
418 <string name="cpu_accuracy_accurate">Accurate</string> 432 <string name="cpu_accuracy_accurate">Accurate</string>
419 <string name="cpu_accuracy_unsafe">Unsafe</string> 433 <string name="cpu_accuracy_unsafe">Unsafe</string>
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index e216eb3de..b58a7073f 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -52,6 +52,7 @@ add_library(common STATIC
52 fiber.cpp 52 fiber.cpp
53 fiber.h 53 fiber.h
54 fixed_point.h 54 fixed_point.h
55 free_region_manager.h
55 fs/file.cpp 56 fs/file.cpp
56 fs/file.h 57 fs/file.h
57 fs/fs.cpp 58 fs/fs.cpp
@@ -166,6 +167,13 @@ if (WIN32)
166 target_link_libraries(common PRIVATE ntdll) 167 target_link_libraries(common PRIVATE ntdll)
167endif() 168endif()
168 169
170if (NOT WIN32)
171 target_sources(common PRIVATE
172 signal_chain.cpp
173 signal_chain.h
174 )
175endif()
176
169if(ANDROID) 177if(ANDROID)
170 target_sources(common 178 target_sources(common
171 PRIVATE 179 PRIVATE
@@ -174,6 +182,15 @@ if(ANDROID)
174 ) 182 )
175endif() 183endif()
176 184
185if (UNIX AND NOT APPLE)
186 target_sources(common PRIVATE
187 linux/gamemode.cpp
188 linux/gamemode.h
189 )
190
191 target_link_libraries(common PRIVATE gamemode::headers)
192endif()
193
177if(ARCHITECTURE_x86_64) 194if(ARCHITECTURE_x86_64)
178 target_sources(common 195 target_sources(common
179 PRIVATE 196 PRIVATE
@@ -191,7 +208,7 @@ if(ARCHITECTURE_x86_64)
191 target_link_libraries(common PRIVATE xbyak::xbyak) 208 target_link_libraries(common PRIVATE xbyak::xbyak)
192endif() 209endif()
193 210
194if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX)) 211if (HAS_NCE)
195 target_sources(common 212 target_sources(common
196 PRIVATE 213 PRIVATE
197 arm64/native_clock.cpp 214 arm64/native_clock.cpp
diff --git a/src/common/free_region_manager.h b/src/common/free_region_manager.h
new file mode 100644
index 000000000..2e590d609
--- /dev/null
+++ b/src/common/free_region_manager.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7#include <boost/icl/interval_set.hpp>
8
9namespace Common {
10
11class FreeRegionManager {
12public:
13 explicit FreeRegionManager() = default;
14 ~FreeRegionManager() = default;
15
16 void SetAddressSpace(void* start, size_t size) {
17 this->FreeBlock(start, size);
18 }
19
20 std::pair<void*, size_t> FreeBlock(void* block_ptr, size_t size) {
21 std::scoped_lock lk(m_mutex);
22
23 // Check to see if we are adjacent to any regions.
24 auto start_address = reinterpret_cast<uintptr_t>(block_ptr);
25 auto end_address = start_address + size;
26 auto it = m_free_regions.find({start_address - 1, end_address + 1});
27
28 // If we are, join with them, ensuring we stay in bounds.
29 if (it != m_free_regions.end()) {
30 start_address = std::min(start_address, it->lower());
31 end_address = std::max(end_address, it->upper());
32 }
33
34 // Free the relevant region.
35 m_free_regions.insert({start_address, end_address});
36
37 // Return the adjusted pointers.
38 block_ptr = reinterpret_cast<void*>(start_address);
39 size = end_address - start_address;
40 return {block_ptr, size};
41 }
42
43 void AllocateBlock(void* block_ptr, size_t size) {
44 std::scoped_lock lk(m_mutex);
45
46 auto address = reinterpret_cast<uintptr_t>(block_ptr);
47 m_free_regions.subtract({address, address + size});
48 }
49
50private:
51 std::mutex m_mutex;
52 boost::icl::interval_set<uintptr_t> m_free_regions;
53};
54
55} // namespace Common
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index ba22595e0..4bfc64f2d 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -21,15 +21,22 @@
21#include <boost/icl/interval_set.hpp> 21#include <boost/icl/interval_set.hpp>
22#include <fcntl.h> 22#include <fcntl.h>
23#include <sys/mman.h> 23#include <sys/mman.h>
24#include <sys/random.h>
24#include <unistd.h> 25#include <unistd.h>
25#include "common/scope_exit.h" 26#include "common/scope_exit.h"
26 27
28#ifndef MAP_NORESERVE
29#define MAP_NORESERVE 0
30#endif
31
27#endif // ^^^ Linux ^^^ 32#endif // ^^^ Linux ^^^
28 33
29#include <mutex> 34#include <mutex>
35#include <random>
30 36
31#include "common/alignment.h" 37#include "common/alignment.h"
32#include "common/assert.h" 38#include "common/assert.h"
39#include "common/free_region_manager.h"
33#include "common/host_memory.h" 40#include "common/host_memory.h"
34#include "common/logging/log.h" 41#include "common/logging/log.h"
35 42
@@ -141,7 +148,7 @@ public:
141 Release(); 148 Release();
142 } 149 }
143 150
144 void Map(size_t virtual_offset, size_t host_offset, size_t length) { 151 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
145 std::unique_lock lock{placeholder_mutex}; 152 std::unique_lock lock{placeholder_mutex};
146 if (!IsNiechePlaceholder(virtual_offset, length)) { 153 if (!IsNiechePlaceholder(virtual_offset, length)) {
147 Split(virtual_offset, length); 154 Split(virtual_offset, length);
@@ -160,7 +167,7 @@ public:
160 } 167 }
161 } 168 }
162 169
163 void Protect(size_t virtual_offset, size_t length, bool read, bool write) { 170 void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
164 DWORD new_flags{}; 171 DWORD new_flags{};
165 if (read && write) { 172 if (read && write) {
166 new_flags = PAGE_READWRITE; 173 new_flags = PAGE_READWRITE;
@@ -186,6 +193,11 @@ public:
186 } 193 }
187 } 194 }
188 195
196 void EnableDirectMappedAddress() {
197 // TODO
198 UNREACHABLE();
199 }
200
189 const size_t backing_size; ///< Size of the backing memory in bytes 201 const size_t backing_size; ///< Size of the backing memory in bytes
190 const size_t virtual_size; ///< Size of the virtual address placeholder in bytes 202 const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
191 203
@@ -353,6 +365,65 @@ private:
353 365
354#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv 366#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
355 367
368#ifdef ARCHITECTURE_arm64
369
370static void* ChooseVirtualBase(size_t virtual_size) {
371 constexpr uintptr_t Map39BitSize = (1ULL << 39);
372 constexpr uintptr_t Map36BitSize = (1ULL << 36);
373
374 // This is not a cryptographic application, we just want something random.
375 std::mt19937_64 rng;
376
377 // We want to ensure we are allocating at an address aligned to the L2 block size.
378 // For Qualcomm devices, we must also allocate memory above 36 bits.
379 const size_t lower = Map36BitSize / HugePageSize;
380 const size_t upper = (Map39BitSize - virtual_size) / HugePageSize;
381 const size_t range = upper - lower;
382
383 // Try up to 64 times to allocate memory at random addresses in the range.
384 for (int i = 0; i < 64; i++) {
385 // Calculate a possible location.
386 uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize;
387
388 // Try to map.
389 // Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
390 void* map_pointer =
391 mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
392 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
393
394 // If we successfully mapped, we're done.
395 if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
396 return map_pointer;
397 }
398
399 // Unmap if necessary, and try again.
400 if (map_pointer != MAP_FAILED) {
401 munmap(map_pointer, virtual_size);
402 }
403 }
404
405 return MAP_FAILED;
406}
407
408#else
409
410static void* ChooseVirtualBase(size_t virtual_size) {
411#if defined(__FreeBSD__)
412 void* virtual_base =
413 mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE,
414 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
415
416 if (virtual_base != MAP_FAILED) {
417 return virtual_base;
418 }
419#endif
420
421 return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE,
422 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
423}
424
425#endif
426
356class HostMemory::Impl { 427class HostMemory::Impl {
357public: 428public:
358 explicit Impl(size_t backing_size_, size_t virtual_size_) 429 explicit Impl(size_t backing_size_, size_t virtual_size_)
@@ -402,29 +473,16 @@ public:
402 } 473 }
403 474
404 // Virtual memory initialization 475 // Virtual memory initialization
405#if defined(__FreeBSD__) 476 virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
406 virtual_base =
407 static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE,
408 MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER, -1, 0));
409 if (virtual_base == MAP_FAILED) {
410 virtual_base = static_cast<u8*>(
411 mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
412 if (virtual_base == MAP_FAILED) {
413 LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
414 throw std::bad_alloc{};
415 }
416 }
417#else
418 virtual_base = static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE,
419 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
420 if (virtual_base == MAP_FAILED) { 477 if (virtual_base == MAP_FAILED) {
421 LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); 478 LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
422 throw std::bad_alloc{}; 479 throw std::bad_alloc{};
423 } 480 }
481#if defined(__linux__)
424 madvise(virtual_base, virtual_size, MADV_HUGEPAGE); 482 madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
425#endif 483#endif
426 484
427 placeholders.add({0, virtual_size}); 485 free_manager.SetAddressSpace(virtual_base, virtual_size);
428 good = true; 486 good = true;
429 } 487 }
430 488
@@ -432,14 +490,29 @@ public:
432 Release(); 490 Release();
433 } 491 }
434 492
435 void Map(size_t virtual_offset, size_t host_offset, size_t length) { 493 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
436 { 494 // Intersect the range with our address space.
437 std::scoped_lock lock{placeholder_mutex}; 495 AdjustMap(&virtual_offset, &length);
438 placeholders.subtract({virtual_offset, virtual_offset + length}); 496
497 // We are removing a placeholder.
498 free_manager.AllocateBlock(virtual_base + virtual_offset, length);
499
500 // Deduce mapping protection flags.
501 int flags = PROT_NONE;
502 if (True(perms & MemoryPermission::Read)) {
503 flags |= PROT_READ;
504 }
505 if (True(perms & MemoryPermission::Write)) {
506 flags |= PROT_WRITE;
507 }
508#ifdef ARCHITECTURE_arm64
509 if (True(perms & MemoryPermission::Execute)) {
510 flags |= PROT_EXEC;
439 } 511 }
512#endif
440 513
441 void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE, 514 void* ret = mmap(virtual_base + virtual_offset, length, flags, MAP_SHARED | MAP_FIXED, fd,
442 MAP_SHARED | MAP_FIXED, fd, host_offset); 515 host_offset);
443 ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); 516 ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
444 } 517 }
445 518
@@ -447,47 +520,54 @@ public:
447 // The method name is wrong. We're still talking about the virtual range. 520 // The method name is wrong. We're still talking about the virtual range.
448 // We don't want to unmap, we want to reserve this memory. 521 // We don't want to unmap, we want to reserve this memory.
449 522
450 { 523 // Intersect the range with our address space.
451 std::scoped_lock lock{placeholder_mutex}; 524 AdjustMap(&virtual_offset, &length);
452 auto it = placeholders.find({virtual_offset - 1, virtual_offset + length + 1});
453
454 if (it != placeholders.end()) {
455 size_t prev_upper = virtual_offset + length;
456 virtual_offset = std::min(virtual_offset, it->lower());
457 length = std::max(it->upper(), prev_upper) - virtual_offset;
458 }
459 525
460 placeholders.add({virtual_offset, virtual_offset + length}); 526 // Merge with any adjacent placeholder mappings.
461 } 527 auto [merged_pointer, merged_size] =
528 free_manager.FreeBlock(virtual_base + virtual_offset, length);
462 529
463 void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE, 530 void* ret = mmap(merged_pointer, merged_size, PROT_NONE,
464 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); 531 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
465 ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); 532 ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
466 } 533 }
467 534
468 void Protect(size_t virtual_offset, size_t length, bool read, bool write) { 535 void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
469 int flags = 0; 536 // Intersect the range with our address space.
537 AdjustMap(&virtual_offset, &length);
538
539 int flags = PROT_NONE;
470 if (read) { 540 if (read) {
471 flags |= PROT_READ; 541 flags |= PROT_READ;
472 } 542 }
473 if (write) { 543 if (write) {
474 flags |= PROT_WRITE; 544 flags |= PROT_WRITE;
475 } 545 }
546#ifdef HAS_NCE
547 if (execute) {
548 flags |= PROT_EXEC;
549 }
550#endif
476 int ret = mprotect(virtual_base + virtual_offset, length, flags); 551 int ret = mprotect(virtual_base + virtual_offset, length, flags);
477 ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno)); 552 ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
478 } 553 }
479 554
555 void EnableDirectMappedAddress() {
556 virtual_base = nullptr;
557 }
558
480 const size_t backing_size; ///< Size of the backing memory in bytes 559 const size_t backing_size; ///< Size of the backing memory in bytes
481 const size_t virtual_size; ///< Size of the virtual address placeholder in bytes 560 const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
482 561
483 u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)}; 562 u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
484 u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)}; 563 u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
564 u8* virtual_map_base{reinterpret_cast<u8*>(MAP_FAILED)};
485 565
486private: 566private:
487 /// Release all resources in the object 567 /// Release all resources in the object
488 void Release() { 568 void Release() {
489 if (virtual_base != MAP_FAILED) { 569 if (virtual_map_base != MAP_FAILED) {
490 int ret = munmap(virtual_base, virtual_size); 570 int ret = munmap(virtual_map_base, virtual_size);
491 ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno)); 571 ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
492 } 572 }
493 573
@@ -502,10 +582,29 @@ private:
502 } 582 }
503 } 583 }
504 584
505 int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create 585 void AdjustMap(size_t* virtual_offset, size_t* length) {
586 if (virtual_base != nullptr) {
587 return;
588 }
589
590 // If we are direct mapped, we want to make sure we are operating on a region
591 // that is in range of our virtual mapping.
592 size_t intended_start = *virtual_offset;
593 size_t intended_end = intended_start + *length;
594 size_t address_space_start = reinterpret_cast<size_t>(virtual_map_base);
595 size_t address_space_end = address_space_start + virtual_size;
506 596
507 boost::icl::interval_set<size_t> placeholders; ///< Mapped placeholders 597 if (address_space_start > intended_end || intended_start > address_space_end) {
508 std::mutex placeholder_mutex; ///< Mutex for placeholders 598 *virtual_offset = 0;
599 *length = 0;
600 } else {
601 *virtual_offset = std::max(intended_start, address_space_start);
602 *length = std::min(intended_end, address_space_end) - *virtual_offset;
603 }
604 }
605
606 int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
607 FreeRegionManager free_manager{};
509}; 608};
510 609
511#else // ^^^ Linux ^^^ vvv Generic vvv 610#else // ^^^ Linux ^^^ vvv Generic vvv
@@ -518,11 +617,13 @@ public:
518 throw std::bad_alloc{}; 617 throw std::bad_alloc{};
519 } 618 }
520 619
521 void Map(size_t virtual_offset, size_t host_offset, size_t length) {} 620 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {}
522 621
523 void Unmap(size_t virtual_offset, size_t length) {} 622 void Unmap(size_t virtual_offset, size_t length) {}
524 623
525 void Protect(size_t virtual_offset, size_t length, bool read, bool write) {} 624 void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
625
626 void EnableDirectMappedAddress() {}
526 627
527 u8* backing_base{nullptr}; 628 u8* backing_base{nullptr};
528 u8* virtual_base{nullptr}; 629 u8* virtual_base{nullptr};
@@ -535,15 +636,16 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
535 try { 636 try {
536 // Try to allocate a fastmem arena. 637 // Try to allocate a fastmem arena.
537 // The implementation will fail with std::bad_alloc on errors. 638 // The implementation will fail with std::bad_alloc on errors.
538 impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), 639 impl =
539 AlignUp(virtual_size, PageAlignment) + 640 std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
540 3 * HugePageSize); 641 AlignUp(virtual_size, PageAlignment) + HugePageSize);
541 backing_base = impl->backing_base; 642 backing_base = impl->backing_base;
542 virtual_base = impl->virtual_base; 643 virtual_base = impl->virtual_base;
543 644
544 if (virtual_base) { 645 if (virtual_base) {
545 virtual_base += 2 * HugePageSize - 1; 646 // Ensure the virtual base is aligned to the L2 block size.
546 virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1); 647 virtual_base = reinterpret_cast<u8*>(
648 Common::AlignUp(reinterpret_cast<uintptr_t>(virtual_base), HugePageSize));
547 virtual_base_offset = virtual_base - impl->virtual_base; 649 virtual_base_offset = virtual_base - impl->virtual_base;
548 } 650 }
549 651
@@ -562,7 +664,8 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
562 664
563HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; 665HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
564 666
565void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) { 667void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
668 MemoryPermission perms) {
566 ASSERT(virtual_offset % PageAlignment == 0); 669 ASSERT(virtual_offset % PageAlignment == 0);
567 ASSERT(host_offset % PageAlignment == 0); 670 ASSERT(host_offset % PageAlignment == 0);
568 ASSERT(length % PageAlignment == 0); 671 ASSERT(length % PageAlignment == 0);
@@ -571,7 +674,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
571 if (length == 0 || !virtual_base || !impl) { 674 if (length == 0 || !virtual_base || !impl) {
572 return; 675 return;
573 } 676 }
574 impl->Map(virtual_offset + virtual_base_offset, host_offset, length); 677 impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
575} 678}
576 679
577void HostMemory::Unmap(size_t virtual_offset, size_t length) { 680void HostMemory::Unmap(size_t virtual_offset, size_t length) {
@@ -584,14 +687,22 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
584 impl->Unmap(virtual_offset + virtual_base_offset, length); 687 impl->Unmap(virtual_offset + virtual_base_offset, length);
585} 688}
586 689
587void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) { 690void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write,
691 bool execute) {
588 ASSERT(virtual_offset % PageAlignment == 0); 692 ASSERT(virtual_offset % PageAlignment == 0);
589 ASSERT(length % PageAlignment == 0); 693 ASSERT(length % PageAlignment == 0);
590 ASSERT(virtual_offset + length <= virtual_size); 694 ASSERT(virtual_offset + length <= virtual_size);
591 if (length == 0 || !virtual_base || !impl) { 695 if (length == 0 || !virtual_base || !impl) {
592 return; 696 return;
593 } 697 }
594 impl->Protect(virtual_offset + virtual_base_offset, length, read, write); 698 impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
699}
700
701void HostMemory::EnableDirectMappedAddress() {
702 if (impl) {
703 impl->EnableDirectMappedAddress();
704 virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
705 }
595} 706}
596 707
597} // namespace Common 708} // namespace Common
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index 447975ded..cebfacab2 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -4,11 +4,20 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include "common/common_funcs.h"
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "common/virtual_buffer.h" 9#include "common/virtual_buffer.h"
9 10
10namespace Common { 11namespace Common {
11 12
13enum class MemoryPermission : u32 {
14 Read = 1 << 0,
15 Write = 1 << 1,
16 ReadWrite = Read | Write,
17 Execute = 1 << 2,
18};
19DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
20
12/** 21/**
13 * A low level linear memory buffer, which supports multiple mappings 22 * A low level linear memory buffer, which supports multiple mappings
14 * Its purpose is to rebuild a given sparse memory layout, including mirrors. 23 * Its purpose is to rebuild a given sparse memory layout, including mirrors.
@@ -31,11 +40,13 @@ public:
31 HostMemory(HostMemory&& other) noexcept; 40 HostMemory(HostMemory&& other) noexcept;
32 HostMemory& operator=(HostMemory&& other) noexcept; 41 HostMemory& operator=(HostMemory&& other) noexcept;
33 42
34 void Map(size_t virtual_offset, size_t host_offset, size_t length); 43 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms);
35 44
36 void Unmap(size_t virtual_offset, size_t length); 45 void Unmap(size_t virtual_offset, size_t length);
37 46
38 void Protect(size_t virtual_offset, size_t length, bool read, bool write); 47 void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false);
48
49 void EnableDirectMappedAddress();
39 50
40 [[nodiscard]] u8* BackingBasePointer() noexcept { 51 [[nodiscard]] u8* BackingBasePointer() noexcept {
41 return backing_base; 52 return backing_base;
diff --git a/src/common/linux/gamemode.cpp b/src/common/linux/gamemode.cpp
new file mode 100644
index 000000000..8d3e2934a
--- /dev/null
+++ b/src/common/linux/gamemode.cpp
@@ -0,0 +1,40 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <gamemode_client.h>
5
6#include "common/linux/gamemode.h"
7#include "common/logging/log.h"
8#include "common/settings.h"
9
10namespace Common::Linux {
11
12void StartGamemode() {
13 if (Settings::values.enable_gamemode) {
14 if (gamemode_request_start() < 0) {
15 LOG_WARNING(Frontend, "Failed to start gamemode: {}", gamemode_error_string());
16 } else {
17 LOG_INFO(Frontend, "Started gamemode");
18 }
19 }
20}
21
22void StopGamemode() {
23 if (Settings::values.enable_gamemode) {
24 if (gamemode_request_end() < 0) {
25 LOG_WARNING(Frontend, "Failed to stop gamemode: {}", gamemode_error_string());
26 } else {
27 LOG_INFO(Frontend, "Stopped gamemode");
28 }
29 }
30}
31
32void SetGamemodeState(bool state) {
33 if (state) {
34 StartGamemode();
35 } else {
36 StopGamemode();
37 }
38}
39
40} // namespace Common::Linux
diff --git a/src/common/linux/gamemode.h b/src/common/linux/gamemode.h
new file mode 100644
index 000000000..b80646ae2
--- /dev/null
+++ b/src/common/linux/gamemode.h
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace Common::Linux {
7
8/**
9 * Start the (Feral Interactive) Linux gamemode if it is installed and it is activated
10 */
11void StartGamemode();
12
13/**
14 * Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated
15 */
16void StopGamemode();
17
18/**
19 * Start or stop the (Feral Interactive) Linux gamemode if it is installed and it is activated
20 * @param state The new state the gamemode should have
21 */
22void SetGamemodeState(bool state);
23
24} // namespace Common::Linux
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 51717be06..4666bd0a0 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -41,6 +41,7 @@ SWITCHABLE(AspectRatio, true);
41SWITCHABLE(AstcDecodeMode, true); 41SWITCHABLE(AstcDecodeMode, true);
42SWITCHABLE(AstcRecompression, true); 42SWITCHABLE(AstcRecompression, true);
43SWITCHABLE(AudioMode, true); 43SWITCHABLE(AudioMode, true);
44SWITCHABLE(CpuBackend, true);
44SWITCHABLE(CpuAccuracy, true); 45SWITCHABLE(CpuAccuracy, true);
45SWITCHABLE(FullscreenMode, true); 46SWITCHABLE(FullscreenMode, true);
46SWITCHABLE(GpuAccuracy, true); 47SWITCHABLE(GpuAccuracy, true);
@@ -155,6 +156,22 @@ bool IsFastmemEnabled() {
155 return true; 156 return true;
156} 157}
157 158
159static bool is_nce_enabled = false;
160
161void SetNceEnabled(bool is_39bit) {
162 const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce;
163 is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit;
164 if (is_nce_selected && !is_nce_enabled) {
165 LOG_WARNING(
166 Common,
167 "Program does not utilize 39-bit address space, unable to natively execute code");
168 }
169}
170
171bool IsNceEnabled() {
172 return is_nce_enabled;
173}
174
158bool IsDockedMode() { 175bool IsDockedMode() {
159 return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; 176 return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
160} 177}
@@ -206,9 +223,9 @@ const char* TranslateCategory(Category category) {
206 case Category::UiAudio: 223 case Category::UiAudio:
207 return "UiAudio"; 224 return "UiAudio";
208 case Category::UiLayout: 225 case Category::UiLayout:
209 return "UiLayout"; 226 return "UILayout";
210 case Category::UiGameList: 227 case Category::UiGameList:
211 return "UiGameList"; 228 return "UIGameList";
212 case Category::Screenshots: 229 case Category::Screenshots:
213 return "Screenshots"; 230 return "Screenshots";
214 case Category::Shortcuts: 231 case Category::Shortcuts:
@@ -219,6 +236,8 @@ const char* TranslateCategory(Category category) {
219 return "Services"; 236 return "Services";
220 case Category::Paths: 237 case Category::Paths:
221 return "Paths"; 238 return "Paths";
239 case Category::Linux:
240 return "Linux";
222 case Category::MaxEnum: 241 case Category::MaxEnum:
223 break; 242 break;
224 } 243 }
diff --git a/src/common/settings.h b/src/common/settings.h
index e899f1ae6..98341ad96 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -63,6 +63,7 @@ SWITCHABLE(AspectRatio, true);
63SWITCHABLE(AstcDecodeMode, true); 63SWITCHABLE(AstcDecodeMode, true);
64SWITCHABLE(AstcRecompression, true); 64SWITCHABLE(AstcRecompression, true);
65SWITCHABLE(AudioMode, true); 65SWITCHABLE(AudioMode, true);
66SWITCHABLE(CpuBackend, true);
66SWITCHABLE(CpuAccuracy, true); 67SWITCHABLE(CpuAccuracy, true);
67SWITCHABLE(FullscreenMode, true); 68SWITCHABLE(FullscreenMode, true);
68SWITCHABLE(GpuAccuracy, true); 69SWITCHABLE(GpuAccuracy, true);
@@ -179,6 +180,14 @@ struct Values {
179 &use_speed_limit}; 180 &use_speed_limit};
180 181
181 // Cpu 182 // Cpu
183 SwitchableSetting<CpuBackend, true> cpu_backend{
184 linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic,
185#ifdef HAS_NCE
186 CpuBackend::Nce,
187#else
188 CpuBackend::Dynarmic,
189#endif
190 "cpu_backend", Category::Cpu};
182 SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto, 191 SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
183 CpuAccuracy::Auto, CpuAccuracy::Paranoid, 192 CpuAccuracy::Auto, CpuAccuracy::Paranoid,
184 "cpu_accuracy", Category::Cpu}; 193 "cpu_accuracy", Category::Cpu};
@@ -232,7 +241,11 @@ struct Values {
232 SwitchableSetting<bool> use_asynchronous_gpu_emulation{ 241 SwitchableSetting<bool> use_asynchronous_gpu_emulation{
233 linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; 242 linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
234 SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, 243 SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
244#ifdef ANDROID
245 AstcDecodeMode::Cpu,
246#else
235 AstcDecodeMode::Gpu, 247 AstcDecodeMode::Gpu,
248#endif
236 AstcDecodeMode::Cpu, 249 AstcDecodeMode::Cpu,
237 AstcDecodeMode::CpuAsynchronous, 250 AstcDecodeMode::CpuAsynchronous,
238 "accelerate_astc", 251 "accelerate_astc",
@@ -304,7 +317,11 @@ struct Values {
304 linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; 317 linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
305 318
306 SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, 319 SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
320#ifdef ANDROID
321 GpuAccuracy::Normal,
322#else
307 GpuAccuracy::High, 323 GpuAccuracy::High,
324#endif
308 GpuAccuracy::Normal, 325 GpuAccuracy::Normal,
309 GpuAccuracy::Extreme, 326 GpuAccuracy::Extreme,
310 "gpu_accuracy", 327 "gpu_accuracy",
@@ -313,20 +330,38 @@ struct Values {
313 true, 330 true,
314 true}; 331 true};
315 GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; 332 GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
316 SwitchableSetting<AnisotropyMode, true> max_anisotropy{ 333 SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
317 linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16, 334#ifdef ANDROID
318 "max_anisotropy", Category::RendererAdvanced}; 335 AnisotropyMode::Default,
336#else
337 AnisotropyMode::Automatic,
338#endif
339 AnisotropyMode::Automatic,
340 AnisotropyMode::X16,
341 "max_anisotropy",
342 Category::RendererAdvanced};
319 SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, 343 SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
320 AstcRecompression::Uncompressed, 344 AstcRecompression::Uncompressed,
321 AstcRecompression::Uncompressed, 345 AstcRecompression::Uncompressed,
322 AstcRecompression::Bc3, 346 AstcRecompression::Bc3,
323 "astc_recompression", 347 "astc_recompression",
324 Category::RendererAdvanced}; 348 Category::RendererAdvanced};
325 SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation", 349 SwitchableSetting<bool> async_presentation{linkage,
326 Category::RendererAdvanced}; 350#ifdef ANDROID
351 true,
352#else
353 false,
354#endif
355 "async_presentation", Category::RendererAdvanced};
327 SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", 356 SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
328 Category::RendererAdvanced}; 357 Category::RendererAdvanced};
329 SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing", 358 SwitchableSetting<bool> use_reactive_flushing{linkage,
359#ifdef ANDROID
360 false,
361#else
362 true,
363#endif
364 "use_reactive_flushing",
330 Category::RendererAdvanced}; 365 Category::RendererAdvanced};
331 SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", 366 SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
332 Category::RendererAdvanced}; 367 Category::RendererAdvanced};
@@ -358,6 +393,8 @@ struct Values {
358 Category::RendererDebug}; 393 Category::RendererDebug};
359 // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control 394 // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
360 bool renderer_amdvlk_depth_bias_workaround{}; 395 bool renderer_amdvlk_depth_bias_workaround{};
396 Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
397 Category::RendererDebug};
361 398
362 // System 399 // System
363 SwitchableSetting<Language, true> language_index{linkage, 400 SwitchableSetting<Language, true> language_index{linkage,
@@ -390,13 +427,20 @@ struct Values {
390 Setting<s32> current_user{linkage, 0, "current_user", Category::System}; 427 Setting<s32> current_user{linkage, 0, "current_user", Category::System};
391 428
392 SwitchableSetting<ConsoleMode> use_docked_mode{linkage, 429 SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
430#ifdef ANDROID
431 ConsoleMode::Handheld,
432#else
393 ConsoleMode::Docked, 433 ConsoleMode::Docked,
434#endif
394 "use_docked_mode", 435 "use_docked_mode",
395 Category::System, 436 Category::System,
396 Specialization::Radio, 437 Specialization::Radio,
397 true, 438 true,
398 true}; 439 true};
399 440
441 // Linux
442 SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux};
443
400 // Controls 444 // Controls
401 InputSetting<std::array<PlayerInput, 10>> players; 445 InputSetting<std::array<PlayerInput, 10>> players;
402 446
@@ -534,6 +578,8 @@ bool IsGPULevelExtreme();
534bool IsGPULevelHigh(); 578bool IsGPULevelHigh();
535 579
536bool IsFastmemEnabled(); 580bool IsFastmemEnabled();
581void SetNceEnabled(bool is_64bit);
582bool IsNceEnabled();
537 583
538bool IsDockedMode(); 584bool IsDockedMode();
539 585
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 7943223eb..344c04439 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -41,6 +41,7 @@ enum class Category : u32 {
41 Multiplayer, 41 Multiplayer,
42 Services, 42 Services,
43 Paths, 43 Paths,
44 Linux,
44 MaxEnum, 45 MaxEnum,
45}; 46};
46 47
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index 11429d7a8..d6351e57e 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -129,6 +129,8 @@ ENUM(ShaderBackend, Glsl, Glasm, SpirV);
129 129
130ENUM(GpuAccuracy, Normal, High, Extreme); 130ENUM(GpuAccuracy, Normal, High, Extreme);
131 131
132ENUM(CpuBackend, Dynarmic, Nce);
133
132ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); 134ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
133 135
134ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); 136ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
diff --git a/src/common/signal_chain.cpp b/src/common/signal_chain.cpp
new file mode 100644
index 000000000..2e4fecc48
--- /dev/null
+++ b/src/common/signal_chain.cpp
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <dlfcn.h>
5
6#include "common/assert.h"
7#include "common/dynamic_library.h"
8#include "common/scope_exit.h"
9#include "common/signal_chain.h"
10
11namespace Common {
12
13template <typename T>
14T* LookupLibcSymbol(const char* name) {
15#if defined(__BIONIC__)
16 Common::DynamicLibrary provider("libc.so");
17 if (!provider.IsOpen()) {
18 UNREACHABLE_MSG("Failed to open libc!");
19 }
20#else
21 // For other operating environments, we assume the symbol is not overridden.
22 const char* base = nullptr;
23 Common::DynamicLibrary provider(base);
24#endif
25
26 void* sym = provider.GetSymbolAddress(name);
27 if (sym == nullptr) {
28 sym = dlsym(RTLD_DEFAULT, name);
29 }
30 if (sym == nullptr) {
31 UNREACHABLE_MSG("Unable to find symbol {}!", name);
32 }
33
34 return reinterpret_cast<T*>(sym);
35}
36
37int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact) {
38 static auto libc_sigaction = LookupLibcSymbol<decltype(sigaction)>("sigaction");
39 return libc_sigaction(signum, act, oldact);
40}
41
42} // namespace Common
diff --git a/src/common/signal_chain.h b/src/common/signal_chain.h
new file mode 100644
index 000000000..8d06a1bd1
--- /dev/null
+++ b/src/common/signal_chain.h
@@ -0,0 +1,19 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#ifndef _WIN32
7
8#include <signal.h>
9
10namespace Common {
11
12// Android's ART overrides sigaction with its own wrapper. This is problematic for SIGSEGV
13// in particular, because ART's handler accesses tpidr_el0, which conflicts with NCE.
14// This extracts the libc symbol and calls it directly.
15int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact);
16
17} // namespace Common
18
19#endif
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index caca9a123..012fdc1e0 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -10,7 +10,7 @@
10#include "common/x64/rdtsc.h" 10#include "common/x64/rdtsc.h"
11#endif 11#endif
12 12
13#if defined(ARCHITECTURE_arm64) && defined(__linux__) 13#ifdef HAS_NCE
14#include "common/arm64/native_clock.h" 14#include "common/arm64/native_clock.h"
15#endif 15#endif
16 16
@@ -68,7 +68,7 @@ std::unique_ptr<WallClock> CreateOptimalClock() {
68 // - Is not more precise than 1 GHz (1ns resolution) 68 // - Is not more precise than 1 GHz (1ns resolution)
69 return std::make_unique<StandardWallClock>(); 69 return std::make_unique<StandardWallClock>();
70 } 70 }
71#elif defined(ARCHITECTURE_arm64) && defined(__linux__) 71#elif defined(HAS_NCE)
72 return std::make_unique<Arm64::NativeClock>(); 72 return std::make_unique<Arm64::NativeClock>();
73#else 73#else
74 return std::make_unique<StandardWallClock>(); 74 return std::make_unique<StandardWallClock>();
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 597890655..85583941c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -529,6 +529,7 @@ add_library(core STATIC
529 hle/service/hid/hid_server.h 529 hle/service/hid/hid_server.h
530 hle/service/hid/hid_system_server.cpp 530 hle/service/hid/hid_system_server.cpp
531 hle/service/hid/hid_system_server.h 531 hle/service/hid/hid_system_server.h
532 hle/service/hid/hid_util.h
532 hle/service/hid/hidbus.cpp 533 hle/service/hid/hidbus.cpp
533 hle/service/hid/hidbus.h 534 hle/service/hid/hidbus.h
534 hle/service/hid/irs.cpp 535 hle/service/hid/irs.cpp
@@ -540,8 +541,8 @@ add_library(core STATIC
540 hle/service/hid/xcd.cpp 541 hle/service/hid/xcd.cpp
541 hle/service/hid/xcd.h 542 hle/service/hid/xcd.h
542 hle/service/hid/errors.h 543 hle/service/hid/errors.h
543 hle/service/hid/controllers/console_sixaxis.cpp 544 hle/service/hid/controllers/console_six_axis.cpp
544 hle/service/hid/controllers/console_sixaxis.h 545 hle/service/hid/controllers/console_six_axis.h
545 hle/service/hid/controllers/controller_base.cpp 546 hle/service/hid/controllers/controller_base.cpp
546 hle/service/hid/controllers/controller_base.h 547 hle/service/hid/controllers/controller_base.h
547 hle/service/hid/controllers/debug_pad.cpp 548 hle/service/hid/controllers/debug_pad.cpp
@@ -556,6 +557,10 @@ add_library(core STATIC
556 hle/service/hid/controllers/npad.h 557 hle/service/hid/controllers/npad.h
557 hle/service/hid/controllers/palma.cpp 558 hle/service/hid/controllers/palma.cpp
558 hle/service/hid/controllers/palma.h 559 hle/service/hid/controllers/palma.h
560 hle/service/hid/controllers/seven_six_axis.cpp
561 hle/service/hid/controllers/seven_six_axis.h
562 hle/service/hid/controllers/six_axis.cpp
563 hle/service/hid/controllers/six_axis.h
559 hle/service/hid/controllers/stubbed.cpp 564 hle/service/hid/controllers/stubbed.cpp
560 hle/service/hid/controllers/stubbed.h 565 hle/service/hid/controllers/stubbed.h
561 hle/service/hid/controllers/touchscreen.cpp 566 hle/service/hid/controllers/touchscreen.cpp
@@ -921,6 +926,22 @@ if (ENABLE_WEB_SERVICE)
921 target_link_libraries(core PRIVATE web_service) 926 target_link_libraries(core PRIVATE web_service)
922endif() 927endif()
923 928
929if (HAS_NCE)
930 enable_language(C ASM)
931 set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
932
933 target_sources(core PRIVATE
934 arm/nce/arm_nce.cpp
935 arm/nce/arm_nce.h
936 arm/nce/arm_nce.s
937 arm/nce/guest_context.h
938 arm/nce/patcher.cpp
939 arm/nce/patcher.h
940 arm/nce/instructions.h
941 )
942 target_link_libraries(core PRIVATE merry::oaknut)
943endif()
944
924if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 945if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
925 target_sources(core PRIVATE 946 target_sources(core PRIVATE
926 arm/dynarmic/arm_dynarmic.h 947 arm/dynarmic/arm_dynarmic.h
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 558fba5bd..d231bf89c 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -201,6 +201,8 @@ void ARM_Interface::Run() {
201 if (True(hr & HaltReason::DataAbort)) { 201 if (True(hr & HaltReason::DataAbort)) {
202 if (system.DebuggerEnabled()) { 202 if (system.DebuggerEnabled()) {
203 system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); 203 system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
204 } else {
205 LogBacktrace();
204 } 206 }
205 current_thread->RequestSuspend(SuspendType::Debug); 207 current_thread->RequestSuspend(SuspendType::Debug);
206 break; 208 break;
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 3d866ff6f..a9d9ac09d 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -81,6 +81,9 @@ public:
81 // thread context to be 800 bytes in size. 81 // thread context to be 800 bytes in size.
82 static_assert(sizeof(ThreadContext64) == 0x320); 82 static_assert(sizeof(ThreadContext64) == 0x320);
83 83
84 /// Perform any backend-specific initialization.
85 virtual void Initialize() {}
86
84 /// Runs the CPU until an event happens 87 /// Runs the CPU until an event happens
85 void Run(); 88 void Run();
86 89
diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp
new file mode 100644
index 000000000..f7bdafd39
--- /dev/null
+++ b/src/core/arm/nce/arm_nce.cpp
@@ -0,0 +1,400 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cinttypes>
5#include <memory>
6
7#include "common/signal_chain.h"
8#include "core/arm/nce/arm_nce.h"
9#include "core/arm/nce/patcher.h"
10#include "core/core.h"
11#include "core/memory.h"
12
13#include "core/hle/kernel/k_process.h"
14
15#include <signal.h>
16#include <sys/syscall.h>
17#include <unistd.h>
18
19namespace Core {
20
21namespace {
22
23struct sigaction g_orig_action;
24
25// Verify assembly offsets.
26using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
27static_assert(offsetof(NativeExecutionParameters, native_context) == TpidrEl0NativeContext);
28static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock);
29static_assert(offsetof(NativeExecutionParameters, magic) == TpidrEl0TlsMagic);
30
31fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
32 _aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved);
33 while (header->magic != FPSIMD_MAGIC) {
34 header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
35 }
36 return reinterpret_cast<fpsimd_context*>(header);
37}
38
39} // namespace
40
41void* ARM_NCE::RestoreGuestContext(void* raw_context) {
42 // Retrieve the host context.
43 auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
44
45 // Thread-local parameters will be located in x9.
46 auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs[9]);
47 auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
48
49 // Retrieve the host floating point state.
50 auto* fpctx = GetFloatingPointState(host_ctx);
51
52 // Save host callee-saved registers.
53 std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &fpctx->vregs[8],
54 sizeof(guest_ctx->host_ctx.host_saved_vregs));
55 std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs[19],
56 sizeof(guest_ctx->host_ctx.host_saved_regs));
57
58 // Save stack pointer.
59 guest_ctx->host_ctx.host_sp = host_ctx.sp;
60
61 // Restore all guest state except tpidr_el0.
62 host_ctx.sp = guest_ctx->sp;
63 host_ctx.pc = guest_ctx->pc;
64 host_ctx.pstate = guest_ctx->pstate;
65 fpctx->fpcr = guest_ctx->fpcr;
66 fpctx->fpsr = guest_ctx->fpsr;
67 std::memcpy(host_ctx.regs, guest_ctx->cpu_registers.data(), sizeof(host_ctx.regs));
68 std::memcpy(fpctx->vregs, guest_ctx->vector_registers.data(), sizeof(fpctx->vregs));
69
70 // Return the new thread-local storage pointer.
71 return tpidr;
72}
73
74void ARM_NCE::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
75 // Retrieve the host context.
76 auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
77
78 // Retrieve the host floating point state.
79 auto* fpctx = GetFloatingPointState(host_ctx);
80
81 // Save all guest registers except tpidr_el0.
82 std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs, sizeof(host_ctx.regs));
83 std::memcpy(guest_ctx->vector_registers.data(), fpctx->vregs, sizeof(fpctx->vregs));
84 guest_ctx->fpsr = fpctx->fpsr;
85 guest_ctx->fpcr = fpctx->fpcr;
86 guest_ctx->pstate = static_cast<u32>(host_ctx.pstate);
87 guest_ctx->pc = host_ctx.pc;
88 guest_ctx->sp = host_ctx.sp;
89
90 // Restore stack pointer.
91 host_ctx.sp = guest_ctx->host_ctx.host_sp;
92
93 // Restore host callee-saved registers.
94 std::memcpy(&host_ctx.regs[19], guest_ctx->host_ctx.host_saved_regs.data(),
95 sizeof(guest_ctx->host_ctx.host_saved_regs));
96 std::memcpy(&fpctx->vregs[8], guest_ctx->host_ctx.host_saved_vregs.data(),
97 sizeof(guest_ctx->host_ctx.host_saved_vregs));
98
99 // Return from the call on exit by setting pc to x30.
100 host_ctx.pc = guest_ctx->host_ctx.host_saved_regs[11];
101
102 // Clear esr_el1 and return it.
103 host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
104}
105
106bool ARM_NCE::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
107 auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
108 auto* info = static_cast<siginfo_t*>(raw_info);
109
110 // Try to handle an invalid access.
111 // TODO: handle accesses which split a page?
112 const Common::ProcessAddress addr =
113 (reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
114 if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
115 // We handled the access successfully and are returning to guest code.
116 return true;
117 }
118
119 // We can't handle the access, so determine why we crashed.
120 const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
121
122 // For data aborts, skip the instruction and return to guest code.
123 // This will allow games to continue in many scenarios where they would otherwise crash.
124 if (!is_prefetch_abort) {
125 host_ctx.pc += 4;
126 return true;
127 }
128
129 // This is a prefetch abort.
130 guest_ctx->esr_el1.fetch_or(static_cast<u64>(HaltReason::PrefetchAbort));
131
132 // Forcibly mark the context as locked. We are still running.
133 // We may race with SignalInterrupt here:
134 // - If we lose the race, then SignalInterrupt will send us a signal we are masking,
135 // and it will do nothing when it is unmasked, as we have already left guest code.
136 // - If we win the race, then SignalInterrupt will wait for us to unlock first.
137 auto& thread_params = guest_ctx->parent->running_thread->GetNativeExecutionParameters();
138 thread_params.lock.store(SpinLockLocked);
139
140 // Return to host.
141 SaveGuestContext(guest_ctx, raw_context);
142 return false;
143}
144
145void ARM_NCE::HandleHostFault(int sig, void* raw_info, void* raw_context) {
146 return g_orig_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
147}
148
149HaltReason ARM_NCE::RunJit() {
150 // Get the thread parameters.
151 // TODO: pass the current thread down from ::Run
152 auto* thread = Kernel::GetCurrentThreadPointer(system.Kernel());
153 auto* thread_params = &thread->GetNativeExecutionParameters();
154
155 {
156 // Lock our core context.
157 std::scoped_lock lk{lock};
158
159 // We should not be running.
160 ASSERT(running_thread == nullptr);
161
162 // Check if we need to run. If we have already been halted, we are done.
163 u64 halt = guest_ctx.esr_el1.exchange(0);
164 if (halt != 0) {
165 return static_cast<HaltReason>(halt);
166 }
167
168 // Mark that we are running.
169 running_thread = thread;
170
171 // Acquire the lock on the thread parameters.
172 // This allows us to force synchronization with SignalInterrupt.
173 LockThreadParameters(thread_params);
174 }
175
176 // Assign current members.
177 guest_ctx.parent = this;
178 thread_params->native_context = &guest_ctx;
179 thread_params->tpidr_el0 = guest_ctx.tpidr_el0;
180 thread_params->tpidrro_el0 = guest_ctx.tpidrro_el0;
181 thread_params->is_running = true;
182
183 HaltReason halt{};
184
185 // TODO: finding and creating the post handler needs to be locked
186 // to deal with dynamic loading of NROs.
187 const auto& post_handlers = system.ApplicationProcess()->GetPostHandlers();
188 if (auto it = post_handlers.find(guest_ctx.pc); it != post_handlers.end()) {
189 halt = ReturnToRunCodeByTrampoline(thread_params, &guest_ctx, it->second);
190 } else {
191 halt = ReturnToRunCodeByExceptionLevelChange(thread_id, thread_params);
192 }
193
194 // Unload members.
195 // The thread does not change, so we can persist the old reference.
196 guest_ctx.tpidr_el0 = thread_params->tpidr_el0;
197 thread_params->native_context = nullptr;
198 thread_params->is_running = false;
199
200 // Unlock the thread parameters.
201 UnlockThreadParameters(thread_params);
202
203 {
204 // Lock the core context.
205 std::scoped_lock lk{lock};
206
207 // On exit, we no longer have an active thread.
208 running_thread = nullptr;
209 }
210
211 // Return the halt reason.
212 return halt;
213}
214
215HaltReason ARM_NCE::StepJit() {
216 return HaltReason::StepThread;
217}
218
219u32 ARM_NCE::GetSvcNumber() const {
220 return guest_ctx.svc_swi;
221}
222
223ARM_NCE::ARM_NCE(System& system_, bool uses_wall_clock_, std::size_t core_index_)
224 : ARM_Interface{system_, uses_wall_clock_}, core_index{core_index_} {
225 guest_ctx.system = &system_;
226}
227
228ARM_NCE::~ARM_NCE() = default;
229
230void ARM_NCE::Initialize() {
231 thread_id = gettid();
232
233 // Setup our signals
234 static std::once_flag flag;
235 std::call_once(flag, [] {
236 using HandlerType = decltype(sigaction::sa_sigaction);
237
238 sigset_t signal_mask;
239 sigemptyset(&signal_mask);
240 sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal);
241 sigaddset(&signal_mask, BreakFromRunCodeSignal);
242 sigaddset(&signal_mask, GuestFaultSignal);
243
244 struct sigaction return_to_run_code_action {};
245 return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
246 return_to_run_code_action.sa_sigaction = reinterpret_cast<HandlerType>(
247 &ARM_NCE::ReturnToRunCodeByExceptionLevelChangeSignalHandler);
248 return_to_run_code_action.sa_mask = signal_mask;
249 Common::SigAction(ReturnToRunCodeByExceptionLevelChangeSignal, &return_to_run_code_action,
250 nullptr);
251
252 struct sigaction break_from_run_code_action {};
253 break_from_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
254 break_from_run_code_action.sa_sigaction =
255 reinterpret_cast<HandlerType>(&ARM_NCE::BreakFromRunCodeSignalHandler);
256 break_from_run_code_action.sa_mask = signal_mask;
257 Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr);
258
259 struct sigaction fault_action {};
260 fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
261 fault_action.sa_sigaction =
262 reinterpret_cast<HandlerType>(&ARM_NCE::GuestFaultSignalHandler);
263 fault_action.sa_mask = signal_mask;
264 Common::SigAction(GuestFaultSignal, &fault_action, &g_orig_action);
265
266 // Simplify call for g_orig_action.
267 // These fields occupy the same space in memory, so this should be a no-op in practice.
268 if (!(g_orig_action.sa_flags & SA_SIGINFO)) {
269 g_orig_action.sa_sigaction =
270 reinterpret_cast<decltype(g_orig_action.sa_sigaction)>(g_orig_action.sa_handler);
271 }
272 });
273}
274
275void ARM_NCE::SetPC(u64 pc) {
276 guest_ctx.pc = pc;
277}
278
279u64 ARM_NCE::GetPC() const {
280 return guest_ctx.pc;
281}
282
283u64 ARM_NCE::GetSP() const {
284 return guest_ctx.sp;
285}
286
287u64 ARM_NCE::GetReg(int index) const {
288 return guest_ctx.cpu_registers[index];
289}
290
291void ARM_NCE::SetReg(int index, u64 value) {
292 guest_ctx.cpu_registers[index] = value;
293}
294
295u128 ARM_NCE::GetVectorReg(int index) const {
296 return guest_ctx.vector_registers[index];
297}
298
299void ARM_NCE::SetVectorReg(int index, u128 value) {
300 guest_ctx.vector_registers[index] = value;
301}
302
303u32 ARM_NCE::GetPSTATE() const {
304 return guest_ctx.pstate;
305}
306
307void ARM_NCE::SetPSTATE(u32 pstate) {
308 guest_ctx.pstate = pstate;
309}
310
311u64 ARM_NCE::GetTlsAddress() const {
312 return guest_ctx.tpidrro_el0;
313}
314
315void ARM_NCE::SetTlsAddress(u64 address) {
316 guest_ctx.tpidrro_el0 = address;
317}
318
319u64 ARM_NCE::GetTPIDR_EL0() const {
320 return guest_ctx.tpidr_el0;
321}
322
323void ARM_NCE::SetTPIDR_EL0(u64 value) {
324 guest_ctx.tpidr_el0 = value;
325}
326
327void ARM_NCE::SaveContext(ThreadContext64& ctx) const {
328 ctx.cpu_registers = guest_ctx.cpu_registers;
329 ctx.sp = guest_ctx.sp;
330 ctx.pc = guest_ctx.pc;
331 ctx.pstate = guest_ctx.pstate;
332 ctx.vector_registers = guest_ctx.vector_registers;
333 ctx.fpcr = guest_ctx.fpcr;
334 ctx.fpsr = guest_ctx.fpsr;
335 ctx.tpidr = guest_ctx.tpidr_el0;
336}
337
338void ARM_NCE::LoadContext(const ThreadContext64& ctx) {
339 guest_ctx.cpu_registers = ctx.cpu_registers;
340 guest_ctx.sp = ctx.sp;
341 guest_ctx.pc = ctx.pc;
342 guest_ctx.pstate = ctx.pstate;
343 guest_ctx.vector_registers = ctx.vector_registers;
344 guest_ctx.fpcr = ctx.fpcr;
345 guest_ctx.fpsr = ctx.fpsr;
346 guest_ctx.tpidr_el0 = ctx.tpidr;
347}
348
349void ARM_NCE::SignalInterrupt() {
350 // Lock core context.
351 std::scoped_lock lk{lock};
352
353 // Add break loop condition.
354 guest_ctx.esr_el1.fetch_or(static_cast<u64>(HaltReason::BreakLoop));
355
356 // If there is no thread running, we are done.
357 if (running_thread == nullptr) {
358 return;
359 }
360
361 // Lock the thread context.
362 auto* params = &running_thread->GetNativeExecutionParameters();
363 LockThreadParameters(params);
364
365 if (params->is_running) {
366 // We should signal to the running thread.
367 // The running thread will unlock the thread context.
368 syscall(SYS_tkill, thread_id, BreakFromRunCodeSignal);
369 } else {
370 // If the thread is no longer running, we have nothing to do.
371 UnlockThreadParameters(params);
372 }
373}
374
375void ARM_NCE::ClearInterrupt() {
376 guest_ctx.esr_el1 = {};
377}
378
379void ARM_NCE::ClearInstructionCache() {
380 // TODO: This is not possible to implement correctly on Linux because
381 // we do not have any access to ic iallu.
382
383 // Require accesses to complete.
384 std::atomic_thread_fence(std::memory_order_seq_cst);
385}
386
387void ARM_NCE::InvalidateCacheRange(u64 addr, std::size_t size) {
388 this->ClearInstructionCache();
389}
390
391void ARM_NCE::ClearExclusiveState() {
392 // No-op.
393}
394
395void ARM_NCE::PageTableChanged(Common::PageTable& page_table,
396 std::size_t new_address_space_size_in_bits) {
397 // No-op. Page table is never used.
398}
399
400} // namespace Core
diff --git a/src/core/arm/nce/arm_nce.h b/src/core/arm/nce/arm_nce.h
new file mode 100644
index 000000000..5fbd6dbf3
--- /dev/null
+++ b/src/core/arm/nce/arm_nce.h
@@ -0,0 +1,108 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7#include <memory>
8#include <span>
9#include <unordered_map>
10#include <vector>
11
12#include "core/arm/arm_interface.h"
13#include "core/arm/nce/guest_context.h"
14
15namespace Core::Memory {
16class Memory;
17}
18
19namespace Core {
20
21class System;
22
23class ARM_NCE final : public ARM_Interface {
24public:
25 ARM_NCE(System& system_, bool uses_wall_clock_, std::size_t core_index_);
26
27 ~ARM_NCE() override;
28
29 void Initialize() override;
30 void SetPC(u64 pc) override;
31 u64 GetPC() const override;
32 u64 GetSP() const override;
33 u64 GetReg(int index) const override;
34 void SetReg(int index, u64 value) override;
35 u128 GetVectorReg(int index) const override;
36 void SetVectorReg(int index, u128 value) override;
37
38 u32 GetPSTATE() const override;
39 void SetPSTATE(u32 pstate) override;
40 u64 GetTlsAddress() const override;
41 void SetTlsAddress(u64 address) override;
42 void SetTPIDR_EL0(u64 value) override;
43 u64 GetTPIDR_EL0() const override;
44
45 Architecture GetArchitecture() const override {
46 return Architecture::Aarch64;
47 }
48
49 void SaveContext(ThreadContext32& ctx) const override {}
50 void SaveContext(ThreadContext64& ctx) const override;
51 void LoadContext(const ThreadContext32& ctx) override {}
52 void LoadContext(const ThreadContext64& ctx) override;
53
54 void SignalInterrupt() override;
55 void ClearInterrupt() override;
56 void ClearExclusiveState() override;
57 void ClearInstructionCache() override;
58 void InvalidateCacheRange(u64 addr, std::size_t size) override;
59 void PageTableChanged(Common::PageTable& new_page_table,
60 std::size_t new_address_space_size_in_bits) override;
61
62protected:
63 HaltReason RunJit() override;
64 HaltReason StepJit() override;
65
66 u32 GetSvcNumber() const override;
67
68 const Kernel::DebugWatchpoint* HaltedWatchpoint() const override {
69 return nullptr;
70 }
71
72 void RewindBreakpointInstruction() override {}
73
74private:
75 // Assembly definitions.
76 static HaltReason ReturnToRunCodeByTrampoline(void* tpidr, GuestContext* ctx,
77 u64 trampoline_addr);
78 static HaltReason ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr);
79
80 static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info,
81 void* raw_context);
82 static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context);
83 static void GuestFaultSignalHandler(int sig, void* info, void* raw_context);
84
85 static void LockThreadParameters(void* tpidr);
86 static void UnlockThreadParameters(void* tpidr);
87
88private:
89 // C++ implementation functions for assembly definitions.
90 static void* RestoreGuestContext(void* raw_context);
91 static void SaveGuestContext(GuestContext* ctx, void* raw_context);
92 static bool HandleGuestFault(GuestContext* ctx, void* info, void* raw_context);
93 static void HandleHostFault(int sig, void* info, void* raw_context);
94
95public:
96 // Members set on initialization.
97 std::size_t core_index{};
98 pid_t thread_id{-1};
99
100 // Core context.
101 GuestContext guest_ctx;
102
103 // Thread and invalidation info.
104 std::mutex lock;
105 Kernel::KThread* running_thread{};
106};
107
108} // namespace Core
diff --git a/src/core/arm/nce/arm_nce.s b/src/core/arm/nce/arm_nce.s
new file mode 100644
index 000000000..b98e09f31
--- /dev/null
+++ b/src/core/arm/nce/arm_nce.s
@@ -0,0 +1,222 @@
1/* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */
2/* SPDX-License-Identifier: GPL-2.0-or-later */
3
4#include "core/arm/nce/arm_nce_asm_definitions.h"
5
6#define LOAD_IMMEDIATE_32(reg, val) \
7 mov reg, #(((val) >> 0x00) & 0xFFFF); \
8 movk reg, #(((val) >> 0x10) & 0xFFFF), lsl #16
9
10
11/* static HaltReason Core::ARM_NCE::ReturnToRunCodeByTrampoline(void* tpidr, Core::GuestContext* ctx, u64 trampoline_addr) */
12.section .text._ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, "ax", %progbits
13.global _ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm
14.type _ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, %function
15_ZN4Core7ARM_NCE27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm:
16 /* Back up host sp to x3. */
17 /* Back up host tpidr_el0 to x4. */
18 mov x3, sp
19 mrs x4, tpidr_el0
20
21 /* Load guest sp. x5 is used as a scratch register. */
22 ldr x5, [x1, #(GuestContextSp)]
23 mov sp, x5
24
25 /* Offset GuestContext pointer to the host member. */
26 add x5, x1, #(GuestContextHostContext)
27
28 /* Save original host sp and tpidr_el0 (x3, x4) to host context. */
29 stp x3, x4, [x5, #(HostContextSpTpidrEl0)]
30
31 /* Save all callee-saved host GPRs. */
32 stp x19, x20, [x5, #(HostContextRegs+0x0)]
33 stp x21, x22, [x5, #(HostContextRegs+0x10)]
34 stp x23, x24, [x5, #(HostContextRegs+0x20)]
35 stp x25, x26, [x5, #(HostContextRegs+0x30)]
36 stp x27, x28, [x5, #(HostContextRegs+0x40)]
37 stp x29, x30, [x5, #(HostContextRegs+0x50)]
38
39 /* Save all callee-saved host FPRs. */
40 stp q8, q9, [x5, #(HostContextVregs+0x0)]
41 stp q10, q11, [x5, #(HostContextVregs+0x20)]
42 stp q12, q13, [x5, #(HostContextVregs+0x40)]
43 stp q14, q15, [x5, #(HostContextVregs+0x60)]
44
45 /* Load guest tpidr_el0 from argument. */
46 msr tpidr_el0, x0
47
48 /* Tail call the trampoline to restore guest state. */
49 br x2
50
51
52/* static HaltReason Core::ARM_NCE::ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr) */
53.section .text._ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv, "ax", %progbits
54.global _ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv
55.type _ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv, %function
56_ZN4Core7ARM_NCE37ReturnToRunCodeByExceptionLevelChangeEiPv:
57 /* This jumps to the signal handler, which will restore the entire context. */
58 /* On entry, x0 = thread id, which is already in the right place. */
59
60 /* Move tpidr to x9 so it is not trampled. */
61 mov x9, x1
62
63 /* Set up arguments. */
64 mov x8, #(__NR_tkill)
65 mov x1, #(ReturnToRunCodeByExceptionLevelChangeSignal)
66
67 /* Tail call the signal handler. */
68 svc #0
69
70 /* Block execution from flowing here. */
71 brk #1000
72
73
74/* static void Core::ARM_NCE::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, void* raw_context) */
75.section .text._ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, "ax", %progbits
76.global _ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_
77.type _ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, %function
78_ZN4Core7ARM_NCE50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
79 stp x29, x30, [sp, #-0x10]!
80 mov x29, sp
81
82 /* Call the context restorer with the raw context. */
83 mov x0, x2
84 bl _ZN4Core7ARM_NCE19RestoreGuestContextEPv
85
86 /* Save the old value of tpidr_el0. */
87 mrs x8, tpidr_el0
88 ldr x9, [x0, #(TpidrEl0NativeContext)]
89 str x8, [x9, #(GuestContextHostContext + HostContextTpidrEl0)]
90
91 /* Set our new tpidr_el0. */
92 msr tpidr_el0, x0
93
94 /* Unlock the context. */
95 bl _ZN4Core7ARM_NCE22UnlockThreadParametersEPv
96
97 /* Returning from here will enter the guest. */
98 ldp x29, x30, [sp], #0x10
99 ret
100
101
102/* static void Core::ARM_NCE::BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context) */
103.section .text._ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_, "ax", %progbits
104.global _ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_
105.type _ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_, %function
106_ZN4Core7ARM_NCE29BreakFromRunCodeSignalHandlerEiPvS1_:
107 /* Check to see if we have the correct TLS magic. */
108 mrs x8, tpidr_el0
109 ldr w9, [x8, #(TpidrEl0TlsMagic)]
110
111 LOAD_IMMEDIATE_32(w10, TlsMagic)
112
113 cmp w9, w10
114 b.ne 1f
115
116 /* Correct TLS magic, so this is a guest interrupt. */
117 /* Restore host tpidr_el0. */
118 ldr x0, [x8, #(TpidrEl0NativeContext)]
119 ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
120 msr tpidr_el0, x3
121
122 /* Tail call the restorer. */
123 mov x1, x2
124 b _ZN4Core7ARM_NCE16SaveGuestContextEPNS_12GuestContextEPv
125
126 /* Returning from here will enter host code. */
127
1281:
129 /* Incorrect TLS magic, so this is a spurious signal. */
130 ret
131
132
133/* static void Core::ARM_NCE::GuestFaultSignalHandler(int sig, void* info, void* raw_context) */
134.section .text._ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_, "ax", %progbits
135.global _ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_
136.type _ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_, %function
137_ZN4Core7ARM_NCE23GuestFaultSignalHandlerEiPvS1_:
138 /* Check to see if we have the correct TLS magic. */
139 mrs x8, tpidr_el0
140 ldr w9, [x8, #(TpidrEl0TlsMagic)]
141
142 LOAD_IMMEDIATE_32(w10, TlsMagic)
143
144 cmp w9, w10
145 b.eq 1f
146
147 /* Incorrect TLS magic, so this is a host fault. */
148 /* Tail call the handler. */
149 b _ZN4Core7ARM_NCE15HandleHostFaultEiPvS1_
150
1511:
152 /* Correct TLS magic, so this is a guest fault. */
153 stp x29, x30, [sp, #-0x20]!
154 str x19, [sp, #0x10]
155 mov x29, sp
156
157 /* Save the old tpidr_el0. */
158 mov x19, x8
159
160 /* Restore host tpidr_el0. */
161 ldr x0, [x8, #(TpidrEl0NativeContext)]
162 ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
163 msr tpidr_el0, x3
164
165 /* Call the handler. */
166 bl _ZN4Core7ARM_NCE16HandleGuestFaultEPNS_12GuestContextEPvS3_
167
168 /* If the handler returned false, we want to preserve the host tpidr_el0. */
169 cbz x0, 2f
170
171 /* Otherwise, restore guest tpidr_el0. */
172 msr tpidr_el0, x19
173
1742:
175 ldr x19, [sp, #0x10]
176 ldp x29, x30, [sp], #0x20
177 ret
178
179
180/* static void Core::ARM_NCE::LockThreadParameters(void* tpidr) */
181.section .text._ZN4Core7ARM_NCE20LockThreadParametersEPv, "ax", %progbits
182.global _ZN4Core7ARM_NCE20LockThreadParametersEPv
183.type _ZN4Core7ARM_NCE20LockThreadParametersEPv, %function
184_ZN4Core7ARM_NCE20LockThreadParametersEPv:
185 /* Offset to lock member. */
186 add x0, x0, #(TpidrEl0Lock)
187
1881:
189 /* Clear the monitor. */
190 clrex
191
1922:
193 /* Load-linked with acquire ordering. */
194 ldaxr w1, [x0]
195
196 /* If the value was SpinLockLocked, clear monitor and retry. */
197 cbz w1, 1b
198
199 /* Store-conditional SpinLockLocked with relaxed ordering. */
200 stxr w1, wzr, [x0]
201
202 /* If we failed to store, retry. */
203 cbnz w1, 2b
204
205 ret
206
207
208/* static void Core::ARM_NCE::UnlockThreadParameters(void* tpidr) */
209.section .text._ZN4Core7ARM_NCE22UnlockThreadParametersEPv, "ax", %progbits
210.global _ZN4Core7ARM_NCE22UnlockThreadParametersEPv
211.type _ZN4Core7ARM_NCE22UnlockThreadParametersEPv, %function
212_ZN4Core7ARM_NCE22UnlockThreadParametersEPv:
213 /* Offset to lock member. */
214 add x0, x0, #(TpidrEl0Lock)
215
216 /* Load SpinLockUnlocked. */
217 mov w1, #(SpinLockUnlocked)
218
219 /* Store value with release ordering. */
220 stlr w1, [x0]
221
222 ret
diff --git a/src/core/arm/nce/arm_nce_asm_definitions.h b/src/core/arm/nce/arm_nce_asm_definitions.h
new file mode 100644
index 000000000..8a9b285b5
--- /dev/null
+++ b/src/core/arm/nce/arm_nce_asm_definitions.h
@@ -0,0 +1,29 @@
1/* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */
2/* SPDX-License-Identifier: GPL-2.0-or-later */
3
4#pragma once
5
6#define __ASSEMBLY__
7
8#include <asm-generic/signal.h>
9#include <asm-generic/unistd.h>
10
11#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
12#define BreakFromRunCodeSignal SIGURG
13#define GuestFaultSignal SIGSEGV
14
15#define GuestContextSp 0xF8
16#define GuestContextHostContext 0x320
17
18#define HostContextSpTpidrEl0 0xE0
19#define HostContextTpidrEl0 0xE8
20#define HostContextRegs 0x0
21#define HostContextVregs 0x60
22
23#define TpidrEl0NativeContext 0x10
24#define TpidrEl0Lock 0x18
25#define TpidrEl0TlsMagic 0x20
26#define TlsMagic 0x555a5559
27
28#define SpinLockLocked 0
29#define SpinLockUnlocked 1
diff --git a/src/core/arm/nce/guest_context.h b/src/core/arm/nce/guest_context.h
new file mode 100644
index 000000000..0767a0337
--- /dev/null
+++ b/src/core/arm/nce/guest_context.h
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8#include "core/arm/arm_interface.h"
9#include "core/arm/nce/arm_nce_asm_definitions.h"
10
11namespace Core {
12
13class ARM_NCE;
14class System;
15
16struct HostContext {
17 alignas(16) std::array<u64, 12> host_saved_regs{};
18 alignas(16) std::array<u128, 8> host_saved_vregs{};
19 u64 host_sp{};
20 void* host_tpidr_el0{};
21};
22
23struct GuestContext {
24 std::array<u64, 31> cpu_registers{};
25 u64 sp{};
26 u64 pc{};
27 u32 fpcr{};
28 u32 fpsr{};
29 std::array<u128, 32> vector_registers{};
30 u32 pstate{};
31 alignas(16) HostContext host_ctx{};
32 u64 tpidrro_el0{};
33 u64 tpidr_el0{};
34 std::atomic<u64> esr_el1{};
35 u32 nzcv{};
36 u32 svc_swi{};
37 System* system{};
38 ARM_NCE* parent{};
39};
40
41// Verify assembly offsets.
42static_assert(offsetof(GuestContext, sp) == GuestContextSp);
43static_assert(offsetof(GuestContext, host_ctx) == GuestContextHostContext);
44static_assert(offsetof(HostContext, host_sp) == HostContextSpTpidrEl0);
45static_assert(offsetof(HostContext, host_tpidr_el0) - 8 == HostContextSpTpidrEl0);
46static_assert(offsetof(HostContext, host_tpidr_el0) == HostContextTpidrEl0);
47static_assert(offsetof(HostContext, host_saved_regs) == HostContextRegs);
48static_assert(offsetof(HostContext, host_saved_vregs) == HostContextVregs);
49
50} // namespace Core
diff --git a/src/core/arm/nce/instructions.h b/src/core/arm/nce/instructions.h
new file mode 100644
index 000000000..5b56ff857
--- /dev/null
+++ b/src/core/arm/nce/instructions.h
@@ -0,0 +1,147 @@
1// SPDX-FileCopyrightText: Copyright © 2020 Skyline Team and Contributors
2// SPDX-License-Identifier: MPL-2.0
3
4#include "common/bit_field.h"
5#include "common/common_types.h"
6
7namespace Core::NCE {
8
9enum SystemRegister : u32 {
10 TpidrEl0 = 0x5E82,
11 TpidrroEl0 = 0x5E83,
12 CntfrqEl0 = 0x5F00,
13 CntpctEl0 = 0x5F01,
14};
15
16// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/SVC--Supervisor-Call-
17union SVC {
18 constexpr explicit SVC(u32 raw_) : raw{raw_} {}
19
20 constexpr bool Verify() {
21 return (this->GetSig0() == 0x1 && this->GetSig1() == 0x6A0);
22 }
23
24 constexpr u32 GetSig0() {
25 return decltype(sig0)::ExtractValue(raw);
26 }
27
28 constexpr u32 GetValue() {
29 return decltype(value)::ExtractValue(raw);
30 }
31
32 constexpr u32 GetSig1() {
33 return decltype(sig1)::ExtractValue(raw);
34 }
35
36 u32 raw;
37
38private:
39 BitField<0, 5, u32> sig0; // 0x1
40 BitField<5, 16, u32> value; // 16-bit immediate
41 BitField<21, 11, u32> sig1; // 0x6A0
42};
43static_assert(sizeof(SVC) == sizeof(u32));
44static_assert(SVC(0xD40000C1).Verify());
45static_assert(SVC(0xD40000C1).GetValue() == 0x6);
46
47// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/MRS--Move-System-Register-
48union MRS {
49 constexpr explicit MRS(u32 raw_) : raw{raw_} {}
50
51 constexpr bool Verify() {
52 return (this->GetSig() == 0xD53);
53 }
54
55 constexpr u32 GetRt() {
56 return decltype(rt)::ExtractValue(raw);
57 }
58
59 constexpr u32 GetSystemReg() {
60 return decltype(system_reg)::ExtractValue(raw);
61 }
62
63 constexpr u32 GetSig() {
64 return decltype(sig)::ExtractValue(raw);
65 }
66
67 u32 raw;
68
69private:
70 BitField<0, 5, u32> rt; // destination register
71 BitField<5, 15, u32> system_reg; // source system register
72 BitField<20, 12, u32> sig; // 0xD53
73};
74static_assert(sizeof(MRS) == sizeof(u32));
75static_assert(MRS(0xD53BE020).Verify());
76static_assert(MRS(0xD53BE020).GetSystemReg() == CntpctEl0);
77static_assert(MRS(0xD53BE020).GetRt() == 0x0);
78
79// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/MSR--register---Move-general-purpose-register-to-System-Register-
80union MSR {
81 constexpr explicit MSR(u32 raw_) : raw{raw_} {}
82
83 constexpr bool Verify() {
84 return this->GetSig() == 0xD51;
85 }
86
87 constexpr u32 GetRt() {
88 return decltype(rt)::ExtractValue(raw);
89 }
90
91 constexpr u32 GetSystemReg() {
92 return decltype(system_reg)::ExtractValue(raw);
93 }
94
95 constexpr u32 GetSig() {
96 return decltype(sig)::ExtractValue(raw);
97 }
98
99 u32 raw;
100
101private:
102 BitField<0, 5, u32> rt; // source register
103 BitField<5, 15, u32> system_reg; // destination system register
104 BitField<20, 12, u32> sig; // 0xD51
105};
106static_assert(sizeof(MSR) == sizeof(u32));
107static_assert(MSR(0xD51BD040).Verify());
108static_assert(MSR(0xD51BD040).GetSystemReg() == TpidrEl0);
109static_assert(MSR(0xD51BD040).GetRt() == 0x0);
110
111// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDXR--Load-Exclusive-Register-
112// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDXP--Load-Exclusive-Pair-of-Registers-
113// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STXR--Store-Exclusive-Register-
114// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STXP--Store-Exclusive-Pair-of-registers-
115union Exclusive {
116 constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
117
118 constexpr bool Verify() {
119 return this->GetSig() == 0x10;
120 }
121
122 constexpr u32 GetSig() {
123 return decltype(sig)::ExtractValue(raw);
124 }
125
126 constexpr u32 AsOrdered() {
127 return raw | decltype(o0)::FormatValue(1);
128 }
129
130 u32 raw;
131
132private:
133 BitField<0, 5, u32> rt; // memory operand
134 BitField<5, 5, u32> rn; // register operand 1
135 BitField<10, 5, u32> rt2; // register operand 2
136 BitField<15, 1, u32> o0; // ordered
137 BitField<16, 5, u32> rs; // status register
138 BitField<21, 2, u32> l; // operation type
139 BitField<23, 7, u32> sig; // 0x10
140 BitField<30, 2, u32> size; // size
141};
142static_assert(Exclusive(0xC85FFC00).Verify());
143static_assert(Exclusive(0xC85FFC00).AsOrdered() == 0xC85FFC00);
144static_assert(Exclusive(0xC85F7C00).AsOrdered() == 0xC85FFC00);
145static_assert(Exclusive(0xC8200440).AsOrdered() == 0xC8208440);
146
147} // namespace Core::NCE
diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp
new file mode 100644
index 000000000..ec8527224
--- /dev/null
+++ b/src/core/arm/nce/patcher.cpp
@@ -0,0 +1,474 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/arm64/native_clock.h"
5#include "common/bit_cast.h"
6#include "common/literals.h"
7#include "core/arm/nce/arm_nce.h"
8#include "core/arm/nce/guest_context.h"
9#include "core/arm/nce/instructions.h"
10#include "core/arm/nce/patcher.h"
11#include "core/core.h"
12#include "core/core_timing.h"
13#include "core/hle/kernel/svc.h"
14
15namespace Core::NCE {
16
17using namespace Common::Literals;
18using namespace oaknut::util;
19
20using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
21
22constexpr size_t MaxRelativeBranch = 128_MiB;
23constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);
24
25Patcher::Patcher() : c(m_patch_instructions) {}
26
27Patcher::~Patcher() = default;
28
29void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
30 const Kernel::CodeSet::Segment& code) {
31
32 // Write save context helper function.
33 c.l(m_save_context);
34 WriteSaveContext();
35
36 // Write load context helper function.
37 c.l(m_load_context);
38 WriteLoadContext();
39
40 // Retrieve text segment data.
41 const auto text = std::span{program_image}.subspan(code.offset, code.size);
42 const auto text_words =
43 std::span<const u32>{reinterpret_cast<const u32*>(text.data()), text.size() / sizeof(u32)};
44
45 // Loop through instructions, patching as needed.
46 for (u32 i = ModuleCodeIndex; i < static_cast<u32>(text_words.size()); i++) {
47 const u32 inst = text_words[i];
48
49 const auto AddRelocations = [&] {
50 const uintptr_t this_offset = i * sizeof(u32);
51 const uintptr_t next_offset = this_offset + sizeof(u32);
52
53 // Relocate from here to patch.
54 this->BranchToPatch(this_offset);
55
56 // Relocate from patch to next instruction.
57 return next_offset;
58 };
59
60 // SVC
61 if (auto svc = SVC{inst}; svc.Verify()) {
62 WriteSvcTrampoline(AddRelocations(), svc.GetValue());
63 continue;
64 }
65
66 // MRS Xn, TPIDR_EL0
67 // MRS Xn, TPIDRRO_EL0
68 if (auto mrs = MRS{inst};
69 mrs.Verify() && (mrs.GetSystemReg() == TpidrroEl0 || mrs.GetSystemReg() == TpidrEl0)) {
70 const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0
71 : oaknut::SystemReg::TPIDR_EL0;
72 const auto dest_reg = oaknut::XReg{static_cast<int>(mrs.GetRt())};
73 WriteMrsHandler(AddRelocations(), dest_reg, src_reg);
74 continue;
75 }
76
77 // MRS Xn, CNTPCT_EL0
78 if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntpctEl0) {
79 WriteCntpctHandler(AddRelocations(), oaknut::XReg{static_cast<int>(mrs.GetRt())});
80 continue;
81 }
82
83 // MRS Xn, CNTFRQ_EL0
84 if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntfrqEl0) {
85 UNREACHABLE();
86 }
87
88 // MSR TPIDR_EL0, Xn
89 if (auto msr = MSR{inst}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) {
90 WriteMsrHandler(AddRelocations(), oaknut::XReg{static_cast<int>(msr.GetRt())});
91 continue;
92 }
93
94 if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
95 m_exclusives.push_back(i);
96 }
97 }
98
99 // Determine patching mode for the final relocation step
100 const size_t image_size = program_image.size();
101 this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
102}
103
104void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
105 const Kernel::CodeSet::Segment& code,
106 Kernel::PhysicalMemory& program_image,
107 EntryTrampolines* out_trampolines) {
108 const size_t patch_size = GetSectionSize();
109 const size_t image_size = program_image.size();
110
111 // Retrieve text segment data.
112 const auto text = std::span{program_image}.subspan(code.offset, code.size);
113 const auto text_words =
114 std::span<u32>{reinterpret_cast<u32*>(text.data()), text.size() / sizeof(u32)};
115
116 const auto ApplyBranchToPatchRelocation = [&](u32* target, const Relocation& rel) {
117 oaknut::CodeGenerator rc{target};
118 if (mode == PatchMode::PreText) {
119 rc.B(rel.patch_offset - patch_size - rel.module_offset);
120 } else {
121 rc.B(image_size - rel.module_offset + rel.patch_offset);
122 }
123 };
124
125 const auto ApplyBranchToModuleRelocation = [&](u32* target, const Relocation& rel) {
126 oaknut::CodeGenerator rc{target};
127 if (mode == PatchMode::PreText) {
128 rc.B(patch_size - rel.patch_offset + rel.module_offset);
129 } else {
130 rc.B(rel.module_offset - image_size - rel.patch_offset);
131 }
132 };
133
134 const auto RebasePatch = [&](ptrdiff_t patch_offset) {
135 if (mode == PatchMode::PreText) {
136 return GetInteger(load_base) + patch_offset;
137 } else {
138 return GetInteger(load_base) + image_size + patch_offset;
139 }
140 };
141
142 const auto RebasePc = [&](uintptr_t module_offset) {
143 if (mode == PatchMode::PreText) {
144 return GetInteger(load_base) + patch_size + module_offset;
145 } else {
146 return GetInteger(load_base) + module_offset;
147 }
148 };
149
150 // We are now ready to relocate!
151 for (const Relocation& rel : m_branch_to_patch_relocations) {
152 ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
153 }
154 for (const Relocation& rel : m_branch_to_module_relocations) {
155 ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
156 rel);
157 }
158
159 // Rewrite PC constants and record post trampolines
160 for (const Relocation& rel : m_write_module_pc_relocations) {
161 oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
162 rc.dx(RebasePc(rel.module_offset));
163 }
164 for (const Trampoline& rel : m_trampolines) {
165 out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
166 }
167
168 // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
169 // Convert to ordered to preserve this assumption.
170 for (const ModuleTextAddress i : m_exclusives) {
171 auto exclusive = Exclusive{text_words[i]};
172 text_words[i] = exclusive.AsOrdered();
173 }
174
175 // Copy to program image
176 if (this->mode == PatchMode::PreText) {
177 std::memcpy(program_image.data(), m_patch_instructions.data(),
178 m_patch_instructions.size() * sizeof(u32));
179 } else {
180 program_image.resize(image_size + patch_size);
181 std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
182 m_patch_instructions.size() * sizeof(u32));
183 }
184}
185
186size_t Patcher::GetSectionSize() const noexcept {
187 return Common::AlignUp(m_patch_instructions.size() * sizeof(u32), Core::Memory::YUZU_PAGESIZE);
188}
189
190void Patcher::WriteLoadContext() {
191 // This function was called, which modifies X30, so use that as a scratch register.
192 // SP contains the guest X30, so save our return X30 to SP + 8, since we have allocated 16 bytes
193 // of stack.
194 c.STR(X30, SP, 8);
195 c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
196 c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
197
198 // Load system registers.
199 c.LDR(W0, X30, offsetof(GuestContext, fpsr));
200 c.MSR(oaknut::SystemReg::FPSR, X0);
201 c.LDR(W0, X30, offsetof(GuestContext, fpcr));
202 c.MSR(oaknut::SystemReg::FPCR, X0);
203 c.LDR(W0, X30, offsetof(GuestContext, nzcv));
204 c.MSR(oaknut::SystemReg::NZCV, X0);
205
206 // Load all vector registers.
207 static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
208 for (int i = 0; i <= 30; i += 2) {
209 c.LDP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
210 }
211
212 // Load all general-purpose registers except X30.
213 for (int i = 0; i <= 28; i += 2) {
214 c.LDP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
215 }
216
217 // Reload our return X30 from the stack and return.
218 // The patch code will reload the guest X30 for us.
219 c.LDR(X30, SP, 8);
220 c.RET();
221}
222
223void Patcher::WriteSaveContext() {
224 // This function was called, which modifies X30, so use that as a scratch register.
225 // SP contains the guest X30, so save our X30 to SP + 8, since we have allocated 16 bytes of
226 // stack.
227 c.STR(X30, SP, 8);
228 c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
229 c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
230
231 // Store all general-purpose registers except X30.
232 for (int i = 0; i <= 28; i += 2) {
233 c.STP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
234 }
235
236 // Store all vector registers.
237 static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
238 for (int i = 0; i <= 30; i += 2) {
239 c.STP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
240 }
241
242 // Store guest system registers, X30 and SP, using X0 as a scratch register.
243 c.STR(X0, SP, PRE_INDEXED, -16);
244 c.LDR(X0, SP, 16);
245 c.STR(X0, X30, 8 * 30);
246 c.ADD(X0, SP, 32);
247 c.STR(X0, X30, offsetof(GuestContext, sp));
248 c.MRS(X0, oaknut::SystemReg::FPSR);
249 c.STR(W0, X30, offsetof(GuestContext, fpsr));
250 c.MRS(X0, oaknut::SystemReg::FPCR);
251 c.STR(W0, X30, offsetof(GuestContext, fpcr));
252 c.MRS(X0, oaknut::SystemReg::NZCV);
253 c.STR(W0, X30, offsetof(GuestContext, nzcv));
254 c.LDR(X0, SP, POST_INDEXED, 16);
255
256 // Reload our return X30 from the stack, and return.
257 c.LDR(X30, SP, 8);
258 c.RET();
259}
260
261void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
262 // We are about to start saving state, so we need to lock the context.
263 this->LockContext();
264
265 // Store guest X30 to the stack. Then, save the context and restore the stack.
266 // This will save all registers except PC, but we know PC at patch time.
267 c.STR(X30, SP, PRE_INDEXED, -16);
268 c.BL(m_save_context);
269 c.LDR(X30, SP, POST_INDEXED, 16);
270
271 // Now that we've saved all registers, we can use any registers as scratch.
272 // Store PC + 4 to arm interface, since we know the instruction offset from the entry point.
273 oaknut::Label pc_after_svc;
274 c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
275 c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
276 c.LDR(X2, pc_after_svc);
277 c.STR(X2, X1, offsetof(GuestContext, pc));
278
279 // Store SVC number to execute when we return
280 c.MOV(X2, svc_id);
281 c.STR(W2, X1, offsetof(GuestContext, svc_swi));
282
283 // We are calling a SVC. Clear esr_el1 and return it.
284 static_assert(std::is_same_v<std::underlying_type_t<HaltReason>, u64>);
285 oaknut::Label retry;
286 c.ADD(X2, X1, offsetof(GuestContext, esr_el1));
287 c.l(retry);
288 c.LDAXR(X0, X2);
289 c.STLXR(W3, XZR, X2);
290 c.CBNZ(W3, retry);
291
292 // Add "calling SVC" flag. Since this is X0, this is now our return value.
293 c.ORR(X0, X0, static_cast<u64>(HaltReason::SupervisorCall));
294
295 // Offset the GuestContext pointer to the HostContext member.
296 // STP has limited range of [-512, 504] which we can't reach otherwise
297 // NB: Due to this all offsets below are from the start of HostContext.
298 c.ADD(X1, X1, offsetof(GuestContext, host_ctx));
299
300 // Reload host TPIDR_EL0 and SP.
301 static_assert(offsetof(HostContext, host_sp) + 8 == offsetof(HostContext, host_tpidr_el0));
302 c.LDP(X2, X3, X1, offsetof(HostContext, host_sp));
303 c.MOV(SP, X2);
304 c.MSR(oaknut::SystemReg::TPIDR_EL0, X3);
305
306 // Load callee-saved host registers and return to host.
307 static constexpr size_t HOST_REGS_OFF = offsetof(HostContext, host_saved_regs);
308 static constexpr size_t HOST_VREGS_OFF = offsetof(HostContext, host_saved_vregs);
309 c.LDP(X19, X20, X1, HOST_REGS_OFF);
310 c.LDP(X21, X22, X1, HOST_REGS_OFF + 2 * sizeof(u64));
311 c.LDP(X23, X24, X1, HOST_REGS_OFF + 4 * sizeof(u64));
312 c.LDP(X25, X26, X1, HOST_REGS_OFF + 6 * sizeof(u64));
313 c.LDP(X27, X28, X1, HOST_REGS_OFF + 8 * sizeof(u64));
314 c.LDP(X29, X30, X1, HOST_REGS_OFF + 10 * sizeof(u64));
315 c.LDP(Q8, Q9, X1, HOST_VREGS_OFF);
316 c.LDP(Q10, Q11, X1, HOST_VREGS_OFF + 2 * sizeof(u128));
317 c.LDP(Q12, Q13, X1, HOST_VREGS_OFF + 4 * sizeof(u128));
318 c.LDP(Q14, Q15, X1, HOST_VREGS_OFF + 6 * sizeof(u128));
319 c.RET();
320
321 // Write the post-SVC trampoline address, which will jump back to the guest after restoring its
322 // state.
323 m_trampolines.push_back({c.offset(), module_dest});
324
325 // Host called this location. Save the return address so we can
326 // unwind the stack properly when jumping back.
327 c.MRS(X2, oaknut::SystemReg::TPIDR_EL0);
328 c.LDR(X2, X2, offsetof(NativeExecutionParameters, native_context));
329 c.ADD(X0, X2, offsetof(GuestContext, host_ctx));
330 c.STR(X30, X0, offsetof(HostContext, host_saved_regs) + 11 * sizeof(u64));
331
332 // Reload all guest registers except X30 and PC.
333 // The function also expects 16 bytes of stack already allocated.
334 c.STR(X30, SP, PRE_INDEXED, -16);
335 c.BL(m_load_context);
336 c.LDR(X30, SP, POST_INDEXED, 16);
337
338 // Use X1 as a scratch register to restore X30.
339 c.STR(X1, SP, PRE_INDEXED, -16);
340 c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
341 c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
342 c.LDR(X30, X1, offsetof(GuestContext, cpu_registers) + sizeof(u64) * 30);
343 c.LDR(X1, SP, POST_INDEXED, 16);
344
345 // Unlock the context.
346 this->UnlockContext();
347
348 // Jump back to the instruction after the emulated SVC.
349 this->BranchToModule(module_dest);
350
351 // Store PC after call.
352 c.l(pc_after_svc);
353 this->WriteModulePc(module_dest);
354}
355
356void Patcher::WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
357 oaknut::SystemReg src_reg) {
358 // Retrieve emulated TLS register from GuestContext.
359 c.MRS(dest_reg, oaknut::SystemReg::TPIDR_EL0);
360 if (src_reg == oaknut::SystemReg::TPIDRRO_EL0) {
361 c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidrro_el0));
362 } else {
363 c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidr_el0));
364 }
365
366 // Jump back to the instruction after the emulated MRS.
367 this->BranchToModule(module_dest);
368}
369
370void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) {
371 const auto scratch_reg = src_reg.index() == 0 ? X1 : X0;
372 c.STR(scratch_reg, SP, PRE_INDEXED, -16);
373
374 // Save guest value to NativeExecutionParameters::tpidr_el0.
375 c.MRS(scratch_reg, oaknut::SystemReg::TPIDR_EL0);
376 c.STR(src_reg, scratch_reg, offsetof(NativeExecutionParameters, tpidr_el0));
377
378 // Restore scratch register.
379 c.LDR(scratch_reg, SP, POST_INDEXED, 16);
380
381 // Jump back to the instruction after the emulated MSR.
382 this->BranchToModule(module_dest);
383}
384
385void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) {
386 static Common::Arm64::NativeClock clock{};
387 const auto factor = clock.GetGuestCNTFRQFactor();
388 const auto raw_factor = Common::BitCast<std::array<u64, 2>>(factor);
389
390 const auto use_x2_x3 = dest_reg.index() == 0 || dest_reg.index() == 1;
391 oaknut::XReg scratch0 = use_x2_x3 ? X2 : X0;
392 oaknut::XReg scratch1 = use_x2_x3 ? X3 : X1;
393
394 oaknut::Label factorlo;
395 oaknut::Label factorhi;
396
397 // Save scratches.
398 c.STP(scratch0, scratch1, SP, PRE_INDEXED, -16);
399
400 // Load counter value.
401 c.MRS(dest_reg, oaknut::SystemReg::CNTVCT_EL0);
402
403 // Load scaling factor.
404 c.LDR(scratch0, factorlo);
405 c.LDR(scratch1, factorhi);
406
407 // Multiply low bits and get result.
408 c.UMULH(scratch0, dest_reg, scratch0);
409
410 // Multiply high bits and add low bit result.
411 c.MADD(dest_reg, dest_reg, scratch1, scratch0);
412
413 // Reload scratches.
414 c.LDP(scratch0, scratch1, SP, POST_INDEXED, 16);
415
416 // Jump back to the instruction after the emulated MRS.
417 this->BranchToModule(module_dest);
418
419 // Scaling factor constant values.
420 c.l(factorlo);
421 c.dx(raw_factor[0]);
422 c.l(factorhi);
423 c.dx(raw_factor[1]);
424}
425
426void Patcher::LockContext() {
427 oaknut::Label retry;
428
429 // Save scratches.
430 c.STP(X0, X1, SP, PRE_INDEXED, -16);
431
432 // Reload lock pointer.
433 c.l(retry);
434 c.CLREX();
435 c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
436 c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
437
438 static_assert(SpinLockLocked == 0);
439
440 // Load-linked with acquire ordering.
441 c.LDAXR(W1, X0);
442
443 // If the value was SpinLockLocked, clear monitor and retry.
444 c.CBZ(W1, retry);
445
446 // Store-conditional SpinLockLocked with relaxed ordering.
447 c.STXR(W1, WZR, X0);
448
449 // If we failed to store, retry.
450 c.CBNZ(W1, retry);
451
452 // We succeeded! Reload scratches.
453 c.LDP(X0, X1, SP, POST_INDEXED, 16);
454}
455
456void Patcher::UnlockContext() {
457 // Save scratches.
458 c.STP(X0, X1, SP, PRE_INDEXED, -16);
459
460 // Load lock pointer.
461 c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
462 c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
463
464 // Load SpinLockUnlocked.
465 c.MOV(W1, SpinLockUnlocked);
466
467 // Store value with release ordering.
468 c.STLR(W1, X0);
469
470 // Load scratches.
471 c.LDP(X0, X1, SP, POST_INDEXED, 16);
472}
473
474} // namespace Core::NCE
diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h
new file mode 100644
index 000000000..c6d1608c1
--- /dev/null
+++ b/src/core/arm/nce/patcher.h
@@ -0,0 +1,98 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7#include <unordered_map>
8#include <vector>
9#include <oaknut/code_block.hpp>
10#include <oaknut/oaknut.hpp>
11
12#include "common/common_types.h"
13#include "core/hle/kernel/code_set.h"
14#include "core/hle/kernel/k_typed_address.h"
15#include "core/hle/kernel/physical_memory.h"
16
17namespace Core::NCE {
18
19enum class PatchMode : u32 {
20 None,
21 PreText, ///< Patch section is inserted before .text
22 PostData, ///< Patch section is inserted after .data
23};
24
25using ModuleTextAddress = u64;
26using PatchTextAddress = u64;
27using EntryTrampolines = std::unordered_map<ModuleTextAddress, PatchTextAddress>;
28
29class Patcher {
30public:
31 explicit Patcher();
32 ~Patcher();
33
34 void PatchText(const Kernel::PhysicalMemory& program_image,
35 const Kernel::CodeSet::Segment& code);
36 void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
37 Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
38 size_t GetSectionSize() const noexcept;
39
40 [[nodiscard]] PatchMode GetPatchMode() const noexcept {
41 return mode;
42 }
43
44private:
45 using ModuleDestLabel = uintptr_t;
46
47 struct Trampoline {
48 ptrdiff_t patch_offset;
49 uintptr_t module_offset;
50 };
51
52 void WriteLoadContext();
53 void WriteSaveContext();
54 void LockContext();
55 void UnlockContext();
56 void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id);
57 void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
58 oaknut::SystemReg src_reg);
59 void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg);
60 void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg);
61
62private:
63 void BranchToPatch(uintptr_t module_dest) {
64 m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
65 }
66
67 void BranchToModule(uintptr_t module_dest) {
68 m_branch_to_module_relocations.push_back({c.offset(), module_dest});
69 c.dw(0);
70 }
71
72 void WriteModulePc(uintptr_t module_dest) {
73 m_write_module_pc_relocations.push_back({c.offset(), module_dest});
74 c.dx(0);
75 }
76
77private:
78 // List of patch instructions we have generated.
79 std::vector<u32> m_patch_instructions{};
80
81 // Relocation type for relative branch from module to patch.
82 struct Relocation {
83 ptrdiff_t patch_offset; ///< Offset in bytes from the start of the patch section.
84 uintptr_t module_offset; ///< Offset in bytes from the start of the text section.
85 };
86
87 oaknut::VectorCodeGenerator c;
88 std::vector<Trampoline> m_trampolines;
89 std::vector<Relocation> m_branch_to_patch_relocations{};
90 std::vector<Relocation> m_branch_to_module_relocations{};
91 std::vector<Relocation> m_write_module_pc_relocations{};
92 std::vector<ModuleTextAddress> m_exclusives{};
93 oaknut::Label m_save_context{};
94 oaknut::Label m_load_context{};
95 PatchMode mode{PatchMode::None};
96};
97
98} // namespace Core::NCE
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 980bb97f9..151eb3870 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -211,6 +211,8 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
211 system.GPU().ObtainContext(); 211 system.GPU().ObtainContext();
212 } 212 }
213 213
214 system.ArmInterface(core).Initialize();
215
214 auto& kernel = system.Kernel(); 216 auto& kernel = system.Kernel();
215 auto& scheduler = *kernel.CurrentScheduler(); 217 auto& scheduler = *kernel.CurrentScheduler();
216 auto* thread = scheduler.GetSchedulerCurrentThread(); 218 auto* thread = scheduler.GetSchedulerCurrentThread();
diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp
index de3f8ef8f..1aea56a99 100644
--- a/src/core/device_memory.cpp
+++ b/src/core/device_memory.cpp
@@ -6,7 +6,7 @@
6 6
7namespace Core { 7namespace Core {
8 8
9#ifdef ANDROID 9#ifdef HAS_NCE
10constexpr size_t VirtualReserveSize = 1ULL << 38; 10constexpr size_t VirtualReserveSize = 1ULL << 38;
11#else 11#else
12constexpr size_t VirtualReserveSize = 1ULL << 39; 12constexpr size_t VirtualReserveSize = 1ULL << 39;
@@ -15,6 +15,7 @@ constexpr size_t VirtualReserveSize = 1ULL << 39;
15DeviceMemory::DeviceMemory() 15DeviceMemory::DeviceMemory()
16 : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(), 16 : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
17 VirtualReserveSize} {} 17 VirtualReserveSize} {}
18
18DeviceMemory::~DeviceMemory() = default; 19DeviceMemory::~DeviceMemory() = default;
19 20
20} // namespace Core 21} // namespace Core
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 0bca05587..cc7af2ea3 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -429,10 +429,6 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
429 LOG_DEBUG(Loader, "{}", log_string); 429 LOG_DEBUG(Loader, "{}", log_string);
430 } 430 }
431 431
432 if (base_romfs == nullptr) {
433 return base_romfs;
434 }
435
436 auto romfs = base_romfs; 432 auto romfs = base_romfs;
437 433
438 // Game Updates 434 // Game Updates
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 1eb1f439a..6de2103a0 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -3,6 +3,7 @@
3 3
4#include <memory> 4#include <memory>
5 5
6#include "common/assert.h"
6#include "common/common_types.h" 7#include "common/common_types.h"
7#include "common/string_util.h" 8#include "common/string_util.h"
8#include "common/swap.h" 9#include "common/swap.h"
@@ -101,24 +102,30 @@ void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size
101} // Anonymous namespace 102} // Anonymous namespace
102 103
103VirtualDir ExtractRomFS(VirtualFile file) { 104VirtualDir ExtractRomFS(VirtualFile file) {
105 auto root_container = std::make_shared<VectorVfsDirectory>();
106 if (!file) {
107 return root_container;
108 }
109
104 RomFSHeader header{}; 110 RomFSHeader header{};
105 if (file->ReadObject(&header) != sizeof(RomFSHeader)) 111 if (file->ReadObject(&header) != sizeof(RomFSHeader)) {
106 return nullptr; 112 return root_container;
113 }
107 114
108 if (header.header_size != sizeof(RomFSHeader)) 115 if (header.header_size != sizeof(RomFSHeader)) {
109 return nullptr; 116 return root_container;
117 }
110 118
111 const u64 file_offset = header.file_meta.offset; 119 const u64 file_offset = header.file_meta.offset;
112 const u64 dir_offset = header.directory_meta.offset; 120 const u64 dir_offset = header.directory_meta.offset;
113 121
114 auto root_container = std::make_shared<VectorVfsDirectory>();
115
116 ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container); 122 ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container);
117 123
118 if (auto root = root_container->GetSubdirectory(""); root) { 124 if (auto root = root_container->GetSubdirectory(""); root) {
119 return std::make_shared<CachedVfsDirectory>(std::move(root)); 125 return std::make_shared<CachedVfsDirectory>(std::move(root));
120 } 126 }
121 127
128 ASSERT(false);
122 return nullptr; 129 return nullptr;
123} 130}
124 131
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 1bc07dae5..35e149905 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -22,7 +22,7 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provi
22 : content_provider{provider}, filesystem_controller{controller} { 22 : content_provider{provider}, filesystem_controller{controller} {
23 // Load the RomFS from the app 23 // Load the RomFS from the app
24 if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { 24 if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) {
25 LOG_ERROR(Service_FS, "Unable to read RomFS!"); 25 LOG_WARNING(Service_FS, "Unable to read base RomFS");
26 } 26 }
27 27
28 updatable = app_loader.IsRomFSUpdatable(); 28 updatable = app_loader.IsRomFSUpdatable();
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index a72df034e..c7b48a58d 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -167,6 +167,11 @@ protected:
167 */ 167 */
168 std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const; 168 std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
169 169
170 /**
171 * Clip the provided coordinates to be inside the touchscreen area.
172 */
173 std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const;
174
170 WindowSystemInfo window_info; 175 WindowSystemInfo window_info;
171 176
172 bool strict_context_required = false; 177 bool strict_context_required = false;
@@ -181,11 +186,6 @@ private:
181 // By default, ignore this request and do nothing. 186 // By default, ignore this request and do nothing.
182 } 187 }
183 188
184 /**
185 * Clip the provided coordinates to be inside the touchscreen area.
186 */
187 std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const;
188
189 Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout 189 Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
190 190
191 u32 client_area_width; ///< Current client width, should be set by window impl. 191 u32 client_area_width; ///< Current client width, should be set by window impl.
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 34927cddd..a6e681e15 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -8,6 +8,7 @@
8#include "common/thread.h" 8#include "common/thread.h"
9#include "core/hid/emulated_controller.h" 9#include "core/hid/emulated_controller.h"
10#include "core/hid/input_converter.h" 10#include "core/hid/input_converter.h"
11#include "core/hle/service/hid/hid_util.h"
11 12
12namespace Core::HID { 13namespace Core::HID {
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 14constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
@@ -82,7 +83,7 @@ Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleInde
82} 83}
83 84
84void EmulatedController::ReloadFromSettings() { 85void EmulatedController::ReloadFromSettings() {
85 const auto player_index = NpadIdTypeToIndex(npad_id_type); 86 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
86 const auto& player = Settings::values.players.GetValue()[player_index]; 87 const auto& player = Settings::values.players.GetValue()[player_index];
87 88
88 for (std::size_t index = 0; index < player.buttons.size(); ++index) { 89 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
@@ -118,7 +119,7 @@ void EmulatedController::ReloadFromSettings() {
118} 119}
119 120
120void EmulatedController::ReloadColorsFromSettings() { 121void EmulatedController::ReloadColorsFromSettings() {
121 const auto player_index = NpadIdTypeToIndex(npad_id_type); 122 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
122 const auto& player = Settings::values.players.GetValue()[player_index]; 123 const auto& player = Settings::values.players.GetValue()[player_index];
123 124
124 // Avoid updating colors if overridden by physical controller 125 // Avoid updating colors if overridden by physical controller
@@ -215,7 +216,7 @@ void EmulatedController::LoadDevices() {
215} 216}
216 217
217void EmulatedController::LoadTASParams() { 218void EmulatedController::LoadTASParams() {
218 const auto player_index = NpadIdTypeToIndex(npad_id_type); 219 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
219 Common::ParamPackage common_params{}; 220 Common::ParamPackage common_params{};
220 common_params.Set("engine", "tas"); 221 common_params.Set("engine", "tas");
221 common_params.Set("port", static_cast<int>(player_index)); 222 common_params.Set("port", static_cast<int>(player_index));
@@ -264,7 +265,7 @@ void EmulatedController::LoadTASParams() {
264} 265}
265 266
266void EmulatedController::LoadVirtualGamepadParams() { 267void EmulatedController::LoadVirtualGamepadParams() {
267 const auto player_index = NpadIdTypeToIndex(npad_id_type); 268 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
268 Common::ParamPackage common_params{}; 269 Common::ParamPackage common_params{};
269 common_params.Set("engine", "virtual_gamepad"); 270 common_params.Set("engine", "virtual_gamepad");
270 common_params.Set("port", static_cast<int>(player_index)); 271 common_params.Set("port", static_cast<int>(player_index));
@@ -508,9 +509,11 @@ void EmulatedController::ReloadInput() {
508 }); 509 });
509 } 510 }
510 turbo_button_state = 0; 511 turbo_button_state = 0;
512 is_initalized = true;
511} 513}
512 514
513void EmulatedController::UnloadInput() { 515void EmulatedController::UnloadInput() {
516 is_initalized = false;
514 for (auto& button : button_devices) { 517 for (auto& button : button_devices) {
515 button.reset(); 518 button.reset();
516 } 519 }
@@ -615,7 +618,7 @@ bool EmulatedController::IsConfiguring() const {
615} 618}
616 619
617void EmulatedController::SaveCurrentConfig() { 620void EmulatedController::SaveCurrentConfig() {
618 const auto player_index = NpadIdTypeToIndex(npad_id_type); 621 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
619 auto& player = Settings::values.players.GetValue()[player_index]; 622 auto& player = Settings::values.players.GetValue()[player_index];
620 player.connected = is_connected; 623 player.connected = is_connected;
621 player.controller_type = MapNPadToSettingsType(npad_type); 624 player.controller_type = MapNPadToSettingsType(npad_type);
@@ -1206,13 +1209,16 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1206} 1209}
1207 1210
1208bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1211bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
1212 if (!is_initalized) {
1213 return false;
1214 }
1209 if (device_index >= output_devices.size()) { 1215 if (device_index >= output_devices.size()) {
1210 return false; 1216 return false;
1211 } 1217 }
1212 if (!output_devices[device_index]) { 1218 if (!output_devices[device_index]) {
1213 return false; 1219 return false;
1214 } 1220 }
1215 const auto player_index = NpadIdTypeToIndex(npad_id_type); 1221 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1216 const auto& player = Settings::values.players.GetValue()[player_index]; 1222 const auto& player = Settings::values.players.GetValue()[player_index];
1217 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; 1223 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
1218 1224
@@ -1238,9 +1244,13 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
1238} 1244}
1239 1245
1240bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { 1246bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
1241 const auto player_index = NpadIdTypeToIndex(npad_id_type); 1247 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1242 const auto& player = Settings::values.players.GetValue()[player_index]; 1248 const auto& player = Settings::values.players.GetValue()[player_index];
1243 1249
1250 if (!is_initalized) {
1251 return false;
1252 }
1253
1244 if (!player.vibration_enabled) { 1254 if (!player.vibration_enabled) {
1245 return false; 1255 return false;
1246 } 1256 }
@@ -1260,6 +1270,10 @@ Common::Input::DriverResult EmulatedController::SetPollingMode(
1260 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { 1270 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
1261 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); 1271 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
1262 1272
1273 if (!is_initalized) {
1274 return Common::Input::DriverResult::InvalidHandle;
1275 }
1276
1263 auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; 1277 auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
1264 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1278 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1265 auto& nfc_output_device = output_devices[3]; 1279 auto& nfc_output_device = output_devices[3];
@@ -1305,6 +1319,10 @@ bool EmulatedController::SetCameraFormat(
1305 Core::IrSensor::ImageTransferProcessorFormat camera_format) { 1319 Core::IrSensor::ImageTransferProcessorFormat camera_format) {
1306 LOG_INFO(Service_HID, "Set camera format {}", camera_format); 1320 LOG_INFO(Service_HID, "Set camera format {}", camera_format);
1307 1321
1322 if (!is_initalized) {
1323 return false;
1324 }
1325
1308 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1326 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1309 auto& camera_output_device = output_devices[2]; 1327 auto& camera_output_device = output_devices[2];
1310 1328
@@ -1328,6 +1346,11 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) {
1328} 1346}
1329 1347
1330bool EmulatedController::HasNfc() const { 1348bool EmulatedController::HasNfc() const {
1349
1350 if (!is_initalized) {
1351 return false;
1352 }
1353
1331 const auto& nfc_output_device = output_devices[3]; 1354 const auto& nfc_output_device = output_devices[3];
1332 1355
1333 switch (npad_type) { 1356 switch (npad_type) {
@@ -1365,6 +1388,10 @@ bool EmulatedController::RemoveNfcHandle() {
1365} 1388}
1366 1389
1367bool EmulatedController::StartNfcPolling() { 1390bool EmulatedController::StartNfcPolling() {
1391 if (!is_initalized) {
1392 return false;
1393 }
1394
1368 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1395 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1369 auto& nfc_virtual_output_device = output_devices[3]; 1396 auto& nfc_virtual_output_device = output_devices[3];
1370 1397
@@ -1376,6 +1403,10 @@ bool EmulatedController::StartNfcPolling() {
1376} 1403}
1377 1404
1378bool EmulatedController::StopNfcPolling() { 1405bool EmulatedController::StopNfcPolling() {
1406 if (!is_initalized) {
1407 return false;
1408 }
1409
1379 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1410 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1380 auto& nfc_virtual_output_device = output_devices[3]; 1411 auto& nfc_virtual_output_device = output_devices[3];
1381 1412
@@ -1387,6 +1418,10 @@ bool EmulatedController::StopNfcPolling() {
1387} 1418}
1388 1419
1389bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { 1420bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1421 if (!is_initalized) {
1422 return false;
1423 }
1424
1390 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1425 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1391 auto& nfc_virtual_output_device = output_devices[3]; 1426 auto& nfc_virtual_output_device = output_devices[3];
1392 1427
@@ -1399,6 +1434,10 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1399 1434
1400bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, 1435bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
1401 Common::Input::MifareRequest& out_data) { 1436 Common::Input::MifareRequest& out_data) {
1437 if (!is_initalized) {
1438 return false;
1439 }
1440
1402 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1441 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1403 auto& nfc_virtual_output_device = output_devices[3]; 1442 auto& nfc_virtual_output_device = output_devices[3];
1404 1443
@@ -1411,6 +1450,10 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ
1411} 1450}
1412 1451
1413bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { 1452bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
1453 if (!is_initalized) {
1454 return false;
1455 }
1456
1414 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1457 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1415 auto& nfc_virtual_output_device = output_devices[3]; 1458 auto& nfc_virtual_output_device = output_devices[3];
1416 1459
@@ -1422,6 +1465,10 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req
1422} 1465}
1423 1466
1424bool EmulatedController::WriteNfc(const std::vector<u8>& data) { 1467bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1468 if (!is_initalized) {
1469 return false;
1470 }
1471
1425 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1472 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1426 auto& nfc_virtual_output_device = output_devices[3]; 1473 auto& nfc_virtual_output_device = output_devices[3];
1427 1474
@@ -1433,6 +1480,10 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1433} 1480}
1434 1481
1435void EmulatedController::SetLedPattern() { 1482void EmulatedController::SetLedPattern() {
1483 if (!is_initalized) {
1484 return;
1485 }
1486
1436 for (auto& device : output_devices) { 1487 for (auto& device : output_devices) {
1437 if (!device) { 1488 if (!device) {
1438 continue; 1489 continue;
@@ -1648,7 +1699,7 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1648 } 1699 }
1649 if (is_connected) { 1700 if (is_connected) {
1650 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", 1701 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1651 NpadIdTypeToIndex(npad_id_type)); 1702 Service::HID::NpadIdTypeToIndex(npad_id_type));
1652 } 1703 }
1653 npad_type = npad_type_; 1704 npad_type = npad_type_;
1654} 1705}
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index ea18c2343..d6e20ab66 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -559,6 +559,7 @@ private:
559 NpadStyleTag supported_style_tag{NpadStyleSet::All}; 559 NpadStyleTag supported_style_tag{NpadStyleSet::All};
560 bool is_connected{false}; 560 bool is_connected{false};
561 bool is_configuring{false}; 561 bool is_configuring{false};
562 bool is_initalized{false};
562 bool system_buttons_enabled{true}; 563 bool system_buttons_enabled{true};
563 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; 564 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
564 u32 turbo_button_state{0}; 565 u32 turbo_button_state{0};
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
index cf53c04d9..2cf25a870 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -6,6 +6,7 @@
6#include "core/hid/emulated_controller.h" 6#include "core/hid/emulated_controller.h"
7#include "core/hid/emulated_devices.h" 7#include "core/hid/emulated_devices.h"
8#include "core/hid/hid_core.h" 8#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/hid_util.h"
9 10
10namespace Core::HID { 11namespace Core::HID {
11 12
@@ -98,11 +99,11 @@ const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
98} 99}
99 100
100EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) { 101EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
101 return GetEmulatedController(IndexToNpadIdType(index)); 102 return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
102} 103}
103 104
104const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { 105const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
105 return GetEmulatedController(IndexToNpadIdType(index)); 106 return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
106} 107}
107 108
108void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { 109void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 9d48cd90e..4bf285f36 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -8,6 +8,7 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/point.h" 9#include "common/point.h"
10#include "common/uuid.h" 10#include "common/uuid.h"
11#include "common/vector_math.h"
11 12
12namespace Core::HID { 13namespace Core::HID {
13 14
@@ -218,6 +219,13 @@ enum class NpadIdType : u32 {
218 Invalid = 0xFFFFFFFF, 219 Invalid = 0xFFFFFFFF,
219}; 220};
220 221
222enum class NpadInterfaceType : u8 {
223 Bluetooth = 1,
224 Rail = 2,
225 Usb = 3,
226 Embedded = 4,
227};
228
221// This is nn::hid::NpadStyleIndex 229// This is nn::hid::NpadStyleIndex
222enum class NpadStyleIndex : u8 { 230enum class NpadStyleIndex : u8 {
223 None = 0, 231 None = 0,
@@ -591,6 +599,29 @@ struct SixAxisSensorIcInformation {
591static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, 599static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
592 "SixAxisSensorIcInformation is an invalid size"); 600 "SixAxisSensorIcInformation is an invalid size");
593 601
602// This is nn::hid::SixAxisSensorAttribute
603struct SixAxisSensorAttribute {
604 union {
605 u32 raw{};
606 BitField<0, 1, u32> is_connected;
607 BitField<1, 1, u32> is_interpolated;
608 };
609};
610static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
611
612// This is nn::hid::SixAxisSensorState
613struct SixAxisSensorState {
614 s64 delta_time{};
615 s64 sampling_number{};
616 Common::Vec3f accel{};
617 Common::Vec3f gyro{};
618 Common::Vec3f rotation{};
619 std::array<Common::Vec3f, 3> orientation{};
620 SixAxisSensorAttribute attribute{};
621 INSERT_PADDING_BYTES(4); // Reserved
622};
623static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
624
594// This is nn::hid::VibrationDeviceHandle 625// This is nn::hid::VibrationDeviceHandle
595struct VibrationDeviceHandle { 626struct VibrationDeviceHandle {
596 NpadStyleIndex npad_type{NpadStyleIndex::None}; 627 NpadStyleIndex npad_type{NpadStyleIndex::None};
@@ -701,60 +732,4 @@ struct UniquePadId {
701}; 732};
702static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); 733static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
703 734
704/// Converts a NpadIdType to an array index.
705constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
706 switch (npad_id_type) {
707 case NpadIdType::Player1:
708 return 0;
709 case NpadIdType::Player2:
710 return 1;
711 case NpadIdType::Player3:
712 return 2;
713 case NpadIdType::Player4:
714 return 3;
715 case NpadIdType::Player5:
716 return 4;
717 case NpadIdType::Player6:
718 return 5;
719 case NpadIdType::Player7:
720 return 6;
721 case NpadIdType::Player8:
722 return 7;
723 case NpadIdType::Handheld:
724 return 8;
725 case NpadIdType::Other:
726 return 9;
727 default:
728 return 0;
729 }
730}
731
732/// Converts an array index to a NpadIdType
733constexpr NpadIdType IndexToNpadIdType(size_t index) {
734 switch (index) {
735 case 0:
736 return NpadIdType::Player1;
737 case 1:
738 return NpadIdType::Player2;
739 case 2:
740 return NpadIdType::Player3;
741 case 3:
742 return NpadIdType::Player4;
743 case 4:
744 return NpadIdType::Player5;
745 case 5:
746 return NpadIdType::Player6;
747 case 6:
748 return NpadIdType::Player7;
749 case 7:
750 return NpadIdType::Player8;
751 case 8:
752 return NpadIdType::Handheld;
753 case 9:
754 return NpadIdType::Other;
755 default:
756 return NpadIdType::Invalid;
757 }
758}
759
760} // namespace Core::HID 735} // namespace Core::HID
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index 11359f318..a6bdd28f2 100644
--- a/src/core/hid/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -13,14 +13,14 @@ InputInterpreter::InputInterpreter(Core::System& system)
13 : npad{system.ServiceManager() 13 : npad{system.ServiceManager()
14 .GetService<Service::HID::IHidServer>("hid") 14 .GetService<Service::HID::IHidServer>("hid")
15 ->GetResourceManager() 15 ->GetResourceManager()
16 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} { 16 ->GetNpad()} {
17 ResetButtonStates(); 17 ResetButtonStates();
18} 18}
19 19
20InputInterpreter::~InputInterpreter() = default; 20InputInterpreter::~InputInterpreter() = default;
21 21
22void InputInterpreter::PollInput() { 22void InputInterpreter::PollInput() {
23 const auto button_state = npad.GetAndResetPressState(); 23 const auto button_state = npad->GetAndResetPressState();
24 24
25 previous_index = current_index; 25 previous_index = current_index;
26 current_index = (current_index + 1) % button_states.size(); 26 current_index = (current_index + 1) % button_states.size();
diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h
index 8c521b381..3569aac93 100644
--- a/src/core/hid/input_interpreter.h
+++ b/src/core/hid/input_interpreter.h
@@ -16,7 +16,7 @@ enum class NpadButton : u64;
16} 16}
17 17
18namespace Service::HID { 18namespace Service::HID {
19class Controller_NPad; 19class NPad;
20} 20}
21 21
22/** 22/**
@@ -101,7 +101,7 @@ public:
101 } 101 }
102 102
103private: 103private:
104 Service::HID::Controller_NPad& npad; 104 std::shared_ptr<Service::HID::NPad> npad;
105 105
106 /// Stores 9 consecutive button states polled from HID. 106 /// Stores 9 consecutive button states polled from HID.
107 std::array<Core::HID::NpadButton, 9> button_states{}; 107 std::array<Core::HID::NpadButton, 9> button_states{};
diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h
index af1af2b78..4d2d0098e 100644
--- a/src/core/hle/kernel/code_set.h
+++ b/src/core/hle/kernel/code_set.h
@@ -75,12 +75,26 @@ struct CodeSet final {
75 return segments[2]; 75 return segments[2];
76 } 76 }
77 77
78#ifdef HAS_NCE
79 Segment& PatchSegment() {
80 return patch_segment;
81 }
82
83 const Segment& PatchSegment() const {
84 return patch_segment;
85 }
86#endif
87
78 /// The overall data that backs this code set. 88 /// The overall data that backs this code set.
79 Kernel::PhysicalMemory memory; 89 Kernel::PhysicalMemory memory;
80 90
81 /// The segments that comprise this code set. 91 /// The segments that comprise this code set.
82 std::array<Segment, 3> segments; 92 std::array<Segment, 3> segments;
83 93
94#ifdef HAS_NCE
95 Segment patch_segment;
96#endif
97
84 /// The entry point address for this code set. 98 /// The entry point address for this code set.
85 KProcessAddress entrypoint = 0; 99 KProcessAddress entrypoint = 0;
86}; 100};
diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp
index 32173e52b..23258071e 100644
--- a/src/core/hle/kernel/k_address_space_info.cpp
+++ b/src/core/hle/kernel/k_address_space_info.cpp
@@ -25,8 +25,8 @@ constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
25 { .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, }, 25 { .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
26 { .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, }, 26 { .bit_width = 36, .address = Size_Invalid, .size = 8_GiB , .type = KAddressSpaceInfo::Type::Heap, },
27 { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, }, 27 { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, },
28#ifdef ANDROID 28#ifdef HAS_NCE
29 // With Android, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region. 29 // With NCE, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region.
30 { .bit_width = 39, .address = 128_MiB , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, 30 { .bit_width = 39, .address = 128_MiB , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
31#else 31#else
32 { .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, }, 32 { .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 47dc8fd35..6691586ed 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -88,6 +88,22 @@ Result FlushDataCache(AddressType addr, u64 size) {
88 R_SUCCEED(); 88 R_SUCCEED();
89} 89}
90 90
91constexpr Common::MemoryPermission ConvertToMemoryPermission(KMemoryPermission perm) {
92 Common::MemoryPermission perms{};
93 if (True(perm & KMemoryPermission::UserRead)) {
94 perms |= Common::MemoryPermission::Read;
95 }
96 if (True(perm & KMemoryPermission::UserWrite)) {
97 perms |= Common::MemoryPermission::Write;
98 }
99#ifdef HAS_NCE
100 if (True(perm & KMemoryPermission::UserExecute)) {
101 perms |= Common::MemoryPermission::Execute;
102 }
103#endif
104 return perms;
105}
106
91} // namespace 107} // namespace
92 108
93void KPageTableBase::MemoryRange::Open() { 109void KPageTableBase::MemoryRange::Open() {
@@ -170,7 +186,8 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
170 KMemoryManager::Pool pool, KProcessAddress code_address, 186 KMemoryManager::Pool pool, KProcessAddress code_address,
171 size_t code_size, KSystemResource* system_resource, 187 size_t code_size, KSystemResource* system_resource,
172 KResourceLimit* resource_limit, 188 KResourceLimit* resource_limit,
173 Core::Memory::Memory& memory) { 189 Core::Memory::Memory& memory,
190 KProcessAddress aslr_space_start) {
174 // Calculate region extents. 191 // Calculate region extents.
175 const size_t as_width = GetAddressSpaceWidth(as_type); 192 const size_t as_width = GetAddressSpaceWidth(as_type);
176 const KProcessAddress start = 0; 193 const KProcessAddress start = 0;
@@ -211,7 +228,8 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
211 heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap); 228 heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
212 stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack); 229 stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
213 kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall); 230 kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
214 m_code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit); 231 m_code_region_start = m_address_space_start + aslr_space_start +
232 GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
215 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); 233 m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
216 m_alias_code_region_start = m_code_region_start; 234 m_alias_code_region_start = m_code_region_start;
217 m_alias_code_region_end = m_code_region_end; 235 m_alias_code_region_end = m_code_region_end;
@@ -5643,7 +5661,8 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5643 case OperationType::Map: { 5661 case OperationType::Map: {
5644 ASSERT(virt_addr != 0); 5662 ASSERT(virt_addr != 0);
5645 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); 5663 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5646 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr); 5664 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
5665 ConvertToMemoryPermission(properties.perm));
5647 5666
5648 // Open references to pages, if we should. 5667 // Open references to pages, if we should.
5649 if (this->IsHeapPhysicalAddress(phys_addr)) { 5668 if (this->IsHeapPhysicalAddress(phys_addr)) {
@@ -5658,8 +5677,11 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5658 } 5677 }
5659 case OperationType::ChangePermissions: 5678 case OperationType::ChangePermissions:
5660 case OperationType::ChangePermissionsAndRefresh: 5679 case OperationType::ChangePermissionsAndRefresh:
5661 case OperationType::ChangePermissionsAndRefreshAndFlush: 5680 case OperationType::ChangePermissionsAndRefreshAndFlush: {
5681 m_memory->ProtectRegion(*m_impl, virt_addr, num_pages * PageSize,
5682 ConvertToMemoryPermission(properties.perm));
5662 R_SUCCEED(); 5683 R_SUCCEED();
5684 }
5663 default: 5685 default:
5664 UNREACHABLE(); 5686 UNREACHABLE();
5665 } 5687 }
@@ -5687,7 +5709,8 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5687 const size_t size{node.GetNumPages() * PageSize}; 5709 const size_t size{node.GetNumPages() * PageSize};
5688 5710
5689 // Map the pages. 5711 // Map the pages.
5690 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress()); 5712 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
5713 ConvertToMemoryPermission(properties.perm));
5691 5714
5692 virt_addr += size; 5715 virt_addr += size;
5693 } 5716 }
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
index ee2c41e67..556d230b3 100644
--- a/src/core/hle/kernel/k_page_table_base.h
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -235,7 +235,8 @@ public:
235 bool enable_device_address_space_merge, bool from_back, 235 bool enable_device_address_space_merge, bool from_back,
236 KMemoryManager::Pool pool, KProcessAddress code_address, 236 KMemoryManager::Pool pool, KProcessAddress code_address,
237 size_t code_size, KSystemResource* system_resource, 237 size_t code_size, KSystemResource* system_resource,
238 KResourceLimit* resource_limit, Core::Memory::Memory& memory); 238 KResourceLimit* resource_limit, Core::Memory::Memory& memory,
239 KProcessAddress aslr_space_start);
239 240
240 void Finalize(); 241 void Finalize();
241 242
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 3cfb414e5..6c29eb72c 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -300,7 +300,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
300 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 300 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
301 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, 301 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
302 params.code_address, params.code_num_pages * PageSize, 302 params.code_address, params.code_num_pages * PageSize,
303 m_system_resource, res_limit, this->GetMemory())); 303 m_system_resource, res_limit, this->GetMemory(), 0));
304 } 304 }
305 ON_RESULT_FAILURE_2 { 305 ON_RESULT_FAILURE_2 {
306 m_page_table.Finalize(); 306 m_page_table.Finalize();
@@ -332,7 +332,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
332 332
333Result KProcess::Initialize(const Svc::CreateProcessParameter& params, 333Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
334 std::span<const u32> user_caps, KResourceLimit* res_limit, 334 std::span<const u32> user_caps, KResourceLimit* res_limit,
335 KMemoryManager::Pool pool) { 335 KMemoryManager::Pool pool, KProcessAddress aslr_space_start) {
336 ASSERT(res_limit != nullptr); 336 ASSERT(res_limit != nullptr);
337 337
338 // Set members. 338 // Set members.
@@ -393,7 +393,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
393 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 393 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
394 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, 394 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
395 params.code_address, code_size, m_system_resource, res_limit, 395 params.code_address, code_size, m_system_resource, res_limit,
396 this->GetMemory())); 396 this->GetMemory(), aslr_space_start));
397 } 397 }
398 ON_RESULT_FAILURE_2 { 398 ON_RESULT_FAILURE_2 {
399 m_page_table.Finalize(); 399 m_page_table.Finalize();
@@ -1128,7 +1128,7 @@ KProcess::KProcess(KernelCore& kernel)
1128KProcess::~KProcess() = default; 1128KProcess::~KProcess() = default;
1129 1129
1130Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, 1130Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
1131 bool is_hbl) { 1131 KProcessAddress aslr_space_start, bool is_hbl) {
1132 // Create a resource limit for the process. 1132 // Create a resource limit for the process.
1133 const auto physical_memory_size = 1133 const auto physical_memory_size =
1134 m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); 1134 m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
@@ -1179,7 +1179,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
1179 .name = {}, 1179 .name = {},
1180 .version = {}, 1180 .version = {},
1181 .program_id = metadata.GetTitleID(), 1181 .program_id = metadata.GetTitleID(),
1182 .code_address = code_address, 1182 .code_address = code_address + GetInteger(aslr_space_start),
1183 .code_num_pages = static_cast<s32>(code_size / PageSize), 1183 .code_num_pages = static_cast<s32>(code_size / PageSize),
1184 .flags = flag, 1184 .flags = flag,
1185 .reslimit = Svc::InvalidHandle, 1185 .reslimit = Svc::InvalidHandle,
@@ -1193,7 +1193,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
1193 1193
1194 // Initialize for application process. 1194 // Initialize for application process.
1195 R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, 1195 R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit,
1196 KMemoryManager::Pool::Application)); 1196 KMemoryManager::Pool::Application, aslr_space_start));
1197 1197
1198 // Assign remaining properties. 1198 // Assign remaining properties.
1199 m_is_hbl = is_hbl; 1199 m_is_hbl = is_hbl;
@@ -1214,6 +1214,17 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
1214 ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute); 1214 ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute);
1215 ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read); 1215 ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read);
1216 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); 1216 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
1217
1218#ifdef HAS_NCE
1219 if (Settings::IsNceEnabled()) {
1220 auto& buffer = m_kernel.System().DeviceMemory().buffer;
1221 const auto& code = code_set.CodeSegment();
1222 const auto& patch = code_set.PatchSegment();
1223 buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true);
1224 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true);
1225 ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
1226 }
1227#endif
1217} 1228}
1218 1229
1219bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) { 1230bool KProcess::InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type) {
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 8339465fd..d8cd0fdde 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -120,6 +120,9 @@ private:
120 std::atomic<s64> m_num_ipc_messages{}; 120 std::atomic<s64> m_num_ipc_messages{};
121 std::atomic<s64> m_num_ipc_replies{}; 121 std::atomic<s64> m_num_ipc_replies{};
122 std::atomic<s64> m_num_ipc_receives{}; 122 std::atomic<s64> m_num_ipc_receives{};
123#ifdef HAS_NCE
124 std::unordered_map<u64, u64> m_post_handlers{};
125#endif
123 126
124private: 127private:
125 Result StartTermination(); 128 Result StartTermination();
@@ -150,7 +153,8 @@ public:
150 std::span<const u32> caps, KResourceLimit* res_limit, 153 std::span<const u32> caps, KResourceLimit* res_limit,
151 KMemoryManager::Pool pool, bool immortal); 154 KMemoryManager::Pool pool, bool immortal);
152 Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps, 155 Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps,
153 KResourceLimit* res_limit, KMemoryManager::Pool pool); 156 KResourceLimit* res_limit, KMemoryManager::Pool pool,
157 KProcessAddress aslr_space_start);
154 void Exit(); 158 void Exit();
155 159
156 const char* GetName() const { 160 const char* GetName() const {
@@ -466,6 +470,12 @@ public:
466 470
467 static void Switch(KProcess* cur_process, KProcess* next_process); 471 static void Switch(KProcess* cur_process, KProcess* next_process);
468 472
473#ifdef HAS_NCE
474 std::unordered_map<u64, u64>& GetPostHandlers() noexcept {
475 return m_post_handlers;
476 }
477#endif
478
469public: 479public:
470 // Attempts to insert a watchpoint into a free slot. Returns false if none are available. 480 // Attempts to insert a watchpoint into a free slot. Returns false if none are available.
471 bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type); 481 bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
@@ -479,7 +489,7 @@ public:
479 489
480public: 490public:
481 Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, 491 Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
482 bool is_hbl); 492 KProcessAddress aslr_space_start, bool is_hbl);
483 493
484 void LoadModule(CodeSet code_set, KProcessAddress base_addr); 494 void LoadModule(CodeSet code_set, KProcessAddress base_addr);
485 495
diff --git a/src/core/hle/kernel/k_process_page_table.h b/src/core/hle/kernel/k_process_page_table.h
index b7ae5abd0..9e40f68bc 100644
--- a/src/core/hle/kernel/k_process_page_table.h
+++ b/src/core/hle/kernel/k_process_page_table.h
@@ -23,10 +23,11 @@ public:
23 Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, 23 Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge,
24 bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, 24 bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address,
25 size_t code_size, KSystemResource* system_resource, 25 size_t code_size, KSystemResource* system_resource,
26 KResourceLimit* resource_limit, Core::Memory::Memory& memory) { 26 KResourceLimit* resource_limit, Core::Memory::Memory& memory,
27 R_RETURN(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, 27 KProcessAddress aslr_space_start) {
28 from_back, pool, code_address, code_size, 28 R_RETURN(m_page_table.InitializeForProcess(
29 system_resource, resource_limit, memory)); 29 as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size,
30 system_resource, resource_limit, memory, aslr_space_start));
30 } 31 }
31 32
32 void Finalize() { 33 void Finalize() {
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index e1f80b04f..e9ca5dfca 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -655,6 +655,21 @@ public:
655 return m_stack_top; 655 return m_stack_top;
656 } 656 }
657 657
658public:
659 // TODO: This shouldn't be defined in kernel namespace
660 struct NativeExecutionParameters {
661 u64 tpidr_el0{};
662 u64 tpidrro_el0{};
663 void* native_context{};
664 std::atomic<u32> lock{1};
665 bool is_running{};
666 u32 magic{Common::MakeMagic('Y', 'U', 'Z', 'U')};
667 };
668
669 NativeExecutionParameters& GetNativeExecutionParameters() {
670 return m_native_execution_parameters;
671 }
672
658private: 673private:
659 KThread* RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key, 674 KThread* RemoveWaiterByKey(bool* out_has_waiters, KProcessAddress key,
660 bool is_kernel_address_key); 675 bool is_kernel_address_key);
@@ -914,6 +929,7 @@ private:
914 ThreadWaitReasonForDebugging m_wait_reason_for_debugging{}; 929 ThreadWaitReasonForDebugging m_wait_reason_for_debugging{};
915 uintptr_t m_argument{}; 930 uintptr_t m_argument{};
916 KProcessAddress m_stack_top{}; 931 KProcessAddress m_stack_top{};
932 NativeExecutionParameters m_native_execution_parameters{};
917 933
918public: 934public:
919 using ConditionVariableThreadTreeType = ConditionVariableThreadTree; 935 using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 5ee869fa2..073039825 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -1,8 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 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/settings.h"
4#include "core/arm/dynarmic/arm_dynarmic_32.h" 5#include "core/arm/dynarmic/arm_dynarmic_32.h"
5#include "core/arm/dynarmic/arm_dynarmic_64.h" 6#include "core/arm/dynarmic/arm_dynarmic_64.h"
7#ifdef HAS_NCE
8#include "core/arm/nce/arm_nce.h"
9#endif
6#include "core/core.h" 10#include "core/core.h"
7#include "core/hle/kernel/k_scheduler.h" 11#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
@@ -14,7 +18,8 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu
14 : m_core_index{core_index}, m_system{system}, m_scheduler{scheduler} { 18 : m_core_index{core_index}, m_system{system}, m_scheduler{scheduler} {
15#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) 19#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
16 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with 20 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with
17 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. 21 // an NCE interface or a 32-bit instance of Dynarmic. This should be abstracted out to a CPU
22 // manager.
18 auto& kernel = system.Kernel(); 23 auto& kernel = system.Kernel();
19 m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( 24 m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
20 system, kernel.IsMulticore(), 25 system, kernel.IsMulticore(),
@@ -28,6 +33,13 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu
28PhysicalCore::~PhysicalCore() = default; 33PhysicalCore::~PhysicalCore() = default;
29 34
30void PhysicalCore::Initialize(bool is_64_bit) { 35void PhysicalCore::Initialize(bool is_64_bit) {
36#if defined(HAS_NCE)
37 if (Settings::IsNceEnabled()) {
38 m_arm_interface = std::make_unique<Core::ARM_NCE>(m_system, m_system.Kernel().IsMulticore(),
39 m_core_index);
40 return;
41 }
42#endif
31#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) 43#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
32 auto& kernel = m_system.Kernel(); 44 auto& kernel = m_system.Kernel();
33 if (!is_64_bit) { 45 if (!is_64_bit) {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index cc643ea09..a266d7c21 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -13,6 +13,7 @@
13#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
14#include "core/file_sys/registered_cache.h" 14#include "core/file_sys/registered_cache.h"
15#include "core/file_sys/savedata_factory.h" 15#include "core/file_sys/savedata_factory.h"
16#include "core/hid/hid_types.h"
16#include "core/hle/kernel/k_event.h" 17#include "core/hle/kernel/k_event.h"
17#include "core/hle/kernel/k_transfer_memory.h" 18#include "core/hle/kernel/k_transfer_memory.h"
18#include "core/hle/result.h" 19#include "core/hle/result.h"
@@ -21,6 +22,7 @@
21#include "core/hle/service/am/applet_ae.h" 22#include "core/hle/service/am/applet_ae.h"
22#include "core/hle/service/am/applet_oe.h" 23#include "core/hle/service/am/applet_oe.h"
23#include "core/hle/service/am/applets/applet_cabinet.h" 24#include "core/hle/service/am/applets/applet_cabinet.h"
25#include "core/hle/service/am/applets/applet_controller.h"
24#include "core/hle/service/am/applets/applet_mii_edit_types.h" 26#include "core/hle/service/am/applets/applet_mii_edit_types.h"
25#include "core/hle/service/am/applets/applet_profile_select.h" 27#include "core/hle/service/am/applets/applet_profile_select.h"
26#include "core/hle/service/am/applets/applet_software_keyboard_types.h" 28#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
@@ -35,6 +37,7 @@
35#include "core/hle/service/caps/caps_su.h" 37#include "core/hle/service/caps/caps_su.h"
36#include "core/hle/service/caps/caps_types.h" 38#include "core/hle/service/caps/caps_types.h"
37#include "core/hle/service/filesystem/filesystem.h" 39#include "core/hle/service/filesystem/filesystem.h"
40#include "core/hle/service/hid/controllers/npad.h"
38#include "core/hle/service/ipc_helpers.h" 41#include "core/hle/service/ipc_helpers.h"
39#include "core/hle/service/ns/ns.h" 42#include "core/hle/service/ns/ns.h"
40#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" 43#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@@ -73,7 +76,7 @@ IWindowController::IWindowController(Core::System& system_)
73 static const FunctionInfo functions[] = { 76 static const FunctionInfo functions[] = {
74 {0, nullptr, "CreateWindow"}, 77 {0, nullptr, "CreateWindow"},
75 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, 78 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
76 {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"}, 79 {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"},
77 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, 80 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
78 {11, nullptr, "ReleaseForegroundRights"}, 81 {11, nullptr, "ReleaseForegroundRights"},
79 {12, nullptr, "RejectToChangeIntoBackground"}, 82 {12, nullptr, "RejectToChangeIntoBackground"},
@@ -97,6 +100,16 @@ void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
97 rb.Push<u64>(process_id); 100 rb.Push<u64>(process_id);
98} 101}
99 102
103void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
104 const u64 process_id = 0;
105
106 LOG_WARNING(Service_AM, "(STUBBED) called");
107
108 IPC::ResponseBuilder rb{ctx, 4};
109 rb.Push(ResultSuccess);
110 rb.Push<u64>(process_id);
111}
112
100void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { 113void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
101 LOG_WARNING(Service_AM, "(STUBBED) called"); 114 LOG_WARNING(Service_AM, "(STUBBED) called");
102 IPC::ResponseBuilder rb{ctx, 2}; 115 IPC::ResponseBuilder rb{ctx, 2};
@@ -1565,7 +1578,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
1565 {6, nullptr, "GetPopInteractiveInDataEvent"}, 1578 {6, nullptr, "GetPopInteractiveInDataEvent"},
1566 {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, 1579 {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
1567 {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, 1580 {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
1568 {12, nullptr, "GetMainAppletIdentityInfo"}, 1581 {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
1569 {13, nullptr, "CanUseApplicationCore"}, 1582 {13, nullptr, "CanUseApplicationCore"},
1570 {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, 1583 {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
1571 {15, nullptr, "GetMainAppletApplicationControlProperty"}, 1584 {15, nullptr, "GetMainAppletApplicationControlProperty"},
@@ -1609,6 +1622,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
1609 case Applets::AppletId::SoftwareKeyboard: 1622 case Applets::AppletId::SoftwareKeyboard:
1610 PushInShowSoftwareKeyboard(); 1623 PushInShowSoftwareKeyboard();
1611 break; 1624 break;
1625 case Applets::AppletId::Controller:
1626 PushInShowController();
1627 break;
1612 default: 1628 default:
1613 break; 1629 break;
1614 } 1630 }
@@ -1666,13 +1682,33 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
1666 rb.PushRaw(applet_info); 1682 rb.PushRaw(applet_info);
1667} 1683}
1668 1684
1669void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { 1685void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
1670 struct AppletIdentityInfo { 1686 struct AppletIdentityInfo {
1671 Applets::AppletId applet_id; 1687 Applets::AppletId applet_id;
1672 INSERT_PADDING_BYTES(0x4); 1688 INSERT_PADDING_BYTES(0x4);
1673 u64 application_id; 1689 u64 application_id;
1674 }; 1690 };
1691 static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
1692
1693 LOG_WARNING(Service_AM, "(STUBBED) called");
1694
1695 const AppletIdentityInfo applet_info{
1696 .applet_id = Applets::AppletId::QLaunch,
1697 .application_id = 0x0100000000001000ull,
1698 };
1699
1700 IPC::ResponseBuilder rb{ctx, 6};
1701 rb.Push(ResultSuccess);
1702 rb.PushRaw(applet_info);
1703}
1675 1704
1705void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
1706 struct AppletIdentityInfo {
1707 Applets::AppletId applet_id;
1708 INSERT_PADDING_BYTES(0x4);
1709 u64 application_id;
1710 };
1711 static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
1676 LOG_WARNING(Service_AM, "(STUBBED) called"); 1712 LOG_WARNING(Service_AM, "(STUBBED) called");
1677 1713
1678 const AppletIdentityInfo applet_info{ 1714 const AppletIdentityInfo applet_info{
@@ -1737,6 +1773,55 @@ void ILibraryAppletSelfAccessor::PushInShowAlbum() {
1737 queue_data.emplace_back(std::move(settings_data)); 1773 queue_data.emplace_back(std::move(settings_data));
1738} 1774}
1739 1775
1776void ILibraryAppletSelfAccessor::PushInShowController() {
1777 const Applets::CommonArguments common_args = {
1778 .arguments_version = Applets::CommonArgumentVersion::Version3,
1779 .size = Applets::CommonArgumentSize::Version3,
1780 .library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8),
1781 .theme_color = Applets::ThemeColor::BasicBlack,
1782 .play_startup_sound = true,
1783 .system_tick = system.CoreTiming().GetClockTicks(),
1784 };
1785
1786 Applets::ControllerSupportArgNew user_args = {
1787 .header = {.player_count_min = 1,
1788 .player_count_max = 4,
1789 .enable_take_over_connection = true,
1790 .enable_left_justify = false,
1791 .enable_permit_joy_dual = true,
1792 .enable_single_mode = false,
1793 .enable_identification_color = false},
1794 .identification_colors = {},
1795 .enable_explain_text = false,
1796 .explain_text = {},
1797 };
1798
1799 Applets::ControllerSupportArgPrivate private_args = {
1800 .arg_private_size = sizeof(Applets::ControllerSupportArgPrivate),
1801 .arg_size = sizeof(Applets::ControllerSupportArgNew),
1802 .is_home_menu = true,
1803 .flag_1 = true,
1804 .mode = Applets::ControllerSupportMode::ShowControllerSupport,
1805 .caller = Applets::ControllerSupportCaller::
1806 Application, // switchbrew: Always zero except with
1807 // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
1808 // which sets this to the input param
1809 .style_set = Core::HID::NpadStyleSet::None,
1810 .joy_hold_type = 0,
1811 };
1812 std::vector<u8> common_args_data(sizeof(common_args));
1813 std::vector<u8> private_args_data(sizeof(private_args));
1814 std::vector<u8> user_args_data(sizeof(user_args));
1815
1816 std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
1817 std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
1818 std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
1819
1820 queue_data.emplace_back(std::move(common_args_data));
1821 queue_data.emplace_back(std::move(private_args_data));
1822 queue_data.emplace_back(std::move(user_args_data));
1823}
1824
1740void ILibraryAppletSelfAccessor::PushInShowCabinetData() { 1825void ILibraryAppletSelfAccessor::PushInShowCabinetData() {
1741 const Applets::CommonArguments arguments{ 1826 const Applets::CommonArguments arguments{
1742 .arguments_version = Applets::CommonArgumentVersion::Version3, 1827 .arguments_version = Applets::CommonArgumentVersion::Version3,
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 8f8cb8a9e..905a71b9f 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -87,6 +87,7 @@ public:
87 87
88private: 88private:
89 void GetAppletResourceUserId(HLERequestContext& ctx); 89 void GetAppletResourceUserId(HLERequestContext& ctx);
90 void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
90 void AcquireForegroundRights(HLERequestContext& ctx); 91 void AcquireForegroundRights(HLERequestContext& ctx);
91}; 92};
92 93
@@ -345,6 +346,7 @@ private:
345 void PopInData(HLERequestContext& ctx); 346 void PopInData(HLERequestContext& ctx);
346 void PushOutData(HLERequestContext& ctx); 347 void PushOutData(HLERequestContext& ctx);
347 void GetLibraryAppletInfo(HLERequestContext& ctx); 348 void GetLibraryAppletInfo(HLERequestContext& ctx);
349 void GetMainAppletIdentityInfo(HLERequestContext& ctx);
348 void ExitProcessAndReturn(HLERequestContext& ctx); 350 void ExitProcessAndReturn(HLERequestContext& ctx);
349 void GetCallerAppletIdentityInfo(HLERequestContext& ctx); 351 void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
350 void GetDesirableKeyboardLayout(HLERequestContext& ctx); 352 void GetDesirableKeyboardLayout(HLERequestContext& ctx);
@@ -355,6 +357,7 @@ private:
355 void PushInShowCabinetData(); 357 void PushInShowCabinetData();
356 void PushInShowMiiEditData(); 358 void PushInShowMiiEditData();
357 void PushInShowSoftwareKeyboard(); 359 void PushInShowSoftwareKeyboard();
360 void PushInShowController();
358 361
359 std::deque<std::vector<u8>> queue_data; 362 std::deque<std::vector<u8>> queue_data;
360}; 363};
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
index b379dadeb..3906c0fa4 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.cpp
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -122,7 +122,8 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
122 Service::NFP::RegisterInfoPrivate register_info{}; 122 Service::NFP::RegisterInfoPrivate register_info{};
123 std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), 123 std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
124 std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); 124 std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
125 125 register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
126 register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'});
126 nfp_device->SetRegisterInfoPrivate(register_info); 127 nfp_device->SetRegisterInfoPrivate(register_info);
127 break; 128 break;
128 } 129 }
@@ -130,7 +131,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
130 nfp_device->DeleteApplicationArea(); 131 nfp_device->DeleteApplicationArea();
131 break; 132 break;
132 case Service::NFP::CabinetMode::StartRestorer: 133 case Service::NFP::CabinetMode::StartRestorer:
133 nfp_device->RestoreAmiibo(); 134 nfp_device->Restore();
134 break; 135 break;
135 case Service::NFP::CabinetMode::StartFormatter: 136 case Service::NFP::CabinetMode::StartFormatter:
136 nfp_device->Format(); 137 nfp_device->Format();
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index f6c64f633..9f839f3d7 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -56,7 +56,7 @@ enum class ControllerSupportResult : u32 {
56struct ControllerSupportArgPrivate { 56struct ControllerSupportArgPrivate {
57 u32 arg_private_size{}; 57 u32 arg_private_size{};
58 u32 arg_size{}; 58 u32 arg_size{};
59 bool flag_0{}; 59 bool is_home_menu{};
60 bool flag_1{}; 60 bool flag_1{};
61 ControllerSupportMode mode{}; 61 ControllerSupportMode mode{};
62 ControllerSupportCaller caller{}; 62 ControllerSupportCaller caller{};
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 8069f75b7..c65e32489 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -127,7 +127,7 @@ public:
127 127
128private: 128private:
129 void GetCore(HLERequestContext& ctx) { 129 void GetCore(HLERequestContext& ctx) {
130 LOG_DEBUG(Service_BTM, "called"); 130 LOG_WARNING(Service_BTM, "called");
131 131
132 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 132 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
133 rb.Push(ResultSuccess); 133 rb.Push(ResultSuccess);
@@ -263,13 +263,13 @@ public:
263 explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} { 263 explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} {
264 // clang-format off 264 // clang-format off
265 static const FunctionInfo functions[] = { 265 static const FunctionInfo functions[] = {
266 {0, nullptr, "StartGamepadPairing"}, 266 {0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"},
267 {1, nullptr, "CancelGamepadPairing"}, 267 {1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"},
268 {2, nullptr, "ClearGamepadPairingDatabase"}, 268 {2, nullptr, "ClearGamepadPairingDatabase"},
269 {3, nullptr, "GetPairedGamepadCount"}, 269 {3, nullptr, "GetPairedGamepadCount"},
270 {4, nullptr, "EnableRadio"}, 270 {4, nullptr, "EnableRadio"},
271 {5, nullptr, "DisableRadio"}, 271 {5, nullptr, "DisableRadio"},
272 {6, nullptr, "GetRadioOnOff"}, 272 {6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"},
273 {7, nullptr, "AcquireRadioEvent"}, 273 {7, nullptr, "AcquireRadioEvent"},
274 {8, nullptr, "AcquireGamepadPairingEvent"}, 274 {8, nullptr, "AcquireGamepadPairingEvent"},
275 {9, nullptr, "IsGamepadPairingStarted"}, 275 {9, nullptr, "IsGamepadPairingStarted"},
@@ -280,18 +280,58 @@ public:
280 {14, nullptr, "AcquireAudioDeviceConnectionEvent"}, 280 {14, nullptr, "AcquireAudioDeviceConnectionEvent"},
281 {15, nullptr, "ConnectAudioDevice"}, 281 {15, nullptr, "ConnectAudioDevice"},
282 {16, nullptr, "IsConnectingAudioDevice"}, 282 {16, nullptr, "IsConnectingAudioDevice"},
283 {17, nullptr, "GetConnectedAudioDevices"}, 283 {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
284 {18, nullptr, "DisconnectAudioDevice"}, 284 {18, nullptr, "DisconnectAudioDevice"},
285 {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, 285 {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
286 {20, nullptr, "GetPairedAudioDevices"}, 286 {20, nullptr, "GetPairedAudioDevices"},
287 {21, nullptr, "RemoveAudioDevicePairing"}, 287 {21, nullptr, "RemoveAudioDevicePairing"},
288 {22, nullptr, "RequestAudioDeviceConnectionRejection"}, 288 {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
289 {23, nullptr, "CancelAudioDeviceConnectionRejection"} 289 {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
290 }; 290 };
291 // clang-format on 291 // clang-format on
292 292
293 RegisterHandlers(functions); 293 RegisterHandlers(functions);
294 } 294 }
295
296private:
297 void IsRadioEnabled(HLERequestContext& ctx) {
298 LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running
299
300 IPC::ResponseBuilder rb{ctx, 3};
301 rb.Push(ResultSuccess);
302 rb.Push(true);
303 }
304
305 void StartGamepadPairing(HLERequestContext& ctx) {
306 LOG_WARNING(Service_BTM, "(STUBBED) called");
307 IPC::ResponseBuilder rb{ctx, 2};
308 rb.Push(ResultSuccess);
309 }
310
311 void CancelGamepadPairing(HLERequestContext& ctx) {
312 LOG_WARNING(Service_BTM, "(STUBBED) called");
313 IPC::ResponseBuilder rb{ctx, 2};
314 rb.Push(ResultSuccess);
315 }
316
317 void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) {
318 LOG_WARNING(Service_BTM, "(STUBBED) called");
319 IPC::ResponseBuilder rb{ctx, 2};
320 rb.Push(ResultSuccess);
321 }
322
323 void GetConnectedAudioDevices(HLERequestContext& ctx) {
324 LOG_WARNING(Service_BTM, "(STUBBED) called");
325 IPC::ResponseBuilder rb{ctx, 3};
326 rb.Push(ResultSuccess);
327 rb.Push<u32>(0);
328 }
329
330 void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
331 LOG_WARNING(Service_BTM, "(STUBBED) called");
332 IPC::ResponseBuilder rb{ctx, 2};
333 rb.Push(ResultSuccess);
334 }
295}; 335};
296 336
297class BTM_SYS final : public ServiceFramework<BTM_SYS> { 337class BTM_SYS final : public ServiceFramework<BTM_SYS> {
@@ -308,7 +348,7 @@ public:
308 348
309private: 349private:
310 void GetCore(HLERequestContext& ctx) { 350 void GetCore(HLERequestContext& ctx) {
311 LOG_DEBUG(Service_BTM, "called"); 351 LOG_WARNING(Service_BTM, "called");
312 352
313 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 353 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
314 rb.Push(ResultSuccess); 354 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 9d05f9801..0507b14e7 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -32,7 +32,7 @@ public:
32 {10200, nullptr, "SendFriendRequestForApplication"}, 32 {10200, nullptr, "SendFriendRequestForApplication"},
33 {10211, nullptr, "AddFacedFriendRequestForApplication"}, 33 {10211, nullptr, "AddFacedFriendRequestForApplication"},
34 {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, 34 {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
35 {10420, nullptr, "IsBlockedUserListCacheAvailable"}, 35 {10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"},
36 {10421, nullptr, "EnsureBlockedUserListAvailable"}, 36 {10421, nullptr, "EnsureBlockedUserListAvailable"},
37 {10500, nullptr, "GetProfileList"}, 37 {10500, nullptr, "GetProfileList"},
38 {10600, nullptr, "DeclareOpenOnlinePlaySession"}, 38 {10600, nullptr, "DeclareOpenOnlinePlaySession"},
@@ -206,6 +206,17 @@ private:
206 rb.Push(true); 206 rb.Push(true);
207 } 207 }
208 208
209 void CheckBlockedUserListAvailability(HLERequestContext& ctx) {
210 IPC::RequestParser rp{ctx};
211 const auto uuid{rp.PopRaw<Common::UUID>()};
212
213 LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());
214
215 IPC::ResponseBuilder rb{ctx, 3};
216 rb.Push(ResultSuccess);
217 rb.Push(true);
218 }
219
209 KernelHelpers::ServiceContext service_context; 220 KernelHelpers::ServiceContext service_context;
210 221
211 Kernel::KEvent* completion_event; 222 Kernel::KEvent* completion_event;
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp
new file mode 100644
index 000000000..b2bf1d78d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/console_six_axis.cpp
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hid/emulated_console.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/console_six_axis.h"
9#include "core/memory.h"
10
11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
13
14ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
15 : ControllerBase{hid_core_} {
16 console = hid_core.GetEmulatedConsole();
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
18 "ConsoleSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at(
20 reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
21}
22
23ConsoleSixAxis::~ConsoleSixAxis() = default;
24
25void ConsoleSixAxis::OnInit() {}
26
27void ConsoleSixAxis::OnRelease() {}
28
29void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 if (!IsControllerActivated()) {
31 return;
32 }
33
34 const auto motion_status = console->GetMotion();
35
36 shared_memory->sampling_number++;
37 shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
38 shared_memory->verticalization_error = motion_status.verticalization_error;
39 shared_memory->gyro_bias = motion_status.gyro_bias;
40}
41
42} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h
new file mode 100644
index 000000000..5b7c6a29a
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/console_six_axis.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/vector_math.h"
7#include "core/hle/service/hid/controllers/controller_base.h"
8
9namespace Core::HID {
10class EmulatedConsole;
11} // namespace Core::HID
12
13namespace Service::HID {
14class ConsoleSixAxis final : public ControllerBase {
15public:
16 explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
17 ~ConsoleSixAxis() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
27
28private:
29 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
30 struct ConsoleSharedMemory {
31 u64 sampling_number{};
32 bool is_seven_six_axis_sensor_at_rest{};
33 INSERT_PADDING_BYTES(3); // padding
34 f32 verticalization_error{};
35 Common::Vec3f gyro_bias{};
36 INSERT_PADDING_BYTES(4); // padding
37 };
38 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
39
40 ConsoleSharedMemory* shared_memory = nullptr;
41 Core::HID::EmulatedConsole* console = nullptr;
42};
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 8ec9f4a95..9de19ebfc 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -13,7 +13,7 @@
13namespace Service::HID { 13namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
15 15
16Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 16DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
17 : ControllerBase{hid_core_} { 17 : ControllerBase{hid_core_} {
18 static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, 18 static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
19 "DebugPadSharedMemory is bigger than the shared memory"); 19 "DebugPadSharedMemory is bigger than the shared memory");
@@ -22,13 +22,13 @@ Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_
22 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 22 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
23} 23}
24 24
25Controller_DebugPad::~Controller_DebugPad() = default; 25DebugPad::~DebugPad() = default;
26 26
27void Controller_DebugPad::OnInit() {} 27void DebugPad::OnInit() {}
28 28
29void Controller_DebugPad::OnRelease() {} 29void DebugPad::OnRelease() {}
30 30
31void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 31void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
32 if (!IsControllerActivated()) { 32 if (!IsControllerActivated()) {
33 shared_memory->debug_pad_lifo.buffer_count = 0; 33 shared_memory->debug_pad_lifo.buffer_count = 0;
34 shared_memory->debug_pad_lifo.buffer_tail = 0; 34 shared_memory->debug_pad_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 68ff0ea79..5566dba77 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -15,10 +15,10 @@ struct AnalogStickState;
15} // namespace Core::HID 15} // namespace Core::HID
16 16
17namespace Service::HID { 17namespace Service::HID {
18class Controller_DebugPad final : public ControllerBase { 18class DebugPad final : public ControllerBase {
19public: 19public:
20 explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 20 explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
21 ~Controller_DebugPad() override; 21 ~DebugPad() override;
22 22
23 // Called when the controller is initialized 23 // Called when the controller is initialized
24 void OnInit() override; 24 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 63eecd42b..59b2ec73c 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -23,7 +23,7 @@ constexpr f32 Square(s32 num) {
23 return static_cast<f32>(num * num); 23 return static_cast<f32>(num * num);
24} 24}
25 25
26Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 26Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
27 : ControllerBase(hid_core_) { 27 : ControllerBase(hid_core_) {
28 static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, 28 static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
29 "GestureSharedMemory is bigger than the shared memory"); 29 "GestureSharedMemory is bigger than the shared memory");
@@ -31,17 +31,17 @@ Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_sh
31 reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); 31 reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
32 console = hid_core.GetEmulatedConsole(); 32 console = hid_core.GetEmulatedConsole();
33} 33}
34Controller_Gesture::~Controller_Gesture() = default; 34Gesture::~Gesture() = default;
35 35
36void Controller_Gesture::OnInit() { 36void Gesture::OnInit() {
37 shared_memory->gesture_lifo.buffer_count = 0; 37 shared_memory->gesture_lifo.buffer_count = 0;
38 shared_memory->gesture_lifo.buffer_tail = 0; 38 shared_memory->gesture_lifo.buffer_tail = 0;
39 force_update = true; 39 force_update = true;
40} 40}
41 41
42void Controller_Gesture::OnRelease() {} 42void Gesture::OnRelease() {}
43 43
44void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 44void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
45 if (!IsControllerActivated()) { 45 if (!IsControllerActivated()) {
46 shared_memory->gesture_lifo.buffer_count = 0; 46 shared_memory->gesture_lifo.buffer_count = 0;
47 shared_memory->gesture_lifo.buffer_tail = 0; 47 shared_memory->gesture_lifo.buffer_tail = 0;
@@ -64,7 +64,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
64 UpdateGestureSharedMemory(gesture, time_difference); 64 UpdateGestureSharedMemory(gesture, time_difference);
65} 65}
66 66
67void Controller_Gesture::ReadTouchInput() { 67void Gesture::ReadTouchInput() {
68 if (!Settings::values.touchscreen.enabled) { 68 if (!Settings::values.touchscreen.enabled) {
69 fingers = {}; 69 fingers = {};
70 return; 70 return;
@@ -76,8 +76,7 @@ void Controller_Gesture::ReadTouchInput() {
76 } 76 }
77} 77}
78 78
79bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, 79bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) {
80 f32 time_difference) {
81 const auto& last_entry = GetLastGestureEntry(); 80 const auto& last_entry = GetLastGestureEntry();
82 if (force_update) { 81 if (force_update) {
83 force_update = false; 82 force_update = false;
@@ -100,8 +99,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
100 return false; 99 return false;
101} 100}
102 101
103void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, 102void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) {
104 f32 time_difference) {
105 GestureType type = GestureType::Idle; 103 GestureType type = GestureType::Idle;
106 GestureAttribute attributes{}; 104 GestureAttribute attributes{};
107 105
@@ -138,8 +136,8 @@ void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
138 shared_memory->gesture_lifo.WriteNextEntry(next_state); 136 shared_memory->gesture_lifo.WriteNextEntry(next_state);
139} 137}
140 138
141void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type, 139void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
142 GestureAttribute& attributes) { 140 GestureAttribute& attributes) {
143 const auto& last_entry = GetLastGestureEntry(); 141 const auto& last_entry = GetLastGestureEntry();
144 142
145 gesture.detection_count++; 143 gesture.detection_count++;
@@ -152,8 +150,8 @@ void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& typ
152 } 150 }
153} 151}
154 152
155void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, 153void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
156 f32 time_difference) { 154 f32 time_difference) {
157 const auto& last_entry = GetLastGestureEntry(); 155 const auto& last_entry = GetLastGestureEntry();
158 156
159 // Promote to pan type if touch moved 157 // Promote to pan type if touch moved
@@ -186,9 +184,8 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Gestu
186 } 184 }
187} 185}
188 186
189void Controller_Gesture::EndGesture(GestureProperties& gesture, 187void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
190 GestureProperties& last_gesture_props, GestureType& type, 188 GestureType& type, GestureAttribute& attributes, f32 time_difference) {
191 GestureAttribute& attributes, f32 time_difference) {
192 const auto& last_entry = GetLastGestureEntry(); 189 const auto& last_entry = GetLastGestureEntry();
193 190
194 if (last_gesture_props.active_points != 0) { 191 if (last_gesture_props.active_points != 0) {
@@ -222,9 +219,8 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
222 } 219 }
223} 220}
224 221
225void Controller_Gesture::SetTapEvent(GestureProperties& gesture, 222void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
226 GestureProperties& last_gesture_props, GestureType& type, 223 GestureType& type, GestureAttribute& attributes) {
227 GestureAttribute& attributes) {
228 type = GestureType::Tap; 224 type = GestureType::Tap;
229 gesture = last_gesture_props; 225 gesture = last_gesture_props;
230 force_update = true; 226 force_update = true;
@@ -236,9 +232,8 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
236 } 232 }
237} 233}
238 234
239void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, 235void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
240 GestureProperties& last_gesture_props, GestureType& type, 236 GestureType& type, f32 time_difference) {
241 f32 time_difference) {
242 const auto& last_entry = GetLastGestureEntry(); 237 const auto& last_entry = GetLastGestureEntry();
243 238
244 next_state.delta = gesture.mid_point - last_entry.pos; 239 next_state.delta = gesture.mid_point - last_entry.pos;
@@ -263,9 +258,8 @@ void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
263 } 258 }
264} 259}
265 260
266void Controller_Gesture::EndPanEvent(GestureProperties& gesture, 261void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
267 GestureProperties& last_gesture_props, GestureType& type, 262 GestureType& type, f32 time_difference) {
268 f32 time_difference) {
269 const auto& last_entry = GetLastGestureEntry(); 263 const auto& last_entry = GetLastGestureEntry();
270 next_state.vel_x = 264 next_state.vel_x =
271 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); 265 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
@@ -287,8 +281,8 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
287 force_update = true; 281 force_update = true;
288} 282}
289 283
290void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, 284void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
291 GestureProperties& last_gesture_props, GestureType& type) { 285 GestureType& type) {
292 const auto& last_entry = GetLastGestureEntry(); 286 const auto& last_entry = GetLastGestureEntry();
293 287
294 type = GestureType::Swipe; 288 type = GestureType::Swipe;
@@ -311,11 +305,11 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
311 next_state.direction = GestureDirection::Up; 305 next_state.direction = GestureDirection::Up;
312} 306}
313 307
314const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { 308const Gesture::GestureState& Gesture::GetLastGestureEntry() const {
315 return shared_memory->gesture_lifo.ReadCurrentEntry().state; 309 return shared_memory->gesture_lifo.ReadCurrentEntry().state;
316} 310}
317 311
318Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { 312Gesture::GestureProperties Gesture::GetGestureProperties() {
319 GestureProperties gesture; 313 GestureProperties gesture;
320 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; 314 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
321 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 315 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 0d6099ea0..4c6f8ee07 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -12,10 +12,10 @@
12#include "core/hle/service/hid/ring_lifo.h" 12#include "core/hle/service/hid/ring_lifo.h"
13 13
14namespace Service::HID { 14namespace Service::HID {
15class Controller_Gesture final : public ControllerBase { 15class Gesture final : public ControllerBase {
16public: 16public:
17 explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 17 explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
18 ~Controller_Gesture() override; 18 ~Gesture() override;
19 19
20 // Called when the controller is initialized 20 // Called when the controller is initialized
21 void OnInit() override; 21 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 117d87433..ddb1b0ba4 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -12,7 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
14 14
15Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 15Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
16 : ControllerBase{hid_core_} { 16 : ControllerBase{hid_core_} {
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, 17 static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
18 "KeyboardSharedMemory is bigger than the shared memory"); 18 "KeyboardSharedMemory is bigger than the shared memory");
@@ -21,13 +21,13 @@ Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_
21 emulated_devices = hid_core.GetEmulatedDevices(); 21 emulated_devices = hid_core.GetEmulatedDevices();
22} 22}
23 23
24Controller_Keyboard::~Controller_Keyboard() = default; 24Keyboard::~Keyboard() = default;
25 25
26void Controller_Keyboard::OnInit() {} 26void Keyboard::OnInit() {}
27 27
28void Controller_Keyboard::OnRelease() {} 28void Keyboard::OnRelease() {}
29 29
30void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 30void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
31 if (!IsControllerActivated()) { 31 if (!IsControllerActivated()) {
32 shared_memory->keyboard_lifo.buffer_count = 0; 32 shared_memory->keyboard_lifo.buffer_count = 0;
33 shared_memory->keyboard_lifo.buffer_tail = 0; 33 shared_memory->keyboard_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 7532f53c6..172ec1309 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -14,10 +14,10 @@ struct KeyboardKey;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Keyboard final : public ControllerBase { 17class Keyboard final : public ControllerBase {
18public: 18public:
19 explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
20 ~Controller_Keyboard() override; 20 ~Keyboard() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
23 void OnInit() override; 23 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 0afc66681..6e5a04e34 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -12,8 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 13constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
14 14
15Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 15Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
16 : ControllerBase{hid_core_} {
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, 16 static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
18 "MouseSharedMemory is bigger than the shared memory"); 17 "MouseSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at( 18 shared_memory = std::construct_at(
@@ -21,12 +20,12 @@ Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared
21 emulated_devices = hid_core.GetEmulatedDevices(); 20 emulated_devices = hid_core.GetEmulatedDevices();
22} 21}
23 22
24Controller_Mouse::~Controller_Mouse() = default; 23Mouse::~Mouse() = default;
25 24
26void Controller_Mouse::OnInit() {} 25void Mouse::OnInit() {}
27void Controller_Mouse::OnRelease() {} 26void Mouse::OnRelease() {}
28 27
29void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 28void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 if (!IsControllerActivated()) { 29 if (!IsControllerActivated()) {
31 shared_memory->mouse_lifo.buffer_count = 0; 30 shared_memory->mouse_lifo.buffer_count = 0;
32 shared_memory->mouse_lifo.buffer_tail = 0; 31 shared_memory->mouse_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 733d00577..a80f3823f 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -14,10 +14,10 @@ struct AnalogStickState;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Mouse final : public ControllerBase { 17class Mouse final : public ControllerBase {
18public: 18public:
19 explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
20 ~Controller_Mouse() override; 20 ~Mouse() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
23 void OnInit() override; 23 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index d46bf917e..08ee9de9c 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -18,6 +18,7 @@
18#include "core/hle/kernel/k_readable_event.h" 18#include "core/hle/kernel/k_readable_event.h"
19#include "core/hle/service/hid/controllers/npad.h" 19#include "core/hle/service/hid/controllers/npad.h"
20#include "core/hle/service/hid/errors.h" 20#include "core/hle/service/hid/errors.h"
21#include "core/hle/service/hid/hid_util.h"
21#include "core/hle/service/kernel_helpers.h" 22#include "core/hle/service/kernel_helpers.h"
22 23
23namespace Service::HID { 24namespace Service::HID {
@@ -29,60 +30,8 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
29 Core::HID::NpadIdType::Handheld, 30 Core::HID::NpadIdType::Handheld,
30}; 31};
31 32
32bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) { 33NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
33 switch (npad_id) { 34 KernelHelpers::ServiceContext& service_context_)
34 case Core::HID::NpadIdType::Player1:
35 case Core::HID::NpadIdType::Player2:
36 case Core::HID::NpadIdType::Player3:
37 case Core::HID::NpadIdType::Player4:
38 case Core::HID::NpadIdType::Player5:
39 case Core::HID::NpadIdType::Player6:
40 case Core::HID::NpadIdType::Player7:
41 case Core::HID::NpadIdType::Player8:
42 case Core::HID::NpadIdType::Other:
43 case Core::HID::NpadIdType::Handheld:
44 return true;
45 default:
46 LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
47 return false;
48 }
49}
50
51Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
52 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
53 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
54 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
55
56 if (!npad_type) {
57 return VibrationInvalidStyleIndex;
58 }
59 if (!npad_id) {
60 return VibrationInvalidNpadId;
61 }
62 if (!device_index) {
63 return VibrationDeviceIndexOutOfRange;
64 }
65
66 return ResultSuccess;
67}
68
69Result Controller_NPad::VerifyValidSixAxisSensorHandle(
70 const Core::HID::SixAxisSensorHandle& device_handle) {
71 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
72 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
73
74 if (!npad_id) {
75 return InvalidNpadId;
76 }
77 if (!device_index) {
78 return NpadDeviceIndexOutOfRange;
79 }
80
81 return ResultSuccess;
82}
83
84Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
85 KernelHelpers::ServiceContext& service_context_)
86 : ControllerBase{hid_core_}, service_context{service_context_} { 35 : ControllerBase{hid_core_}, service_context{service_context_} {
87 static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size); 36 static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);
88 for (std::size_t i = 0; i < controller_data.size(); ++i) { 37 for (std::size_t i = 0; i < controller_data.size(); ++i) {
@@ -103,7 +52,7 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_m
103 } 52 }
104} 53}
105 54
106Controller_NPad::~Controller_NPad() { 55NPad::~NPad() {
107 for (std::size_t i = 0; i < controller_data.size(); ++i) { 56 for (std::size_t i = 0; i < controller_data.size(); ++i) {
108 auto& controller = controller_data[i]; 57 auto& controller = controller_data[i];
109 controller.device->DeleteCallback(controller.callback_key); 58 controller.device->DeleteCallback(controller.callback_key);
@@ -111,8 +60,7 @@ Controller_NPad::~Controller_NPad() {
111 OnRelease(); 60 OnRelease();
112} 61}
113 62
114void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, 63void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) {
115 std::size_t controller_idx) {
116 if (type == Core::HID::ControllerTriggerType::All) { 64 if (type == Core::HID::ControllerTriggerType::All) {
117 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx); 65 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
118 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); 66 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
@@ -150,7 +98,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
150 } 98 }
151} 99}
152 100
153void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { 101void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
154 auto& controller = GetControllerFromNpadIdType(npad_id); 102 auto& controller = GetControllerFromNpadIdType(npad_id);
155 if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { 103 if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
156 return; 104 return;
@@ -344,12 +292,13 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
344 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, 292 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
345 Common::Input::PollingMode::Active); 293 Common::Input::PollingMode::Active);
346 } 294 }
295
347 SignalStyleSetChangedEvent(npad_id); 296 SignalStyleSetChangedEvent(npad_id);
348 WriteEmptyEntry(controller.shared_memory); 297 WriteEmptyEntry(controller.shared_memory);
349 hid_core.SetLastActiveController(npad_id); 298 hid_core.SetLastActiveController(npad_id);
350} 299}
351 300
352void Controller_NPad::OnInit() { 301void NPad::OnInit() {
353 if (!IsControllerActivated()) { 302 if (!IsControllerActivated()) {
354 return; 303 return;
355 } 304 }
@@ -383,7 +332,7 @@ void Controller_NPad::OnInit() {
383 } 332 }
384} 333}
385 334
386void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) { 335void NPad::WriteEmptyEntry(NpadInternalState* npad) {
387 NPadGenericState dummy_pad_state{}; 336 NPadGenericState dummy_pad_state{};
388 NpadGcTriggerState dummy_gc_state{}; 337 NpadGcTriggerState dummy_gc_state{};
389 dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; 338 dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
@@ -404,7 +353,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) {
404 npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); 353 npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
405} 354}
406 355
407void Controller_NPad::OnRelease() { 356void NPad::OnRelease() {
408 is_controller_initialized = false; 357 is_controller_initialized = false;
409 for (std::size_t i = 0; i < controller_data.size(); ++i) { 358 for (std::size_t i = 0; i < controller_data.size(); ++i) {
410 auto& controller = controller_data[i]; 359 auto& controller = controller_data[i];
@@ -415,7 +364,7 @@ void Controller_NPad::OnRelease() {
415 } 364 }
416} 365}
417 366
418void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { 367void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
419 std::scoped_lock lock{mutex}; 368 std::scoped_lock lock{mutex};
420 auto& controller = GetControllerFromNpadIdType(npad_id); 369 auto& controller = GetControllerFromNpadIdType(npad_id);
421 const auto controller_type = controller.device->GetNpadStyleIndex(); 370 const auto controller_type = controller.device->GetNpadStyleIndex();
@@ -484,7 +433,7 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
484 } 433 }
485} 434}
486 435
487void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 436void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
488 if (!IsControllerActivated()) { 437 if (!IsControllerActivated()) {
489 return; 438 return;
490 } 439 }
@@ -614,134 +563,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
614 } 563 }
615} 564}
616 565
617void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) { 566void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
618 if (!IsControllerActivated()) {
619 return;
620 }
621
622 for (std::size_t i = 0; i < controller_data.size(); ++i) {
623 auto& controller = controller_data[i];
624
625 const auto& controller_type = controller.device->GetNpadStyleIndex();
626
627 if (controller_type == Core::HID::NpadStyleIndex::None ||
628 !controller.device->IsConnected()) {
629 continue;
630 }
631
632 auto* npad = controller.shared_memory;
633 const auto& motion_state = controller.device->GetMotions();
634 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
635 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
636 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
637 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
638 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
639 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
640
641 // Clear previous state
642 sixaxis_fullkey_state = {};
643 sixaxis_handheld_state = {};
644 sixaxis_dual_left_state = {};
645 sixaxis_dual_right_state = {};
646 sixaxis_left_lifo_state = {};
647 sixaxis_right_lifo_state = {};
648
649 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
650 controller.sixaxis_at_rest = true;
651 for (std::size_t e = 0; e < motion_state.size(); ++e) {
652 controller.sixaxis_at_rest =
653 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
654 }
655 }
656
657 const auto set_motion_state = [&](SixAxisSensorState& state,
658 const Core::HID::ControllerMotion& hid_state) {
659 using namespace std::literals::chrono_literals;
660 static constexpr SixAxisSensorState default_motion_state = {
661 .delta_time = std::chrono::nanoseconds(5ms).count(),
662 .accel = {0, 0, -1.0f},
663 .orientation =
664 {
665 Common::Vec3f{1.0f, 0, 0},
666 Common::Vec3f{0, 1.0f, 0},
667 Common::Vec3f{0, 0, 1.0f},
668 },
669 .attribute = {1},
670 };
671 if (!controller.sixaxis_sensor_enabled) {
672 state = default_motion_state;
673 return;
674 }
675 if (!Settings::values.motion_enabled.GetValue()) {
676 state = default_motion_state;
677 return;
678 }
679 state.attribute.is_connected.Assign(1);
680 state.delta_time = std::chrono::nanoseconds(5ms).count();
681 state.accel = hid_state.accel;
682 state.gyro = hid_state.gyro;
683 state.rotation = hid_state.rotation;
684 state.orientation = hid_state.orientation;
685 };
686
687 switch (controller_type) {
688 case Core::HID::NpadStyleIndex::None:
689 ASSERT(false);
690 break;
691 case Core::HID::NpadStyleIndex::ProController:
692 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
693 break;
694 case Core::HID::NpadStyleIndex::Handheld:
695 set_motion_state(sixaxis_handheld_state, motion_state[0]);
696 break;
697 case Core::HID::NpadStyleIndex::JoyconDual:
698 set_motion_state(sixaxis_dual_left_state, motion_state[0]);
699 set_motion_state(sixaxis_dual_right_state, motion_state[1]);
700 break;
701 case Core::HID::NpadStyleIndex::JoyconLeft:
702 set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
703 break;
704 case Core::HID::NpadStyleIndex::JoyconRight:
705 set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
706 break;
707 case Core::HID::NpadStyleIndex::Pokeball:
708 using namespace std::literals::chrono_literals;
709 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
710 sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
711 break;
712 default:
713 break;
714 }
715
716 sixaxis_fullkey_state.sampling_number =
717 npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
718 sixaxis_handheld_state.sampling_number =
719 npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
720 sixaxis_dual_left_state.sampling_number =
721 npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
722 sixaxis_dual_right_state.sampling_number =
723 npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
724 sixaxis_left_lifo_state.sampling_number =
725 npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
726 sixaxis_right_lifo_state.sampling_number =
727 npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
728
729 if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
730 // This buffer only is updated on handheld on HW
731 npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
732 } else {
733 // Handheld doesn't update this buffer on HW
734 npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
735 }
736
737 npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
738 npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
739 npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
740 npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
741 }
742}
743
744void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
745 hid_core.SetSupportedStyleTag(style_set); 567 hid_core.SetSupportedStyleTag(style_set);
746 568
747 if (is_controller_initialized) { 569 if (is_controller_initialized) {
@@ -752,14 +574,14 @@ void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
752 is_controller_initialized = true; 574 is_controller_initialized = true;
753} 575}
754 576
755Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { 577Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const {
756 if (!is_controller_initialized) { 578 if (!is_controller_initialized) {
757 return {Core::HID::NpadStyleSet::None}; 579 return {Core::HID::NpadStyleSet::None};
758 } 580 }
759 return hid_core.GetSupportedStyleTag(); 581 return hid_core.GetSupportedStyleTag();
760} 582}
761 583
762Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { 584Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
763 constexpr std::size_t max_number_npad_ids = 0xa; 585 constexpr std::size_t max_number_npad_ids = 0xa;
764 const auto length = data.size(); 586 const auto length = data.size();
765 ASSERT(length > 0 && (length % sizeof(u32)) == 0); 587 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
@@ -775,17 +597,17 @@ Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
775 return ResultSuccess; 597 return ResultSuccess;
776} 598}
777 599
778void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 600void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
779 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); 601 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32);
780 ASSERT(max_length <= copy_amount); 602 ASSERT(max_length <= copy_amount);
781 std::memcpy(data, supported_npad_id_types.data(), copy_amount); 603 std::memcpy(data, supported_npad_id_types.data(), copy_amount);
782} 604}
783 605
784std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { 606std::size_t NPad::GetSupportedNpadIdTypesSize() const {
785 return supported_npad_id_types.size(); 607 return supported_npad_id_types.size();
786} 608}
787 609
788void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { 610void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
789 if (joy_hold_type != NpadJoyHoldType::Horizontal && 611 if (joy_hold_type != NpadJoyHoldType::Horizontal &&
790 joy_hold_type != NpadJoyHoldType::Vertical) { 612 joy_hold_type != NpadJoyHoldType::Vertical) {
791 LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}", 613 LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
@@ -795,11 +617,11 @@ void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
795 hold_type = joy_hold_type; 617 hold_type = joy_hold_type;
796} 618}
797 619
798Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { 620NPad::NpadJoyHoldType NPad::GetHoldType() const {
799 return hold_type; 621 return hold_type;
800} 622}
801 623
802void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { 624void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
803 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { 625 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
804 ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); 626 ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
805 return; 627 return;
@@ -808,21 +630,20 @@ void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode a
808 handheld_activation_mode = activation_mode; 630 handheld_activation_mode = activation_mode;
809} 631}
810 632
811Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { 633NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
812 return handheld_activation_mode; 634 return handheld_activation_mode;
813} 635}
814 636
815void Controller_NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { 637void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
816 communication_mode = communication_mode_; 638 communication_mode = communication_mode_;
817} 639}
818 640
819Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode() const { 641NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
820 return communication_mode; 642 return communication_mode;
821} 643}
822 644
823bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, 645bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
824 NpadJoyDeviceType npad_device_type, 646 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) {
825 NpadJoyAssignmentMode assignment_mode) {
826 if (!IsNpadIdValid(npad_id)) { 647 if (!IsNpadIdValid(npad_id)) {
827 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 648 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
828 return false; 649 return false;
@@ -891,9 +712,8 @@ bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID:
891 return true; 712 return true;
892} 713}
893 714
894bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, 715bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
895 std::size_t device_index, 716 const Core::HID::VibrationValue& vibration_value) {
896 const Core::HID::VibrationValue& vibration_value) {
897 auto& controller = GetControllerFromNpadIdType(npad_id); 717 auto& controller = GetControllerFromNpadIdType(npad_id);
898 if (!controller.device->IsConnected()) { 718 if (!controller.device->IsConnected()) {
899 return false; 719 return false;
@@ -937,10 +757,9 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
937 return controller.device->SetVibration(device_index, vibration); 757 return controller.device->SetVibration(device_index, vibration);
938} 758}
939 759
940void Controller_NPad::VibrateController( 760void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
941 const Core::HID::VibrationDeviceHandle& vibration_device_handle, 761 const Core::HID::VibrationValue& vibration_value) {
942 const Core::HID::VibrationValue& vibration_value) { 762 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
943 if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
944 return; 763 return;
945 } 764 }
946 765
@@ -984,7 +803,7 @@ void Controller_NPad::VibrateController(
984 } 803 }
985} 804}
986 805
987void Controller_NPad::VibrateControllers( 806void NPad::VibrateControllers(
988 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, 807 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
989 std::span<const Core::HID::VibrationValue> vibration_values) { 808 std::span<const Core::HID::VibrationValue> vibration_values) {
990 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { 809 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
@@ -1001,9 +820,9 @@ void Controller_NPad::VibrateControllers(
1001 } 820 }
1002} 821}
1003 822
1004Core::HID::VibrationValue Controller_NPad::GetLastVibration( 823Core::HID::VibrationValue NPad::GetLastVibration(
1005 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 824 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
1006 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 825 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1007 return {}; 826 return {};
1008 } 827 }
1009 828
@@ -1012,9 +831,9 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration(
1012 return controller.vibration[device_index].latest_vibration_value; 831 return controller.vibration[device_index].latest_vibration_value;
1013} 832}
1014 833
1015void Controller_NPad::InitializeVibrationDevice( 834void NPad::InitializeVibrationDevice(
1016 const Core::HID::VibrationDeviceHandle& vibration_device_handle) { 835 const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
1017 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 836 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1018 return; 837 return;
1019 } 838 }
1020 839
@@ -1023,8 +842,8 @@ void Controller_NPad::InitializeVibrationDevice(
1023 InitializeVibrationDeviceAtIndex(npad_index, device_index); 842 InitializeVibrationDeviceAtIndex(npad_index, device_index);
1024} 843}
1025 844
1026void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, 845void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
1027 std::size_t device_index) { 846 std::size_t device_index) {
1028 auto& controller = GetControllerFromNpadIdType(npad_id); 847 auto& controller = GetControllerFromNpadIdType(npad_id);
1029 if (!Settings::values.vibration_enabled.GetValue()) { 848 if (!Settings::values.vibration_enabled.GetValue()) {
1030 controller.vibration[device_index].device_mounted = false; 849 controller.vibration[device_index].device_mounted = false;
@@ -1035,13 +854,13 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa
1035 controller.device->IsVibrationEnabled(device_index); 854 controller.device->IsVibrationEnabled(device_index);
1036} 855}
1037 856
1038void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 857void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
1039 permit_vibration_session_enabled = permit_vibration_session; 858 permit_vibration_session_enabled = permit_vibration_session;
1040} 859}
1041 860
1042bool Controller_NPad::IsVibrationDeviceMounted( 861bool NPad::IsVibrationDeviceMounted(
1043 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 862 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
1044 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 863 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1045 return false; 864 return false;
1046 } 865 }
1047 866
@@ -1050,7 +869,7 @@ bool Controller_NPad::IsVibrationDeviceMounted(
1050 return controller.vibration[device_index].device_mounted; 869 return controller.vibration[device_index].device_mounted;
1051} 870}
1052 871
1053Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { 872Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
1054 if (!IsNpadIdValid(npad_id)) { 873 if (!IsNpadIdValid(npad_id)) {
1055 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 874 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1056 // Fallback to player 1 875 // Fallback to player 1
@@ -1062,18 +881,17 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad
1062 return controller.styleset_changed_event->GetReadableEvent(); 881 return controller.styleset_changed_event->GetReadableEvent();
1063} 882}
1064 883
1065void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { 884void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
1066 const auto& controller = GetControllerFromNpadIdType(npad_id); 885 const auto& controller = GetControllerFromNpadIdType(npad_id);
1067 controller.styleset_changed_event->Signal(); 886 controller.styleset_changed_event->Signal();
1068} 887}
1069 888
1070void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, 889void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) {
1071 Core::HID::NpadIdType npad_id) {
1072 UpdateControllerAt(controller, npad_id, true); 890 UpdateControllerAt(controller, npad_id, true);
1073} 891}
1074 892
1075void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, 893void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id,
1076 Core::HID::NpadIdType npad_id, bool connected) { 894 bool connected) {
1077 auto& controller = GetControllerFromNpadIdType(npad_id); 895 auto& controller = GetControllerFromNpadIdType(npad_id);
1078 if (!connected) { 896 if (!connected) {
1079 DisconnectNpad(npad_id); 897 DisconnectNpad(npad_id);
@@ -1084,7 +902,7 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
1084 InitNewlyAddedController(npad_id); 902 InitNewlyAddedController(npad_id);
1085} 903}
1086 904
1087Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { 905Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1088 if (!IsNpadIdValid(npad_id)) { 906 if (!IsNpadIdValid(npad_id)) {
1089 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 907 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1090 return InvalidNpadId; 908 return InvalidNpadId;
@@ -1133,54 +951,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1133 return ResultSuccess; 951 return ResultSuccess;
1134} 952}
1135 953
1136Result Controller_NPad::SetGyroscopeZeroDriftMode( 954Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1137 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1138 Core::HID::GyroscopeZeroDriftMode drift_mode) {
1139 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1140 if (is_valid.IsError()) {
1141 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1142 return is_valid;
1143 }
1144
1145 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1146 auto& controller = GetControllerFromHandle(sixaxis_handle);
1147 sixaxis.gyroscope_zero_drift_mode = drift_mode;
1148 controller.device->SetGyroscopeZeroDriftMode(drift_mode);
1149
1150 return ResultSuccess;
1151}
1152
1153Result Controller_NPad::GetGyroscopeZeroDriftMode(
1154 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1155 Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
1156 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1157 if (is_valid.IsError()) {
1158 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1159 return is_valid;
1160 }
1161
1162 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1163 drift_mode = sixaxis.gyroscope_zero_drift_mode;
1164
1165 return ResultSuccess;
1166}
1167
1168Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1169 bool& is_at_rest) const {
1170 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1171 if (is_valid.IsError()) {
1172 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1173 return is_valid;
1174 }
1175
1176 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1177 is_at_rest = controller.sixaxis_at_rest;
1178 return ResultSuccess;
1179}
1180
1181Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1182 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { 955 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
1183 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); 956 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
1184 if (is_valid.IsError()) { 957 if (is_valid.IsError()) {
1185 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 958 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1186 return is_valid; 959 return is_valid;
@@ -1191,65 +964,9 @@ Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1191 return ResultSuccess; 964 return ResultSuccess;
1192} 965}
1193 966
1194Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough( 967Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1195 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
1196 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1197 if (is_valid.IsError()) {
1198 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1199 return is_valid;
1200 }
1201
1202 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1203 sixaxis.unaltered_passtrough = is_enabled;
1204 return ResultSuccess;
1205}
1206
1207Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
1208 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
1209 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1210 if (is_valid.IsError()) {
1211 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1212 return is_valid;
1213 }
1214
1215 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1216 is_enabled = sixaxis.unaltered_passtrough;
1217 return ResultSuccess;
1218}
1219
1220Result Controller_NPad::LoadSixAxisSensorCalibrationParameter(
1221 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1222 Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
1223 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1224 if (is_valid.IsError()) {
1225 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1226 return is_valid;
1227 }
1228
1229 // TODO: Request this data to the controller. On error return 0xd8ca
1230 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1231 calibration = sixaxis.calibration;
1232 return ResultSuccess;
1233}
1234
1235Result Controller_NPad::GetSixAxisSensorIcInformation(
1236 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1237 Core::HID::SixAxisSensorIcInformation& ic_information) const {
1238 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1239 if (is_valid.IsError()) {
1240 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1241 return is_valid;
1242 }
1243
1244 // TODO: Request this data to the controller. On error return 0xd8ca
1245 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1246 ic_information = sixaxis.ic_information;
1247 return ResultSuccess;
1248}
1249
1250Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1251 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 968 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1252 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); 969 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
1253 if (is_valid.IsError()) { 970 if (is_valid.IsError()) {
1254 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 971 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1255 return is_valid; 972 return is_valid;
@@ -1261,83 +978,32 @@ Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1261 return ResultSuccess; 978 return ResultSuccess;
1262} 979}
1263 980
1264Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 981NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
1265 bool sixaxis_status) { 982 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
1266 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1267 if (is_valid.IsError()) {
1268 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1269 return is_valid;
1270 }
1271
1272 auto& controller = GetControllerFromHandle(sixaxis_handle);
1273 controller.sixaxis_sensor_enabled = sixaxis_status;
1274 return ResultSuccess;
1275} 983}
1276 984
1277Result Controller_NPad::IsSixAxisSensorFusionEnabled( 985NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
1278 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const { 986 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
1279 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1280 if (is_valid.IsError()) {
1281 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1282 return is_valid;
1283 }
1284
1285 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1286 is_fusion_enabled = sixaxis.is_fusion_enabled;
1287
1288 return ResultSuccess;
1289} 987}
1290Result Controller_NPad::SetSixAxisFusionEnabled(
1291 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
1292 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1293 if (is_valid.IsError()) {
1294 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1295 return is_valid;
1296 }
1297 988
1298 auto& sixaxis = GetSixaxisState(sixaxis_handle); 989NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
1299 sixaxis.is_fusion_enabled = is_fusion_enabled; 990 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
1300
1301 return ResultSuccess;
1302} 991}
1303 992
1304Result Controller_NPad::SetSixAxisFusionParameters( 993NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
1305 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 994 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
1306 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1307 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1308 if (is_valid.IsError()) {
1309 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1310 return is_valid;
1311 }
1312
1313 const auto param1 = sixaxis_fusion_parameters.parameter1;
1314 if (param1 < 0.0f || param1 > 1.0f) {
1315 return InvalidSixAxisFusionRange;
1316 }
1317
1318 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1319 sixaxis.fusion = sixaxis_fusion_parameters;
1320
1321 return ResultSuccess;
1322} 995}
1323 996
1324Result Controller_NPad::GetSixAxisFusionParameters( 997NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
1325 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 998 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
1326 Core::HID::SixAxisSensorFusionParameters& parameters) const { 999}
1327 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1328 if (is_valid.IsError()) {
1329 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1330 return is_valid;
1331 }
1332
1333 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1334 parameters = sixaxis.fusion;
1335 1000
1336 return ResultSuccess; 1001NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
1002 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
1337} 1003}
1338 1004
1339Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 1005Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1340 Core::HID::NpadIdType npad_id_2) { 1006 Core::HID::NpadIdType npad_id_2) {
1341 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1007 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1342 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1008 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1343 npad_id_2); 1009 npad_id_2);
@@ -1399,18 +1065,17 @@ Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1399 return ResultSuccess; 1065 return ResultSuccess;
1400} 1066}
1401 1067
1402void Controller_NPad::StartLRAssignmentMode() { 1068void NPad::StartLRAssignmentMode() {
1403 // Nothing internally is used for lr assignment mode. Since we have the ability to set the 1069 // Nothing internally is used for lr assignment mode. Since we have the ability to set the
1404 // controller types from boot, it doesn't really matter about showing a selection screen 1070 // controller types from boot, it doesn't really matter about showing a selection screen
1405 is_in_lr_assignment_mode = true; 1071 is_in_lr_assignment_mode = true;
1406} 1072}
1407 1073
1408void Controller_NPad::StopLRAssignmentMode() { 1074void NPad::StopLRAssignmentMode() {
1409 is_in_lr_assignment_mode = false; 1075 is_in_lr_assignment_mode = false;
1410} 1076}
1411 1077
1412Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, 1078Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) {
1413 Core::HID::NpadIdType npad_id_2) {
1414 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1079 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1415 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1080 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1416 npad_id_2); 1081 npad_id_2);
@@ -1441,8 +1106,7 @@ Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1441 return ResultSuccess; 1106 return ResultSuccess;
1442} 1107}
1443 1108
1444Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, 1109Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const {
1445 Core::HID::LedPattern& pattern) const {
1446 if (!IsNpadIdValid(npad_id)) { 1110 if (!IsNpadIdValid(npad_id)) {
1447 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1111 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1448 return InvalidNpadId; 1112 return InvalidNpadId;
@@ -1452,8 +1116,8 @@ Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
1452 return ResultSuccess; 1116 return ResultSuccess;
1453} 1117}
1454 1118
1455Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 1119Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
1456 bool& is_valid) const { 1120 bool& is_valid) const {
1457 if (!IsNpadIdValid(npad_id)) { 1121 if (!IsNpadIdValid(npad_id)) {
1458 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1122 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1459 return InvalidNpadId; 1123 return InvalidNpadId;
@@ -1463,8 +1127,8 @@ Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::
1463 return ResultSuccess; 1127 return ResultSuccess;
1464} 1128}
1465 1129
1466Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( 1130Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
1467 bool is_protection_enabled, Core::HID::NpadIdType npad_id) { 1131 Core::HID::NpadIdType npad_id) {
1468 if (!IsNpadIdValid(npad_id)) { 1132 if (!IsNpadIdValid(npad_id)) {
1469 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1133 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1470 return InvalidNpadId; 1134 return InvalidNpadId;
@@ -1474,11 +1138,11 @@ Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
1474 return ResultSuccess; 1138 return ResultSuccess;
1475} 1139}
1476 1140
1477void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1141void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
1478 analog_stick_use_center_clamp = use_center_clamp; 1142 analog_stick_use_center_clamp = use_center_clamp;
1479} 1143}
1480 1144
1481void Controller_NPad::ClearAllConnectedControllers() { 1145void NPad::ClearAllConnectedControllers() {
1482 for (auto& controller : controller_data) { 1146 for (auto& controller : controller_data) {
1483 if (controller.device->IsConnected() && 1147 if (controller.device->IsConnected() &&
1484 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { 1148 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
@@ -1488,13 +1152,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
1488 } 1152 }
1489} 1153}
1490 1154
1491void Controller_NPad::DisconnectAllConnectedControllers() { 1155void NPad::DisconnectAllConnectedControllers() {
1492 for (auto& controller : controller_data) { 1156 for (auto& controller : controller_data) {
1493 controller.device->Disconnect(); 1157 controller.device->Disconnect();
1494 } 1158 }
1495} 1159}
1496 1160
1497void Controller_NPad::ConnectAllDisconnectedControllers() { 1161void NPad::ConnectAllDisconnectedControllers() {
1498 for (auto& controller : controller_data) { 1162 for (auto& controller : controller_data) {
1499 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && 1163 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
1500 !controller.device->IsConnected()) { 1164 !controller.device->IsConnected()) {
@@ -1503,18 +1167,18 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
1503 } 1167 }
1504} 1168}
1505 1169
1506void Controller_NPad::ClearAllControllers() { 1170void NPad::ClearAllControllers() {
1507 for (auto& controller : controller_data) { 1171 for (auto& controller : controller_data) {
1508 controller.device->Disconnect(); 1172 controller.device->Disconnect();
1509 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); 1173 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1510 } 1174 }
1511} 1175}
1512 1176
1513Core::HID::NpadButton Controller_NPad::GetAndResetPressState() { 1177Core::HID::NpadButton NPad::GetAndResetPressState() {
1514 return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); 1178 return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
1515} 1179}
1516 1180
1517void Controller_NPad::ApplyNpadSystemCommonPolicy() { 1181void NPad::ApplyNpadSystemCommonPolicy() {
1518 Core::HID::NpadStyleTag styletag{}; 1182 Core::HID::NpadStyleTag styletag{};
1519 styletag.fullkey.Assign(1); 1183 styletag.fullkey.Assign(1);
1520 styletag.handheld.Assign(1); 1184 styletag.handheld.Assign(1);
@@ -1539,7 +1203,7 @@ void Controller_NPad::ApplyNpadSystemCommonPolicy() {
1539 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; 1203 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
1540} 1204}
1541 1205
1542bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { 1206bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
1543 if (controller == Core::HID::NpadStyleIndex::Handheld) { 1207 if (controller == Core::HID::NpadStyleIndex::Handheld) {
1544 const bool support_handheld = 1208 const bool support_handheld =
1545 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1209 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
@@ -1590,51 +1254,50 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
1590 return false; 1254 return false;
1591} 1255}
1592 1256
1593Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1257NPad::NpadControllerData& NPad::GetControllerFromHandle(
1594 const Core::HID::SixAxisSensorHandle& device_handle) { 1258 const Core::HID::VibrationDeviceHandle& device_handle) {
1595 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1259 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1596 return GetControllerFromNpadIdType(npad_id); 1260 return GetControllerFromNpadIdType(npad_id);
1597} 1261}
1598 1262
1599const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1263const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1600 const Core::HID::SixAxisSensorHandle& device_handle) const { 1264 const Core::HID::VibrationDeviceHandle& device_handle) const {
1601 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1265 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1602 return GetControllerFromNpadIdType(npad_id); 1266 return GetControllerFromNpadIdType(npad_id);
1603} 1267}
1604 1268
1605Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1269NPad::NpadControllerData& NPad::GetControllerFromHandle(
1606 const Core::HID::VibrationDeviceHandle& device_handle) { 1270 const Core::HID::SixAxisSensorHandle& device_handle) {
1607 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1271 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1608 return GetControllerFromNpadIdType(npad_id); 1272 return GetControllerFromNpadIdType(npad_id);
1609} 1273}
1610 1274
1611const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1275const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1612 const Core::HID::VibrationDeviceHandle& device_handle) const { 1276 const Core::HID::SixAxisSensorHandle& device_handle) const {
1613 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1277 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1614 return GetControllerFromNpadIdType(npad_id); 1278 return GetControllerFromNpadIdType(npad_id);
1615} 1279}
1616 1280
1617Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( 1281NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
1618 Core::HID::NpadIdType npad_id) {
1619 if (!IsNpadIdValid(npad_id)) { 1282 if (!IsNpadIdValid(npad_id)) {
1620 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1283 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1621 npad_id = Core::HID::NpadIdType::Player1; 1284 npad_id = Core::HID::NpadIdType::Player1;
1622 } 1285 }
1623 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); 1286 const auto npad_index = NpadIdTypeToIndex(npad_id);
1624 return controller_data[npad_index]; 1287 return controller_data[npad_index];
1625} 1288}
1626 1289
1627const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( 1290const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(
1628 Core::HID::NpadIdType npad_id) const { 1291 Core::HID::NpadIdType npad_id) const {
1629 if (!IsNpadIdValid(npad_id)) { 1292 if (!IsNpadIdValid(npad_id)) {
1630 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1293 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1631 npad_id = Core::HID::NpadIdType::Player1; 1294 npad_id = Core::HID::NpadIdType::Player1;
1632 } 1295 }
1633 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); 1296 const auto npad_index = NpadIdTypeToIndex(npad_id);
1634 return controller_data[npad_index]; 1297 return controller_data[npad_index];
1635} 1298}
1636 1299
1637Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( 1300Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1638 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1301 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1639 auto& controller = GetControllerFromHandle(sixaxis_handle); 1302 auto& controller = GetControllerFromHandle(sixaxis_handle);
1640 switch (sixaxis_handle.npad_type) { 1303 switch (sixaxis_handle.npad_type) {
@@ -1657,7 +1320,7 @@ Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1657 } 1320 }
1658} 1321}
1659 1322
1660const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( 1323const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1661 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1324 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1662 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1325 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1663 switch (sixaxis_handle.npad_type) { 1326 switch (sixaxis_handle.npad_type) {
@@ -1680,50 +1343,13 @@ const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1680 } 1343 }
1681} 1344}
1682 1345
1683Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( 1346NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
1684 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1347 const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory;
1685 auto& controller = GetControllerFromHandle(sixaxis_handle);
1686 switch (sixaxis_handle.npad_type) {
1687 case Core::HID::NpadStyleIndex::ProController:
1688 case Core::HID::NpadStyleIndex::Pokeball:
1689 return controller.sixaxis_fullkey;
1690 case Core::HID::NpadStyleIndex::Handheld:
1691 return controller.sixaxis_handheld;
1692 case Core::HID::NpadStyleIndex::JoyconDual:
1693 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1694 return controller.sixaxis_dual_left;
1695 }
1696 return controller.sixaxis_dual_right;
1697 case Core::HID::NpadStyleIndex::JoyconLeft:
1698 return controller.sixaxis_left;
1699 case Core::HID::NpadStyleIndex::JoyconRight:
1700 return controller.sixaxis_right;
1701 default:
1702 return controller.sixaxis_unknown;
1703 }
1704}
1705 1348
1706const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( 1349 return {
1707 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1350 .ui_variant = 0,
1708 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1351 .footer = shared_memory->applet_footer_type,
1709 switch (sixaxis_handle.npad_type) { 1352 };
1710 case Core::HID::NpadStyleIndex::ProController:
1711 case Core::HID::NpadStyleIndex::Pokeball:
1712 return controller.sixaxis_fullkey;
1713 case Core::HID::NpadStyleIndex::Handheld:
1714 return controller.sixaxis_handheld;
1715 case Core::HID::NpadStyleIndex::JoyconDual:
1716 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1717 return controller.sixaxis_dual_left;
1718 }
1719 return controller.sixaxis_dual_right;
1720 case Core::HID::NpadStyleIndex::JoyconLeft:
1721 return controller.sixaxis_left;
1722 case Core::HID::NpadStyleIndex::JoyconRight:
1723 return controller.sixaxis_right;
1724 default:
1725 return controller.sixaxis_unknown;
1726 }
1727} 1353}
1728 1354
1729} // namespace Service::HID 1355} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index e23b4986c..9167c93f0 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -10,7 +10,6 @@
10 10
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/vector_math.h"
14 13
15#include "core/hid/hid_types.h" 14#include "core/hid/hid_types.h"
16#include "core/hle/service/hid/controllers/controller_base.h" 15#include "core/hle/service/hid/controllers/controller_base.h"
@@ -34,11 +33,11 @@ union Result;
34 33
35namespace Service::HID { 34namespace Service::HID {
36 35
37class Controller_NPad final : public ControllerBase { 36class NPad final : public ControllerBase {
38public: 37public:
39 explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 38 explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
40 KernelHelpers::ServiceContext& service_context_); 39 KernelHelpers::ServiceContext& service_context_);
41 ~Controller_NPad() override; 40 ~NPad() override;
42 41
43 // Called when the controller is initialized 42 // Called when the controller is initialized
44 void OnInit() override; 43 void OnInit() override;
@@ -49,9 +48,6 @@ public:
49 // When the controller is requesting an update for the shared memory 48 // When the controller is requesting an update for the shared memory
50 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 49 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
51 50
52 // When the controller is requesting a motion update for the shared memory
53 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override;
54
55 // This is nn::hid::NpadJoyHoldType 51 // This is nn::hid::NpadJoyHoldType
56 enum class NpadJoyHoldType : u64 { 52 enum class NpadJoyHoldType : u64 {
57 Vertical = 0, 53 Vertical = 0,
@@ -78,6 +74,46 @@ public:
78 MaxActivationMode = 3, 74 MaxActivationMode = 3,
79 }; 75 };
80 76
77 // This is nn::hid::system::AppletFooterUiAttributesSet
78 struct AppletFooterUiAttributes {
79 INSERT_PADDING_BYTES(0x4);
80 };
81
82 // This is nn::hid::system::AppletFooterUiType
83 enum class AppletFooterUiType : u8 {
84 None = 0,
85 HandheldNone = 1,
86 HandheldJoyConLeftOnly = 2,
87 HandheldJoyConRightOnly = 3,
88 HandheldJoyConLeftJoyConRight = 4,
89 JoyDual = 5,
90 JoyDualLeftOnly = 6,
91 JoyDualRightOnly = 7,
92 JoyLeftHorizontal = 8,
93 JoyLeftVertical = 9,
94 JoyRightHorizontal = 10,
95 JoyRightVertical = 11,
96 SwitchProController = 12,
97 CompatibleProController = 13,
98 CompatibleJoyCon = 14,
99 LarkHvc1 = 15,
100 LarkHvc2 = 16,
101 LarkNesLeft = 17,
102 LarkNesRight = 18,
103 Lucia = 19,
104 Verification = 20,
105 Lagon = 21,
106 };
107
108 using AppletFooterUiVariant = u8;
109
110 // This is "nn::hid::system::AppletDetailedUiType".
111 struct AppletDetailedUiType {
112 AppletFooterUiVariant ui_variant;
113 INSERT_PADDING_BYTES(0x2);
114 AppletFooterUiType footer;
115 };
116 static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
81 // This is nn::hid::NpadCommunicationMode 117 // This is nn::hid::NpadCommunicationMode
82 enum class NpadCommunicationMode : u64 { 118 enum class NpadCommunicationMode : u64 {
83 Mode_5ms = 0, 119 Mode_5ms = 0,
@@ -93,6 +129,8 @@ public:
93 Revision3 = 3, 129 Revision3 = 3,
94 }; 130 };
95 131
132 using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>;
133
96 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 134 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
97 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 135 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
98 136
@@ -145,37 +183,18 @@ public:
145 183
146 Result DisconnectNpad(Core::HID::NpadIdType npad_id); 184 Result DisconnectNpad(Core::HID::NpadIdType npad_id);
147 185
148 Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
149 Core::HID::GyroscopeZeroDriftMode drift_mode);
150 Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
151 Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
152 Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
153 bool& is_at_rest) const;
154 Result IsFirmwareUpdateAvailableForSixAxisSensor( 186 Result IsFirmwareUpdateAvailableForSixAxisSensor(
155 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; 187 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
156 Result EnableSixAxisSensorUnalteredPassthrough(
157 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
158 Result IsSixAxisSensorUnalteredPassthroughEnabled(
159 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
160 Result LoadSixAxisSensorCalibrationParameter(
161 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
162 Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
163 Result GetSixAxisSensorIcInformation(
164 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
165 Core::HID::SixAxisSensorIcInformation& ic_information) const;
166 Result ResetIsSixAxisSensorDeviceNewlyAssigned( 188 Result ResetIsSixAxisSensorDeviceNewlyAssigned(
167 const Core::HID::SixAxisSensorHandle& sixaxis_handle); 189 const Core::HID::SixAxisSensorHandle& sixaxis_handle);
168 Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 190
169 bool sixaxis_status); 191 SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
170 Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 192 SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
171 bool& is_fusion_enabled) const; 193 SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
172 Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 194 SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
173 bool is_fusion_enabled); 195 SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
174 Result SetSixAxisFusionParameters( 196 SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
175 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 197
176 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
177 Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
178 Core::HID::SixAxisSensorFusionParameters& parameters) const;
179 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; 198 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
180 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 199 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
181 bool& is_enabled) const; 200 bool& is_enabled) const;
@@ -199,10 +218,7 @@ public:
199 218
200 void ApplyNpadSystemCommonPolicy(); 219 void ApplyNpadSystemCommonPolicy();
201 220
202 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); 221 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
203 static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
204 static Result VerifyValidSixAxisSensorHandle(
205 const Core::HID::SixAxisSensorHandle& device_handle);
206 222
207private: 223private:
208 static constexpr std::size_t NPAD_COUNT = 10; 224 static constexpr std::size_t NPAD_COUNT = 10;
@@ -261,29 +277,6 @@ private:
261 }; 277 };
262 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); 278 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
263 279
264 // This is nn::hid::SixAxisSensorAttribute
265 struct SixAxisSensorAttribute {
266 union {
267 u32 raw{};
268 BitField<0, 1, u32> is_connected;
269 BitField<1, 1, u32> is_interpolated;
270 };
271 };
272 static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
273
274 // This is nn::hid::SixAxisSensorState
275 struct SixAxisSensorState {
276 s64 delta_time{};
277 s64 sampling_number{};
278 Common::Vec3f accel{};
279 Common::Vec3f gyro{};
280 Common::Vec3f rotation{};
281 std::array<Common::Vec3f, 3> orientation{};
282 SixAxisSensorAttribute attribute{};
283 INSERT_PADDING_BYTES(4); // Reserved
284 };
285 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
286
287 // This is nn::hid::server::NpadGcTriggerState 280 // This is nn::hid::server::NpadGcTriggerState
288 struct NpadGcTriggerState { 281 struct NpadGcTriggerState {
289 s64 sampling_number{}; 282 s64 sampling_number{};
@@ -360,37 +353,6 @@ private:
360 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, 353 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
361 "NfcXcdDeviceHandleStateImpl is an invalid size"); 354 "NfcXcdDeviceHandleStateImpl is an invalid size");
362 355
363 // This is nn::hid::system::AppletFooterUiAttributesSet
364 struct AppletFooterUiAttributes {
365 INSERT_PADDING_BYTES(0x4);
366 };
367
368 // This is nn::hid::system::AppletFooterUiType
369 enum class AppletFooterUiType : u8 {
370 None = 0,
371 HandheldNone = 1,
372 HandheldJoyConLeftOnly = 2,
373 HandheldJoyConRightOnly = 3,
374 HandheldJoyConLeftJoyConRight = 4,
375 JoyDual = 5,
376 JoyDualLeftOnly = 6,
377 JoyDualRightOnly = 7,
378 JoyLeftHorizontal = 8,
379 JoyLeftVertical = 9,
380 JoyRightHorizontal = 10,
381 JoyRightVertical = 11,
382 SwitchProController = 12,
383 CompatibleProController = 13,
384 CompatibleJoyCon = 14,
385 LarkHvc1 = 15,
386 LarkHvc2 = 16,
387 LarkNesLeft = 17,
388 LarkNesRight = 18,
389 Lucia = 19,
390 Verification = 20,
391 Lagon = 21,
392 };
393
394 // This is nn::hid::NpadLarkType 356 // This is nn::hid::NpadLarkType
395 enum class NpadLarkType : u32 { 357 enum class NpadLarkType : u32 {
396 Invalid, 358 Invalid,
@@ -434,12 +396,12 @@ private:
434 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; 396 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
435 Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; 397 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
436 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; 398 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
437 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; 399 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
438 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; 400 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
439 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; 401 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
440 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; 402 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
441 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; 403 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
442 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; 404 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
443 DeviceType device_type{}; 405 DeviceType device_type{};
444 INSERT_PADDING_BYTES(0x4); // Reserved 406 INSERT_PADDING_BYTES(0x4); // Reserved
445 NPadSystemProperties system_properties{}; 407 NPadSystemProperties system_properties{};
@@ -473,16 +435,6 @@ private:
473 std::chrono::steady_clock::time_point last_vibration_timepoint{}; 435 std::chrono::steady_clock::time_point last_vibration_timepoint{};
474 }; 436 };
475 437
476 struct SixaxisParameters {
477 bool is_fusion_enabled{true};
478 bool unaltered_passtrough{false};
479 Core::HID::SixAxisSensorFusionParameters fusion{};
480 Core::HID::SixAxisSensorCalibrationParameter calibration{};
481 Core::HID::SixAxisSensorIcInformation ic_information{};
482 Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
483 Core::HID::GyroscopeZeroDriftMode::Standard};
484 };
485
486 struct NpadControllerData { 438 struct NpadControllerData {
487 Kernel::KEvent* styleset_changed_event{}; 439 Kernel::KEvent* styleset_changed_event{};
488 NpadInternalState* shared_memory = nullptr; 440 NpadInternalState* shared_memory = nullptr;
@@ -496,27 +448,10 @@ private:
496 bool is_dual_left_connected{true}; 448 bool is_dual_left_connected{true};
497 bool is_dual_right_connected{true}; 449 bool is_dual_right_connected{true};
498 450
499 // Motion parameters
500 bool sixaxis_at_rest{true};
501 bool sixaxis_sensor_enabled{true};
502 SixaxisParameters sixaxis_fullkey{};
503 SixaxisParameters sixaxis_handheld{};
504 SixaxisParameters sixaxis_dual_left{};
505 SixaxisParameters sixaxis_dual_right{};
506 SixaxisParameters sixaxis_left{};
507 SixaxisParameters sixaxis_right{};
508 SixaxisParameters sixaxis_unknown{};
509
510 // Current pad state 451 // Current pad state
511 NPadGenericState npad_pad_state{}; 452 NPadGenericState npad_pad_state{};
512 NPadGenericState npad_libnx_state{}; 453 NPadGenericState npad_libnx_state{};
513 NpadGcTriggerState npad_trigger_state{}; 454 NpadGcTriggerState npad_trigger_state{};
514 SixAxisSensorState sixaxis_fullkey_state{};
515 SixAxisSensorState sixaxis_handheld_state{};
516 SixAxisSensorState sixaxis_dual_left_state{};
517 SixAxisSensorState sixaxis_dual_right_state{};
518 SixAxisSensorState sixaxis_left_lifo_state{};
519 SixAxisSensorState sixaxis_right_lifo_state{};
520 int callback_key{}; 455 int callback_key{};
521 }; 456 };
522 457
@@ -527,13 +462,13 @@ private:
527 void WriteEmptyEntry(NpadInternalState* npad); 462 void WriteEmptyEntry(NpadInternalState* npad);
528 463
529 NpadControllerData& GetControllerFromHandle( 464 NpadControllerData& GetControllerFromHandle(
530 const Core::HID::SixAxisSensorHandle& device_handle);
531 const NpadControllerData& GetControllerFromHandle(
532 const Core::HID::SixAxisSensorHandle& device_handle) const;
533 NpadControllerData& GetControllerFromHandle(
534 const Core::HID::VibrationDeviceHandle& device_handle); 465 const Core::HID::VibrationDeviceHandle& device_handle);
535 const NpadControllerData& GetControllerFromHandle( 466 const NpadControllerData& GetControllerFromHandle(
536 const Core::HID::VibrationDeviceHandle& device_handle) const; 467 const Core::HID::VibrationDeviceHandle& device_handle) const;
468 NpadControllerData& GetControllerFromHandle(
469 const Core::HID::SixAxisSensorHandle& device_handle);
470 const NpadControllerData& GetControllerFromHandle(
471 const Core::HID::SixAxisSensorHandle& device_handle) const;
537 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); 472 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
538 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; 473 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
539 474
@@ -541,9 +476,6 @@ private:
541 const Core::HID::SixAxisSensorHandle& device_handle); 476 const Core::HID::SixAxisSensorHandle& device_handle);
542 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( 477 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
543 const Core::HID::SixAxisSensorHandle& device_handle) const; 478 const Core::HID::SixAxisSensorHandle& device_handle) const;
544 SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
545 const SixaxisParameters& GetSixaxisState(
546 const Core::HID::SixAxisSensorHandle& device_handle) const;
547 479
548 std::atomic<u64> press_state{}; 480 std::atomic<u64> press_state{};
549 481
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp
index 51a18335f..588ff9d62 100644
--- a/src/core/hle/service/hid/controllers/palma.cpp
+++ b/src/core/hle/service/hid/controllers/palma.cpp
@@ -12,35 +12,35 @@
12 12
13namespace Service::HID { 13namespace Service::HID {
14 14
15Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 15Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
16 KernelHelpers::ServiceContext& service_context_) 16 KernelHelpers::ServiceContext& service_context_)
17 : ControllerBase{hid_core_}, service_context{service_context_} { 17 : ControllerBase{hid_core_}, service_context{service_context_} {
18 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 18 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); 19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
20} 20}
21 21
22Controller_Palma::~Controller_Palma() { 22Palma::~Palma() {
23 service_context.CloseEvent(operation_complete_event); 23 service_context.CloseEvent(operation_complete_event);
24}; 24};
25 25
26void Controller_Palma::OnInit() {} 26void Palma::OnInit() {}
27 27
28void Controller_Palma::OnRelease() {} 28void Palma::OnRelease() {}
29 29
30void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 30void Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
31 if (!IsControllerActivated()) { 31 if (!IsControllerActivated()) {
32 return; 32 return;
33 } 33 }
34} 34}
35 35
36Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, 36Result Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
37 PalmaConnectionHandle& handle) { 37 PalmaConnectionHandle& handle) {
38 active_handle.npad_id = npad_id; 38 active_handle.npad_id = npad_id;
39 handle = active_handle; 39 handle = active_handle;
40 return ResultSuccess; 40 return ResultSuccess;
41} 41}
42 42
43Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { 43Result Palma::InitializePalma(const PalmaConnectionHandle& handle) {
44 if (handle.npad_id != active_handle.npad_id) { 44 if (handle.npad_id != active_handle.npad_id) {
45 return InvalidPalmaHandle; 45 return InvalidPalmaHandle;
46 } 46 }
@@ -48,7 +48,7 @@ Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
48 return ResultSuccess; 48 return ResultSuccess;
49} 49}
50 50
51Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( 51Kernel::KReadableEvent& Palma::AcquirePalmaOperationCompleteEvent(
52 const PalmaConnectionHandle& handle) const { 52 const PalmaConnectionHandle& handle) const {
53 if (handle.npad_id != active_handle.npad_id) { 53 if (handle.npad_id != active_handle.npad_id) {
54 LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); 54 LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
@@ -56,9 +56,9 @@ Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
56 return operation_complete_event->GetReadableEvent(); 56 return operation_complete_event->GetReadableEvent();
57} 57}
58 58
59Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, 59Result Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
60 PalmaOperationType& operation_type, 60 PalmaOperationType& operation_type,
61 PalmaOperationData& data) const { 61 PalmaOperationData& data) const {
62 if (handle.npad_id != active_handle.npad_id) { 62 if (handle.npad_id != active_handle.npad_id) {
63 return InvalidPalmaHandle; 63 return InvalidPalmaHandle;
64 } 64 }
@@ -67,8 +67,7 @@ Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& hand
67 return ResultSuccess; 67 return ResultSuccess;
68} 68}
69 69
70Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, 70Result Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity) {
71 u64 palma_activity) {
72 if (handle.npad_id != active_handle.npad_id) { 71 if (handle.npad_id != active_handle.npad_id) {
73 return InvalidPalmaHandle; 72 return InvalidPalmaHandle;
74 } 73 }
@@ -79,8 +78,7 @@ Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
79 return ResultSuccess; 78 return ResultSuccess;
80} 79}
81 80
82Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, 81Result Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_) {
83 PalmaFrModeType fr_mode_) {
84 if (handle.npad_id != active_handle.npad_id) { 82 if (handle.npad_id != active_handle.npad_id) {
85 return InvalidPalmaHandle; 83 return InvalidPalmaHandle;
86 } 84 }
@@ -88,7 +86,7 @@ Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
88 return ResultSuccess; 86 return ResultSuccess;
89} 87}
90 88
91Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { 89Result Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
92 if (handle.npad_id != active_handle.npad_id) { 90 if (handle.npad_id != active_handle.npad_id) {
93 return InvalidPalmaHandle; 91 return InvalidPalmaHandle;
94 } 92 }
@@ -99,25 +97,25 @@ Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
99 return ResultSuccess; 97 return ResultSuccess;
100} 98}
101 99
102Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { 100Result Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
103 if (handle.npad_id != active_handle.npad_id) { 101 if (handle.npad_id != active_handle.npad_id) {
104 return InvalidPalmaHandle; 102 return InvalidPalmaHandle;
105 } 103 }
106 return ResultSuccess; 104 return ResultSuccess;
107} 105}
108 106
109Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { 107Result Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
110 if (handle.npad_id != active_handle.npad_id) { 108 if (handle.npad_id != active_handle.npad_id) {
111 return InvalidPalmaHandle; 109 return InvalidPalmaHandle;
112 } 110 }
113 return ResultSuccess; 111 return ResultSuccess;
114} 112}
115 113
116void Controller_Palma::ReadPalmaApplicationSection() {} 114void Palma::ReadPalmaApplicationSection() {}
117 115
118void Controller_Palma::WritePalmaApplicationSection() {} 116void Palma::WritePalmaApplicationSection() {}
119 117
120Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { 118Result Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
121 if (handle.npad_id != active_handle.npad_id) { 119 if (handle.npad_id != active_handle.npad_id) {
122 return InvalidPalmaHandle; 120 return InvalidPalmaHandle;
123 } 121 }
@@ -128,7 +126,7 @@ Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle
128 return ResultSuccess; 126 return ResultSuccess;
129} 127}
130 128
131Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { 129Result Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
132 if (handle.npad_id != active_handle.npad_id) { 130 if (handle.npad_id != active_handle.npad_id) {
133 return InvalidPalmaHandle; 131 return InvalidPalmaHandle;
134 } 132 }
@@ -139,10 +137,9 @@ Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle&
139 return ResultSuccess; 137 return ResultSuccess;
140} 138}
141 139
142void Controller_Palma::WritePalmaActivityEntry() {} 140void Palma::WritePalmaActivityEntry() {}
143 141
144Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, 142Result Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown) {
145 u64 unknown) {
146 if (handle.npad_id != active_handle.npad_id) { 143 if (handle.npad_id != active_handle.npad_id) {
147 return InvalidPalmaHandle; 144 return InvalidPalmaHandle;
148 } 145 }
@@ -153,8 +150,8 @@ Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandl
153 return ResultSuccess; 150 return ResultSuccess;
154} 151}
155 152
156Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, 153Result Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
157 Common::ProcessAddress t_mem, u64 size) { 154 Common::ProcessAddress t_mem, u64 size) {
158 if (handle.npad_id != active_handle.npad_id) { 155 if (handle.npad_id != active_handle.npad_id) {
159 return InvalidPalmaHandle; 156 return InvalidPalmaHandle;
160 } 157 }
@@ -165,8 +162,8 @@ Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle
165 return ResultSuccess; 162 return ResultSuccess;
166} 163}
167 164
168Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, 165Result Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
169 s32 database_id_version_) { 166 s32 database_id_version_) {
170 if (handle.npad_id != active_handle.npad_id) { 167 if (handle.npad_id != active_handle.npad_id) {
171 return InvalidPalmaHandle; 168 return InvalidPalmaHandle;
172 } 169 }
@@ -178,8 +175,7 @@ Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnec
178 return ResultSuccess; 175 return ResultSuccess;
179} 176}
180 177
181Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( 178Result Palma::GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle) {
182 const PalmaConnectionHandle& handle) {
183 if (handle.npad_id != active_handle.npad_id) { 179 if (handle.npad_id != active_handle.npad_id) {
184 return InvalidPalmaHandle; 180 return InvalidPalmaHandle;
185 } 181 }
@@ -191,26 +187,26 @@ Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
191 return ResultSuccess; 187 return ResultSuccess;
192} 188}
193 189
194void Controller_Palma::SuspendPalmaFeature() {} 190void Palma::SuspendPalmaFeature() {}
195 191
196Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { 192Result Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
197 if (handle.npad_id != active_handle.npad_id) { 193 if (handle.npad_id != active_handle.npad_id) {
198 return InvalidPalmaHandle; 194 return InvalidPalmaHandle;
199 } 195 }
200 return operation.result; 196 return operation.result;
201} 197}
202void Controller_Palma::ReadPalmaPlayLog() {} 198void Palma::ReadPalmaPlayLog() {}
203 199
204void Controller_Palma::ResetPalmaPlayLog() {} 200void Palma::ResetPalmaPlayLog() {}
205 201
206void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { 202void Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
207 // If true controllers are able to be paired 203 // If true controllers are able to be paired
208 is_connectable = is_all_connectable; 204 is_connectable = is_all_connectable;
209} 205}
210 206
211void Controller_Palma::SetIsPalmaPairedConnectable() {} 207void Palma::SetIsPalmaPairedConnectable() {}
212 208
213Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { 209Result Palma::PairPalma(const PalmaConnectionHandle& handle) {
214 if (handle.npad_id != active_handle.npad_id) { 210 if (handle.npad_id != active_handle.npad_id) {
215 return InvalidPalmaHandle; 211 return InvalidPalmaHandle;
216 } 212 }
@@ -218,14 +214,14 @@ Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
218 return ResultSuccess; 214 return ResultSuccess;
219} 215}
220 216
221void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} 217void Palma::SetPalmaBoostMode(bool boost_mode) {}
222 218
223void Controller_Palma::CancelWritePalmaWaveEntry() {} 219void Palma::CancelWritePalmaWaveEntry() {}
224 220
225void Controller_Palma::EnablePalmaBoostMode() {} 221void Palma::EnablePalmaBoostMode() {}
226 222
227void Controller_Palma::GetPalmaBluetoothAddress() {} 223void Palma::GetPalmaBluetoothAddress() {}
228 224
229void Controller_Palma::SetDisallowedPalmaConnection() {} 225void Palma::SetDisallowedPalmaConnection() {}
230 226
231} // namespace Service::HID 227} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h
index a0491a819..a6047f36a 100644
--- a/src/core/hle/service/hid/controllers/palma.h
+++ b/src/core/hle/service/hid/controllers/palma.h
@@ -23,7 +23,7 @@ class EmulatedController;
23} // namespace Core::HID 23} // namespace Core::HID
24 24
25namespace Service::HID { 25namespace Service::HID {
26class Controller_Palma final : public ControllerBase { 26class Palma final : public ControllerBase {
27public: 27public:
28 using PalmaOperationData = std::array<u8, 0x140>; 28 using PalmaOperationData = std::array<u8, 0x140>;
29 29
@@ -97,9 +97,9 @@ public:
97 static_assert(sizeof(PalmaConnectionHandle) == 0x8, 97 static_assert(sizeof(PalmaConnectionHandle) == 0x8,
98 "PalmaConnectionHandle has incorrect size."); 98 "PalmaConnectionHandle has incorrect size.");
99 99
100 explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 100 explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
101 KernelHelpers::ServiceContext& service_context_); 101 KernelHelpers::ServiceContext& service_context_);
102 ~Controller_Palma() override; 102 ~Palma() override;
103 103
104 // Called when the controller is initialized 104 // Called when the controller is initialized
105 void OnInit() override; 105 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/seven_six_axis.cpp
index bcb272eaf..495568484 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/seven_six_axis.cpp
@@ -1,32 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <cstring>
5#include "common/common_types.h"
4#include "core/core.h" 6#include "core/core.h"
5#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
6#include "core/hid/emulated_console.h" 9#include "core/hid/emulated_console.h"
10#include "core/hid/emulated_devices.h"
7#include "core/hid/hid_core.h" 11#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/console_sixaxis.h" 12#include "core/hle/service/hid/controllers/seven_six_axis.h"
9#include "core/memory.h" 13#include "core/memory.h"
10 14
11namespace Service::HID { 15namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; 16SevenSixAxis::SevenSixAxis(Core::System& system_)
13
14Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_)
15 : ControllerBase{system_.HIDCore()}, system{system_} { 17 : ControllerBase{system_.HIDCore()}, system{system_} {
16 console = hid_core.GetEmulatedConsole(); 18 console = hid_core.GetEmulatedConsole();
17 static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
18 "ConsoleSharedMemory is bigger than the shared memory");
19 shared_memory = std::construct_at(
20 reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
21} 19}
22 20
23Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; 21SevenSixAxis::~SevenSixAxis() = default;
24
25void Controller_ConsoleSixAxis::OnInit() {}
26 22
27void Controller_ConsoleSixAxis::OnRelease() {} 23void SevenSixAxis::OnInit() {}
24void SevenSixAxis::OnRelease() {}
28 25
29void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 26void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 if (!IsControllerActivated() || transfer_memory == 0) { 27 if (!IsControllerActivated() || transfer_memory == 0) {
31 seven_sixaxis_lifo.buffer_count = 0; 28 seven_sixaxis_lifo.buffer_count = 0;
32 seven_sixaxis_lifo.buffer_tail = 0; 29 seven_sixaxis_lifo.buffer_tail = 0;
@@ -53,22 +50,17 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
53 -motion_status.quaternion.xyz.z, 50 -motion_status.quaternion.xyz.z,
54 }; 51 };
55 52
56 shared_memory->sampling_number++;
57 shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
58 shared_memory->verticalization_error = motion_status.verticalization_error;
59 shared_memory->gyro_bias = motion_status.gyro_bias;
60
61 // Update seven six axis transfer memory
62 seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); 53 seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
63 system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, 54 system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
64 sizeof(seven_sixaxis_lifo)); 55 sizeof(seven_sixaxis_lifo));
65} 56}
66 57
67void Controller_ConsoleSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { 58void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
68 transfer_memory = t_mem; 59 transfer_memory = t_mem;
69} 60}
70 61
71void Controller_ConsoleSixAxis::ResetTimestamp() { 62void SevenSixAxis::ResetTimestamp() {
72 last_saved_timestamp = last_global_timestamp; 63 last_saved_timestamp = last_global_timestamp;
73} 64}
65
74} // namespace Service::HID 66} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/seven_six_axis.h
index 7015d924c..40e3f5d12 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/seven_six_axis.h
@@ -1,10 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <array> 6#include "common/common_types.h"
7
8#include "common/quaternion.h" 7#include "common/quaternion.h"
9#include "common/typed_address.h" 8#include "common/typed_address.h"
10#include "core/hle/service/hid/controllers/controller_base.h" 9#include "core/hle/service/hid/controllers/controller_base.h"
@@ -19,10 +18,10 @@ class EmulatedConsole;
19} // namespace Core::HID 18} // namespace Core::HID
20 19
21namespace Service::HID { 20namespace Service::HID {
22class Controller_ConsoleSixAxis final : public ControllerBase { 21class SevenSixAxis final : public ControllerBase {
23public: 22public:
24 explicit Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_); 23 explicit SevenSixAxis(Core::System& system_);
25 ~Controller_ConsoleSixAxis() override; 24 ~SevenSixAxis() override;
26 25
27 // Called when the controller is initialized 26 // Called when the controller is initialized
28 void OnInit() override; 27 void OnInit() override;
@@ -51,28 +50,16 @@ private:
51 }; 50 };
52 static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); 51 static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
53 52
54 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
55 struct ConsoleSharedMemory {
56 u64 sampling_number{};
57 bool is_seven_six_axis_sensor_at_rest{};
58 INSERT_PADDING_BYTES(3); // padding
59 f32 verticalization_error{};
60 Common::Vec3f gyro_bias{};
61 INSERT_PADDING_BYTES(4); // padding
62 };
63 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
64
65 Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{}; 53 Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
66 static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); 54 static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
67 55
56 u64 last_saved_timestamp{};
57 u64 last_global_timestamp{};
58
68 SevenSixAxisState next_seven_sixaxis_state{}; 59 SevenSixAxisState next_seven_sixaxis_state{};
69 Common::ProcessAddress transfer_memory{}; 60 Common::ProcessAddress transfer_memory{};
70 ConsoleSharedMemory* shared_memory = nullptr;
71 Core::HID::EmulatedConsole* console = nullptr; 61 Core::HID::EmulatedConsole* console = nullptr;
72 62
73 u64 last_saved_timestamp{};
74 u64 last_global_timestamp{};
75
76 Core::System& system; 63 Core::System& system;
77}; 64};
78} // namespace Service::HID 65} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp
new file mode 100644
index 000000000..3d24a5c04
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/six_axis.cpp
@@ -0,0 +1,413 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/common_types.h"
5#include "core/core_timing.h"
6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/npad.h"
9#include "core/hle/service/hid/controllers/six_axis.h"
10#include "core/hle/service/hid/errors.h"
11#include "core/hle/service/hid/hid_util.h"
12
13namespace Service::HID {
14
15SixAxis::SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_)
16 : ControllerBase{hid_core_}, npad{npad_} {
17 for (std::size_t i = 0; i < controller_data.size(); ++i) {
18 auto& controller = controller_data[i];
19 controller.device = hid_core.GetEmulatedControllerByIndex(i);
20 }
21}
22
23SixAxis::~SixAxis() = default;
24
25void SixAxis::OnInit() {}
26void SixAxis::OnRelease() {}
27
28void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
29 if (!IsControllerActivated()) {
30 return;
31 }
32
33 for (std::size_t i = 0; i < controller_data.size(); ++i) {
34 auto& controller = controller_data[i];
35
36 const auto npad_id = IndexToNpadIdType(i);
37 const auto& controller_type = controller.device->GetNpadStyleIndex();
38
39 if (controller_type == Core::HID::NpadStyleIndex::None ||
40 !controller.device->IsConnected()) {
41 continue;
42 }
43
44 const auto& motion_state = controller.device->GetMotions();
45 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
46 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
47 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
48 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
49 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
50 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
51
52 auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id);
53 auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id);
54 auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id);
55 auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id);
56 auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id);
57 auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id);
58
59 // Clear previous state
60 sixaxis_fullkey_state = {};
61 sixaxis_handheld_state = {};
62 sixaxis_dual_left_state = {};
63 sixaxis_dual_right_state = {};
64 sixaxis_left_lifo_state = {};
65 sixaxis_right_lifo_state = {};
66
67 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
68 controller.sixaxis_at_rest = true;
69 for (std::size_t e = 0; e < motion_state.size(); ++e) {
70 controller.sixaxis_at_rest =
71 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
72 }
73 }
74
75 const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
76 const Core::HID::ControllerMotion& hid_state) {
77 using namespace std::literals::chrono_literals;
78 static constexpr Core::HID::SixAxisSensorState default_motion_state = {
79 .delta_time = std::chrono::nanoseconds(5ms).count(),
80 .accel = {0, 0, -1.0f},
81 .orientation =
82 {
83 Common::Vec3f{1.0f, 0, 0},
84 Common::Vec3f{0, 1.0f, 0},
85 Common::Vec3f{0, 0, 1.0f},
86 },
87 .attribute = {1},
88 };
89 if (!controller.sixaxis_sensor_enabled) {
90 state = default_motion_state;
91 return;
92 }
93 if (!Settings::values.motion_enabled.GetValue()) {
94 state = default_motion_state;
95 return;
96 }
97 state.attribute.is_connected.Assign(1);
98 state.delta_time = std::chrono::nanoseconds(5ms).count();
99 state.accel = hid_state.accel;
100 state.gyro = hid_state.gyro;
101 state.rotation = hid_state.rotation;
102 state.orientation = hid_state.orientation;
103 };
104
105 switch (controller_type) {
106 case Core::HID::NpadStyleIndex::None:
107 ASSERT(false);
108 break;
109 case Core::HID::NpadStyleIndex::ProController:
110 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
111 break;
112 case Core::HID::NpadStyleIndex::Handheld:
113 set_motion_state(sixaxis_handheld_state, motion_state[0]);
114 break;
115 case Core::HID::NpadStyleIndex::JoyconDual:
116 set_motion_state(sixaxis_dual_left_state, motion_state[0]);
117 set_motion_state(sixaxis_dual_right_state, motion_state[1]);
118 break;
119 case Core::HID::NpadStyleIndex::JoyconLeft:
120 set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
121 break;
122 case Core::HID::NpadStyleIndex::JoyconRight:
123 set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
124 break;
125 case Core::HID::NpadStyleIndex::Pokeball:
126 using namespace std::literals::chrono_literals;
127 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
128 sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
129 break;
130 default:
131 break;
132 }
133
134 sixaxis_fullkey_state.sampling_number =
135 sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
136 sixaxis_handheld_state.sampling_number =
137 sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
138 sixaxis_dual_left_state.sampling_number =
139 sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
140 sixaxis_dual_right_state.sampling_number =
141 sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
142 sixaxis_left_lifo_state.sampling_number =
143 sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
144 sixaxis_right_lifo_state.sampling_number =
145 sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
146
147 if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
148 // This buffer only is updated on handheld on HW
149 sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
150 } else {
151 // Handheld doesn't update this buffer on HW
152 sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
153 }
154
155 sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
156 sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
157 sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
158 sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
159 }
160}
161
162Result SixAxis::SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
163 Core::HID::GyroscopeZeroDriftMode drift_mode) {
164 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
165 if (is_valid.IsError()) {
166 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
167 return is_valid;
168 }
169
170 auto& sixaxis = GetSixaxisState(sixaxis_handle);
171 auto& controller = GetControllerFromHandle(sixaxis_handle);
172 sixaxis.gyroscope_zero_drift_mode = drift_mode;
173 controller.device->SetGyroscopeZeroDriftMode(drift_mode);
174
175 return ResultSuccess;
176}
177
178Result SixAxis::GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
179 Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
180 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
181 if (is_valid.IsError()) {
182 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
183 return is_valid;
184 }
185
186 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
187 drift_mode = sixaxis.gyroscope_zero_drift_mode;
188
189 return ResultSuccess;
190}
191
192Result SixAxis::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
193 bool& is_at_rest) const {
194 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
195 if (is_valid.IsError()) {
196 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
197 return is_valid;
198 }
199
200 const auto& controller = GetControllerFromHandle(sixaxis_handle);
201 is_at_rest = controller.sixaxis_at_rest;
202 return ResultSuccess;
203}
204
205Result SixAxis::LoadSixAxisSensorCalibrationParameter(
206 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
207 Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
208 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
209 if (is_valid.IsError()) {
210 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
211 return is_valid;
212 }
213
214 // TODO: Request this data to the controller. On error return 0xd8ca
215 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
216 calibration = sixaxis.calibration;
217 return ResultSuccess;
218}
219
220Result SixAxis::GetSixAxisSensorIcInformation(
221 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
222 Core::HID::SixAxisSensorIcInformation& ic_information) const {
223 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
224 if (is_valid.IsError()) {
225 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
226 return is_valid;
227 }
228
229 // TODO: Request this data to the controller. On error return 0xd8ca
230 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
231 ic_information = sixaxis.ic_information;
232 return ResultSuccess;
233}
234
235Result SixAxis::EnableSixAxisSensorUnalteredPassthrough(
236 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
237 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
238 if (is_valid.IsError()) {
239 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
240 return is_valid;
241 }
242
243 auto& sixaxis = GetSixaxisState(sixaxis_handle);
244 sixaxis.unaltered_passtrough = is_enabled;
245 return ResultSuccess;
246}
247
248Result SixAxis::IsSixAxisSensorUnalteredPassthroughEnabled(
249 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
250 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
251 if (is_valid.IsError()) {
252 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
253 return is_valid;
254 }
255
256 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
257 is_enabled = sixaxis.unaltered_passtrough;
258 return ResultSuccess;
259}
260
261Result SixAxis::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
262 bool sixaxis_status) {
263 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
264 if (is_valid.IsError()) {
265 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
266 return is_valid;
267 }
268
269 auto& controller = GetControllerFromHandle(sixaxis_handle);
270 controller.sixaxis_sensor_enabled = sixaxis_status;
271 return ResultSuccess;
272}
273
274Result SixAxis::IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
275 bool& is_fusion_enabled) const {
276 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
277 if (is_valid.IsError()) {
278 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
279 return is_valid;
280 }
281
282 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
283 is_fusion_enabled = sixaxis.is_fusion_enabled;
284
285 return ResultSuccess;
286}
287Result SixAxis::SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
288 bool is_fusion_enabled) {
289 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
290 if (is_valid.IsError()) {
291 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
292 return is_valid;
293 }
294
295 auto& sixaxis = GetSixaxisState(sixaxis_handle);
296 sixaxis.is_fusion_enabled = is_fusion_enabled;
297
298 return ResultSuccess;
299}
300
301Result SixAxis::SetSixAxisFusionParameters(
302 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
303 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
304 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
305 if (is_valid.IsError()) {
306 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
307 return is_valid;
308 }
309
310 const auto param1 = sixaxis_fusion_parameters.parameter1;
311 if (param1 < 0.0f || param1 > 1.0f) {
312 return InvalidSixAxisFusionRange;
313 }
314
315 auto& sixaxis = GetSixaxisState(sixaxis_handle);
316 sixaxis.fusion = sixaxis_fusion_parameters;
317
318 return ResultSuccess;
319}
320
321Result SixAxis::GetSixAxisFusionParameters(
322 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
323 Core::HID::SixAxisSensorFusionParameters& parameters) const {
324 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
325 if (is_valid.IsError()) {
326 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
327 return is_valid;
328 }
329
330 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
331 parameters = sixaxis.fusion;
332
333 return ResultSuccess;
334}
335
336SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
337 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
338 auto& controller = GetControllerFromHandle(sixaxis_handle);
339 switch (sixaxis_handle.npad_type) {
340 case Core::HID::NpadStyleIndex::ProController:
341 case Core::HID::NpadStyleIndex::Pokeball:
342 return controller.sixaxis_fullkey;
343 case Core::HID::NpadStyleIndex::Handheld:
344 return controller.sixaxis_handheld;
345 case Core::HID::NpadStyleIndex::JoyconDual:
346 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
347 return controller.sixaxis_dual_left;
348 }
349 return controller.sixaxis_dual_right;
350 case Core::HID::NpadStyleIndex::JoyconLeft:
351 return controller.sixaxis_left;
352 case Core::HID::NpadStyleIndex::JoyconRight:
353 return controller.sixaxis_right;
354 default:
355 return controller.sixaxis_unknown;
356 }
357}
358
359const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
360 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
361 const auto& controller = GetControllerFromHandle(sixaxis_handle);
362 switch (sixaxis_handle.npad_type) {
363 case Core::HID::NpadStyleIndex::ProController:
364 case Core::HID::NpadStyleIndex::Pokeball:
365 return controller.sixaxis_fullkey;
366 case Core::HID::NpadStyleIndex::Handheld:
367 return controller.sixaxis_handheld;
368 case Core::HID::NpadStyleIndex::JoyconDual:
369 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
370 return controller.sixaxis_dual_left;
371 }
372 return controller.sixaxis_dual_right;
373 case Core::HID::NpadStyleIndex::JoyconLeft:
374 return controller.sixaxis_left;
375 case Core::HID::NpadStyleIndex::JoyconRight:
376 return controller.sixaxis_right;
377 default:
378 return controller.sixaxis_unknown;
379 }
380}
381
382SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
383 const Core::HID::SixAxisSensorHandle& device_handle) {
384 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
385 return GetControllerFromNpadIdType(npad_id);
386}
387
388const SixAxis::NpadControllerData& SixAxis::GetControllerFromHandle(
389 const Core::HID::SixAxisSensorHandle& device_handle) const {
390 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
391 return GetControllerFromNpadIdType(npad_id);
392}
393
394SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
395 if (!IsNpadIdValid(npad_id)) {
396 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
397 npad_id = Core::HID::NpadIdType::Player1;
398 }
399 const auto npad_index = NpadIdTypeToIndex(npad_id);
400 return controller_data[npad_index];
401}
402
403const SixAxis::NpadControllerData& SixAxis::GetControllerFromNpadIdType(
404 Core::HID::NpadIdType npad_id) const {
405 if (!IsNpadIdValid(npad_id)) {
406 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
407 npad_id = Core::HID::NpadIdType::Player1;
408 }
409 const auto npad_index = NpadIdTypeToIndex(npad_id);
410 return controller_data[npad_index];
411}
412
413} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/six_axis.h b/src/core/hle/service/hid/controllers/six_axis.h
new file mode 100644
index 000000000..4c4f5dc7b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/six_axis.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hid/hid_types.h"
8#include "core/hle/service/hid/controllers/controller_base.h"
9#include "core/hle/service/hid/ring_lifo.h"
10
11namespace Core::HID {
12class EmulatedController;
13} // namespace Core::HID
14
15namespace Service::HID {
16class NPad;
17
18class SixAxis final : public ControllerBase {
19public:
20 explicit SixAxis(Core::HID::HIDCore& hid_core_, std::shared_ptr<NPad> npad_);
21 ~SixAxis() override;
22
23 // Called when the controller is initialized
24 void OnInit() override;
25
26 // When the controller is released
27 void OnRelease() override;
28
29 // When the controller is requesting an update for the shared memory
30 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
31
32 Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
33 Core::HID::GyroscopeZeroDriftMode drift_mode);
34 Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
35 Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
36 Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
37 bool& is_at_rest) const;
38 Result EnableSixAxisSensorUnalteredPassthrough(
39 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
40 Result IsSixAxisSensorUnalteredPassthroughEnabled(
41 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
42 Result LoadSixAxisSensorCalibrationParameter(
43 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
44 Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
45 Result GetSixAxisSensorIcInformation(
46 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
47 Core::HID::SixAxisSensorIcInformation& ic_information) const;
48 Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
49 bool sixaxis_status);
50 Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
51 bool& is_fusion_enabled) const;
52 Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
53 bool is_fusion_enabled);
54 Result SetSixAxisFusionParameters(
55 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
56 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
57 Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
58 Core::HID::SixAxisSensorFusionParameters& parameters) const;
59
60private:
61 static constexpr std::size_t NPAD_COUNT = 10;
62
63 struct SixaxisParameters {
64 bool is_fusion_enabled{true};
65 bool unaltered_passtrough{false};
66 Core::HID::SixAxisSensorFusionParameters fusion{};
67 Core::HID::SixAxisSensorCalibrationParameter calibration{};
68 Core::HID::SixAxisSensorIcInformation ic_information{};
69 Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
70 Core::HID::GyroscopeZeroDriftMode::Standard};
71 };
72
73 struct NpadControllerData {
74 Core::HID::EmulatedController* device = nullptr;
75
76 // Motion parameters
77 bool sixaxis_at_rest{true};
78 bool sixaxis_sensor_enabled{true};
79 SixaxisParameters sixaxis_fullkey{};
80 SixaxisParameters sixaxis_handheld{};
81 SixaxisParameters sixaxis_dual_left{};
82 SixaxisParameters sixaxis_dual_right{};
83 SixaxisParameters sixaxis_left{};
84 SixaxisParameters sixaxis_right{};
85 SixaxisParameters sixaxis_unknown{};
86
87 // Current pad state
88 Core::HID::SixAxisSensorState sixaxis_fullkey_state{};
89 Core::HID::SixAxisSensorState sixaxis_handheld_state{};
90 Core::HID::SixAxisSensorState sixaxis_dual_left_state{};
91 Core::HID::SixAxisSensorState sixaxis_dual_right_state{};
92 Core::HID::SixAxisSensorState sixaxis_left_lifo_state{};
93 Core::HID::SixAxisSensorState sixaxis_right_lifo_state{};
94 int callback_key{};
95 };
96
97 SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
98 const SixaxisParameters& GetSixaxisState(
99 const Core::HID::SixAxisSensorHandle& device_handle) const;
100
101 NpadControllerData& GetControllerFromHandle(
102 const Core::HID::SixAxisSensorHandle& device_handle);
103 const NpadControllerData& GetControllerFromHandle(
104 const Core::HID::SixAxisSensorHandle& device_handle) const;
105 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
106 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
107
108 std::shared_ptr<NPad> npad;
109 std::array<NpadControllerData, NPAD_COUNT> controller_data{};
110};
111} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 3ef91df4b..fcd973414 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -15,9 +15,9 @@
15namespace Service::HID { 15namespace Service::HID {
16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
17 17
18Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, 18TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
19 u8* raw_shared_memory_) 19 : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
20 : ControllerBase{hid_core_} { 20 touchscreen_height(Layout::ScreenUndocked::Height) {
21 static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, 21 static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
22 "TouchSharedMemory is bigger than the shared memory"); 22 "TouchSharedMemory is bigger than the shared memory");
23 shared_memory = std::construct_at( 23 shared_memory = std::construct_at(
@@ -25,13 +25,13 @@ Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
25 console = hid_core.GetEmulatedConsole(); 25 console = hid_core.GetEmulatedConsole();
26} 26}
27 27
28Controller_Touchscreen::~Controller_Touchscreen() = default; 28TouchScreen::~TouchScreen() = default;
29 29
30void Controller_Touchscreen::OnInit() {} 30void TouchScreen::OnInit() {}
31 31
32void Controller_Touchscreen::OnRelease() {} 32void TouchScreen::OnRelease() {}
33 33
34void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 34void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
35 shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); 35 shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
36 36
37 if (!IsControllerActivated()) { 37 if (!IsControllerActivated()) {
@@ -96,8 +96,8 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
96 if (id < active_fingers_count) { 96 if (id < active_fingers_count) {
97 const auto& [active_x, active_y] = active_fingers[id].position; 97 const auto& [active_x, active_y] = active_fingers[id].position;
98 touch_entry.position = { 98 touch_entry.position = {
99 .x = static_cast<u16>(active_x * Layout::ScreenUndocked::Width), 99 .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)),
100 .y = static_cast<u16>(active_y * Layout::ScreenUndocked::Height), 100 .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)),
101 }; 101 };
102 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; 102 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
103 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; 103 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
@@ -121,4 +121,9 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
121 shared_memory->touch_screen_lifo.WriteNextEntry(next_state); 121 shared_memory->touch_screen_lifo.WriteNextEntry(next_state);
122} 122}
123 123
124void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) {
125 touchscreen_width = width;
126 touchscreen_height = height;
127}
128
124} // namespace Service::HID 129} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index dd00921fd..79f026a81 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -14,10 +14,10 @@ class EmulatedConsole;
14} // namespace Core::HID 14} // namespace Core::HID
15 15
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Touchscreen final : public ControllerBase { 17class TouchScreen final : public ControllerBase {
18public: 18public:
19 explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
20 ~Controller_Touchscreen() override; 20 ~TouchScreen() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
23 void OnInit() override; 23 void OnInit() override;
@@ -28,6 +28,8 @@ public:
28 // When the controller is requesting an update for the shared memory 28 // When the controller is requesting an update for the shared memory
29 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 29 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
30 30
31 void SetTouchscreenDimensions(u32 width, u32 height);
32
31private: 33private:
32 static constexpr std::size_t MAX_FINGERS = 16; 34 static constexpr std::size_t MAX_FINGERS = 16;
33 35
@@ -53,5 +55,7 @@ private:
53 Core::HID::EmulatedConsole* console = nullptr; 55 Core::HID::EmulatedConsole* console = nullptr;
54 56
55 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; 57 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
58 u32 touchscreen_width;
59 u32 touchscreen_height;
56}; 60};
57} // namespace Service::HID 61} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 62119e2c5..0aaed1fa7 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -10,20 +10,19 @@
10namespace Service::HID { 10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12 12
13Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) 13XPad::XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
14 : ControllerBase{hid_core_} {
15 static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size, 14 static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
16 "XpadSharedMemory is bigger than the shared memory"); 15 "XpadSharedMemory is bigger than the shared memory");
17 shared_memory = std::construct_at( 16 shared_memory = std::construct_at(
18 reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); 17 reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
19} 18}
20Controller_XPad::~Controller_XPad() = default; 19XPad::~XPad() = default;
21 20
22void Controller_XPad::OnInit() {} 21void XPad::OnInit() {}
23 22
24void Controller_XPad::OnRelease() {} 23void XPad::OnRelease() {}
25 24
26void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 25void XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
27 if (!IsControllerActivated()) { 26 if (!IsControllerActivated()) {
28 shared_memory->basic_xpad_lifo.buffer_count = 0; 27 shared_memory->basic_xpad_lifo.buffer_count = 0;
29 shared_memory->basic_xpad_lifo.buffer_tail = 0; 28 shared_memory->basic_xpad_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index d01dee5fc..9e63a317a 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -10,10 +10,10 @@
10#include "core/hle/service/hid/ring_lifo.h" 10#include "core/hle/service/hid/ring_lifo.h"
11 11
12namespace Service::HID { 12namespace Service::HID {
13class Controller_XPad final : public ControllerBase { 13class XPad final : public ControllerBase {
14public: 14public:
15 explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 15 explicit XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
16 ~Controller_XPad() override; 16 ~XPad() override;
17 17
18 // Called when the controller is initialized 18 // Called when the controller is initialized
19 void OnInit() override; 19 void OnInit() override;
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index 0be6a7186..a7d1578d9 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -12,11 +12,12 @@
12#include "core/hle/service/hid/errors.h" 12#include "core/hle/service/hid/errors.h"
13#include "core/hle/service/hid/hid_firmware_settings.h" 13#include "core/hle/service/hid/hid_firmware_settings.h"
14#include "core/hle/service/hid/hid_server.h" 14#include "core/hle/service/hid/hid_server.h"
15#include "core/hle/service/hid/hid_util.h"
15#include "core/hle/service/hid/resource_manager.h" 16#include "core/hle/service/hid/resource_manager.h"
16#include "core/hle/service/ipc_helpers.h" 17#include "core/hle/service/ipc_helpers.h"
17#include "core/memory.h" 18#include "core/memory.h"
18 19
19#include "core/hle/service/hid/controllers/console_sixaxis.h" 20#include "core/hle/service/hid/controllers/console_six_axis.h"
20#include "core/hle/service/hid/controllers/controller_base.h" 21#include "core/hle/service/hid/controllers/controller_base.h"
21#include "core/hle/service/hid/controllers/debug_pad.h" 22#include "core/hle/service/hid/controllers/debug_pad.h"
22#include "core/hle/service/hid/controllers/gesture.h" 23#include "core/hle/service/hid/controllers/gesture.h"
@@ -24,9 +25,9 @@
24#include "core/hle/service/hid/controllers/mouse.h" 25#include "core/hle/service/hid/controllers/mouse.h"
25#include "core/hle/service/hid/controllers/npad.h" 26#include "core/hle/service/hid/controllers/npad.h"
26#include "core/hle/service/hid/controllers/palma.h" 27#include "core/hle/service/hid/controllers/palma.h"
27#include "core/hle/service/hid/controllers/stubbed.h" 28#include "core/hle/service/hid/controllers/seven_six_axis.h"
29#include "core/hle/service/hid/controllers/six_axis.h"
28#include "core/hle/service/hid/controllers/touchscreen.h" 30#include "core/hle/service/hid/controllers/touchscreen.h"
29#include "core/hle/service/hid/controllers/xpad.h"
30 31
31namespace Service::HID { 32namespace Service::HID {
32 33
@@ -50,8 +51,7 @@ private:
50 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; 51 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
51 52
52 if (resource_manager != nullptr) { 53 if (resource_manager != nullptr) {
53 resource_manager->GetController<Controller_NPad>(HidController::NPad) 54 resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
54 .InitializeVibrationDevice(vibration_device_handle);
55 } 55 }
56 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={}",
@@ -208,6 +208,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
208 {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, 208 {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
209 {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, 209 {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
210 {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, 210 {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
211 {1004, &IHidServer::SetTouchScreenResolution, "SetTouchScreenResolution"},
211 {2000, nullptr, "ActivateDigitizer"}, 212 {2000, nullptr, "ActivateDigitizer"},
212 }; 213 };
213 // clang-format on 214 // clang-format on
@@ -235,15 +236,14 @@ void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {
235 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 236 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
236 237
237 Result result = ResultSuccess; 238 Result result = ResultSuccess;
238 auto& debug_pad = 239 auto debug_pad = GetResourceManager()->GetDebugPad();
239 GetResourceManager()->GetController<Controller_DebugPad>(HidController::DebugPad);
240 240
241 if (!firmware_settings->IsDeviceManaged()) { 241 if (!firmware_settings->IsDeviceManaged()) {
242 result = debug_pad.Activate(); 242 result = debug_pad->Activate();
243 } 243 }
244 244
245 if (result.IsSuccess()) { 245 if (result.IsSuccess()) {
246 result = debug_pad.Activate(applet_resource_user_id); 246 result = debug_pad->Activate(applet_resource_user_id);
247 } 247 }
248 248
249 IPC::ResponseBuilder rb{ctx, 2}; 249 IPC::ResponseBuilder rb{ctx, 2};
@@ -257,15 +257,14 @@ void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) {
257 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 257 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
258 258
259 Result result = ResultSuccess; 259 Result result = ResultSuccess;
260 auto& touch_screen = 260 auto touch_screen = GetResourceManager()->GetTouchScreen();
261 GetResourceManager()->GetController<Controller_Touchscreen>(HidController::Touchscreen);
262 261
263 if (!firmware_settings->IsDeviceManaged()) { 262 if (!firmware_settings->IsDeviceManaged()) {
264 result = touch_screen.Activate(); 263 result = touch_screen->Activate();
265 } 264 }
266 265
267 if (result.IsSuccess()) { 266 if (result.IsSuccess()) {
268 result = touch_screen.Activate(applet_resource_user_id); 267 result = touch_screen->Activate(applet_resource_user_id);
269 } 268 }
270 269
271 IPC::ResponseBuilder rb{ctx, 2}; 270 IPC::ResponseBuilder rb{ctx, 2};
@@ -279,14 +278,14 @@ void IHidServer::ActivateMouse(HLERequestContext& ctx) {
279 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 278 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
280 279
281 Result result = ResultSuccess; 280 Result result = ResultSuccess;
282 auto& mouse = GetResourceManager()->GetController<Controller_Mouse>(HidController::Mouse); 281 auto mouse = GetResourceManager()->GetMouse();
283 282
284 if (!firmware_settings->IsDeviceManaged()) { 283 if (!firmware_settings->IsDeviceManaged()) {
285 result = mouse.Activate(); 284 result = mouse->Activate();
286 } 285 }
287 286
288 if (result.IsSuccess()) { 287 if (result.IsSuccess()) {
289 result = mouse.Activate(applet_resource_user_id); 288 result = mouse->Activate(applet_resource_user_id);
290 } 289 }
291 290
292 IPC::ResponseBuilder rb{ctx, 2}; 291 IPC::ResponseBuilder rb{ctx, 2};
@@ -300,15 +299,14 @@ void IHidServer::ActivateKeyboard(HLERequestContext& ctx) {
300 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 299 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
301 300
302 Result result = ResultSuccess; 301 Result result = ResultSuccess;
303 auto& keyboard = 302 auto keyboard = GetResourceManager()->GetKeyboard();
304 GetResourceManager()->GetController<Controller_Keyboard>(HidController::Keyboard);
305 303
306 if (!firmware_settings->IsDeviceManaged()) { 304 if (!firmware_settings->IsDeviceManaged()) {
307 result = keyboard.Activate(); 305 result = keyboard->Activate();
308 } 306 }
309 307
310 if (result.IsSuccess()) { 308 if (result.IsSuccess()) {
311 result = keyboard.Activate(applet_resource_user_id); 309 result = keyboard->Activate(applet_resource_user_id);
312 } 310 }
313 311
314 IPC::ResponseBuilder rb{ctx, 2}; 312 IPC::ResponseBuilder rb{ctx, 2};
@@ -502,8 +500,8 @@ void IHidServer::StartSixAxisSensor(HLERequestContext& ctx) {
502 500
503 const auto parameters{rp.PopRaw<Parameters>()}; 501 const auto parameters{rp.PopRaw<Parameters>()};
504 502
505 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 503 auto six_axis = GetResourceManager()->GetSixAxis();
506 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true); 504 const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, true);
507 505
508 LOG_DEBUG(Service_HID, 506 LOG_DEBUG(Service_HID,
509 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 507 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -525,8 +523,8 @@ void IHidServer::StopSixAxisSensor(HLERequestContext& ctx) {
525 523
526 const auto parameters{rp.PopRaw<Parameters>()}; 524 const auto parameters{rp.PopRaw<Parameters>()};
527 525
528 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 526 auto six_axis = GetResourceManager()->GetSixAxis();
529 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false); 527 const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, false);
530 528
531 LOG_DEBUG(Service_HID, 529 LOG_DEBUG(Service_HID,
532 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 530 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -549,9 +547,9 @@ void IHidServer::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) {
549 const auto parameters{rp.PopRaw<Parameters>()}; 547 const auto parameters{rp.PopRaw<Parameters>()};
550 548
551 bool is_enabled{}; 549 bool is_enabled{};
552 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 550 auto six_axis = GetResourceManager()->GetSixAxis();
553 const auto result = 551 const auto result =
554 controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); 552 six_axis->IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
555 553
556 LOG_DEBUG(Service_HID, 554 LOG_DEBUG(Service_HID,
557 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 555 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -575,9 +573,9 @@ void IHidServer::EnableSixAxisSensorFusion(HLERequestContext& ctx) {
575 573
576 const auto parameters{rp.PopRaw<Parameters>()}; 574 const auto parameters{rp.PopRaw<Parameters>()};
577 575
578 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 576 auto six_axis = GetResourceManager()->GetSixAxis();
579 const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, 577 const auto result = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle,
580 parameters.enable_sixaxis_sensor_fusion); 578 parameters.enable_sixaxis_sensor_fusion);
581 579
582 LOG_DEBUG(Service_HID, 580 LOG_DEBUG(Service_HID,
583 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " 581 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
@@ -602,9 +600,9 @@ void IHidServer::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
602 600
603 const auto parameters{rp.PopRaw<Parameters>()}; 601 const auto parameters{rp.PopRaw<Parameters>()};
604 602
605 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 603 auto six_axis = GetResourceManager()->GetSixAxis();
606 const auto result = 604 const auto result =
607 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); 605 six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
608 606
609 LOG_DEBUG(Service_HID, 607 LOG_DEBUG(Service_HID,
610 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " 608 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
@@ -629,10 +627,9 @@ void IHidServer::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
629 const auto parameters{rp.PopRaw<Parameters>()}; 627 const auto parameters{rp.PopRaw<Parameters>()};
630 628
631 Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; 629 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
632 const auto& controller = 630 auto six_axis = GetResourceManager()->GetSixAxis();
633 GetResourceManager()->GetController<Controller_NPad>(HidController::NPad);
634 const auto result = 631 const auto result =
635 controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); 632 six_axis->GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
636 633
637 LOG_DEBUG(Service_HID, 634 LOG_DEBUG(Service_HID,
638 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 635 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -660,10 +657,10 @@ void IHidServer::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
660 .parameter1 = 0.03f, 657 .parameter1 = 0.03f,
661 .parameter2 = 0.4f, 658 .parameter2 = 0.4f,
662 }; 659 };
663 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 660 auto six_axis = GetResourceManager()->GetSixAxis();
664 const auto result1 = 661 const auto result1 =
665 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); 662 six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
666 const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); 663 const auto result2 = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
667 664
668 LOG_DEBUG(Service_HID, 665 LOG_DEBUG(Service_HID,
669 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 666 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -684,8 +681,8 @@ void IHidServer::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
684 const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()}; 681 const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()};
685 const auto applet_resource_user_id{rp.Pop<u64>()}; 682 const auto applet_resource_user_id{rp.Pop<u64>()};
686 683
687 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 684 auto six_axis = GetResourceManager()->GetSixAxis();
688 const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); 685 const auto result = six_axis->SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
689 686
690 LOG_DEBUG(Service_HID, 687 LOG_DEBUG(Service_HID,
691 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " 688 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -709,8 +706,8 @@ void IHidServer::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
709 const auto parameters{rp.PopRaw<Parameters>()}; 706 const auto parameters{rp.PopRaw<Parameters>()};
710 707
711 auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; 708 auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
712 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 709 auto six_axis = GetResourceManager()->GetSixAxis();
713 const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); 710 const auto result = six_axis->GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
714 711
715 LOG_DEBUG(Service_HID, 712 LOG_DEBUG(Service_HID,
716 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 713 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -734,8 +731,8 @@ void IHidServer::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
734 const auto parameters{rp.PopRaw<Parameters>()}; 731 const auto parameters{rp.PopRaw<Parameters>()};
735 732
736 const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; 733 const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
737 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 734 auto six_axis = GetResourceManager()->GetSixAxis();
738 const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); 735 const auto result = six_axis->SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
739 736
740 LOG_DEBUG(Service_HID, 737 LOG_DEBUG(Service_HID,
741 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 738 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -758,8 +755,8 @@ void IHidServer::IsSixAxisSensorAtRest(HLERequestContext& ctx) {
758 const auto parameters{rp.PopRaw<Parameters>()}; 755 const auto parameters{rp.PopRaw<Parameters>()};
759 756
760 bool is_at_rest{}; 757 bool is_at_rest{};
761 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 758 auto six_axis = GetResourceManager()->GetSixAxis();
762 controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); 759 six_axis->IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
763 760
764 LOG_DEBUG(Service_HID, 761 LOG_DEBUG(Service_HID,
765 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 762 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -783,9 +780,9 @@ void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ct
783 const auto parameters{rp.PopRaw<Parameters>()}; 780 const auto parameters{rp.PopRaw<Parameters>()};
784 781
785 bool is_firmware_available{}; 782 bool is_firmware_available{};
786 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 783 auto controller = GetResourceManager()->GetNpad();
787 controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, 784 controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
788 is_firmware_available); 785 is_firmware_available);
789 786
790 LOG_WARNING( 787 LOG_WARNING(
791 Service_HID, 788 Service_HID,
@@ -809,9 +806,9 @@ void IHidServer::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx)
809 806
810 const auto parameters{rp.PopRaw<Parameters>()}; 807 const auto parameters{rp.PopRaw<Parameters>()};
811 808
812 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 809 auto six_axis = GetResourceManager()->GetSixAxis();
813 const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( 810 const auto result = six_axis->EnableSixAxisSensorUnalteredPassthrough(parameters.sixaxis_handle,
814 parameters.sixaxis_handle, parameters.enabled); 811 parameters.enabled);
815 812
816 LOG_DEBUG(Service_HID, 813 LOG_DEBUG(Service_HID,
817 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " 814 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
@@ -836,8 +833,8 @@ void IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& c
836 const auto parameters{rp.PopRaw<Parameters>()}; 833 const auto parameters{rp.PopRaw<Parameters>()};
837 834
838 bool is_unaltered_sisxaxis_enabled{}; 835 bool is_unaltered_sisxaxis_enabled{};
839 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 836 auto six_axis = GetResourceManager()->GetSixAxis();
840 const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( 837 const auto result = six_axis->IsSixAxisSensorUnalteredPassthroughEnabled(
841 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); 838 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
842 839
843 LOG_DEBUG( 840 LOG_DEBUG(
@@ -863,9 +860,9 @@ void IHidServer::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) {
863 const auto parameters{rp.PopRaw<Parameters>()}; 860 const auto parameters{rp.PopRaw<Parameters>()};
864 861
865 Core::HID::SixAxisSensorCalibrationParameter calibration{}; 862 Core::HID::SixAxisSensorCalibrationParameter calibration{};
866 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 863 auto six_axis = GetResourceManager()->GetSixAxis();
867 const auto result = 864 const auto result =
868 controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); 865 six_axis->LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
869 866
870 LOG_WARNING( 867 LOG_WARNING(
871 Service_HID, 868 Service_HID,
@@ -893,9 +890,9 @@ void IHidServer::GetSixAxisSensorIcInformation(HLERequestContext& ctx) {
893 const auto parameters{rp.PopRaw<Parameters>()}; 890 const auto parameters{rp.PopRaw<Parameters>()};
894 891
895 Core::HID::SixAxisSensorIcInformation ic_information{}; 892 Core::HID::SixAxisSensorIcInformation ic_information{};
896 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 893 auto six_axis = GetResourceManager()->GetSixAxis();
897 const auto result = 894 const auto result =
898 controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); 895 six_axis->GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
899 896
900 LOG_WARNING( 897 LOG_WARNING(
901 Service_HID, 898 Service_HID,
@@ -922,9 +919,9 @@ void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx)
922 919
923 const auto parameters{rp.PopRaw<Parameters>()}; 920 const auto parameters{rp.PopRaw<Parameters>()};
924 921
925 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 922 auto controller = GetResourceManager()->GetNpad();
926 const auto result = 923 const auto result =
927 controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); 924 controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
928 925
929 LOG_WARNING( 926 LOG_WARNING(
930 Service_HID, 927 Service_HID,
@@ -951,15 +948,15 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) {
951 parameters.basic_gesture_id, parameters.applet_resource_user_id); 948 parameters.basic_gesture_id, parameters.applet_resource_user_id);
952 949
953 Result result = ResultSuccess; 950 Result result = ResultSuccess;
954 auto& gesture = GetResourceManager()->GetController<Controller_Gesture>(HidController::Gesture); 951 auto gesture = GetResourceManager()->GetGesture();
955 952
956 if (!firmware_settings->IsDeviceManaged()) { 953 if (!firmware_settings->IsDeviceManaged()) {
957 result = gesture.Activate(); 954 result = gesture->Activate();
958 } 955 }
959 956
960 if (result.IsSuccess()) { 957 if (result.IsSuccess()) {
961 // TODO: Use gesture id here 958 // TODO: Use gesture id here
962 result = gesture.Activate(parameters.applet_resource_user_id); 959 result = gesture->Activate(parameters.applet_resource_user_id);
963 } 960 }
964 961
965 IPC::ResponseBuilder rb{ctx, 2}; 962 IPC::ResponseBuilder rb{ctx, 2};
@@ -977,9 +974,7 @@ void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
977 974
978 const auto parameters{rp.PopRaw<Parameters>()}; 975 const auto parameters{rp.PopRaw<Parameters>()};
979 976
980 GetResourceManager() 977 GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset});
981 ->GetController<Controller_NPad>(HidController::NPad)
982 .SetSupportedStyleSet({parameters.supported_styleset});
983 978
984 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", 979 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
985 parameters.supported_styleset, parameters.applet_resource_user_id); 980 parameters.supported_styleset, parameters.applet_resource_user_id);
@@ -996,19 +991,14 @@ void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
996 991
997 IPC::ResponseBuilder rb{ctx, 3}; 992 IPC::ResponseBuilder rb{ctx, 3};
998 rb.Push(ResultSuccess); 993 rb.Push(ResultSuccess);
999 rb.PushEnum(GetResourceManager() 994 rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw);
1000 ->GetController<Controller_NPad>(HidController::NPad)
1001 .GetSupportedStyleSet()
1002 .raw);
1003} 995}
1004 996
1005void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) { 997void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) {
1006 IPC::RequestParser rp{ctx}; 998 IPC::RequestParser rp{ctx};
1007 const auto applet_resource_user_id{rp.Pop<u64>()}; 999 const auto applet_resource_user_id{rp.Pop<u64>()};
1008 1000
1009 const auto result = GetResourceManager() 1001 const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer());
1010 ->GetController<Controller_NPad>(HidController::NPad)
1011 .SetSupportedNpadIdTypes(ctx.ReadBuffer());
1012 1002
1013 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1003 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1014 1004
@@ -1022,10 +1012,10 @@ void IHidServer::ActivateNpad(HLERequestContext& ctx) {
1022 1012
1023 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1013 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1024 1014
1025 auto& npad = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1015 auto npad = GetResourceManager()->GetNpad();
1026 1016
1027 // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0); 1017 // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0);
1028 const Result result = npad.Activate(applet_resource_user_id); 1018 const Result result = npad->Activate(applet_resource_user_id);
1029 1019
1030 IPC::ResponseBuilder rb{ctx, 2}; 1020 IPC::ResponseBuilder rb{ctx, 2};
1031 rb.Push(result); 1021 rb.Push(result);
@@ -1059,15 +1049,12 @@ void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) {
1059 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); 1049 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
1060 1050
1061 // Games expect this event to be signaled after calling this function 1051 // Games expect this event to be signaled after calling this function
1062 GetResourceManager() 1052 GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id);
1063 ->GetController<Controller_NPad>(HidController::NPad)
1064 .SignalStyleSetChangedEvent(parameters.npad_id);
1065 1053
1066 IPC::ResponseBuilder rb{ctx, 2, 1}; 1054 IPC::ResponseBuilder rb{ctx, 2, 1};
1067 rb.Push(ResultSuccess); 1055 rb.Push(ResultSuccess);
1068 rb.PushCopyObjects(GetResourceManager() 1056 rb.PushCopyObjects(
1069 ->GetController<Controller_NPad>(HidController::NPad) 1057 GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id));
1070 .GetStyleSetChangedEvent(parameters.npad_id));
1071} 1058}
1072 1059
1073void IHidServer::DisconnectNpad(HLERequestContext& ctx) { 1060void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
@@ -1081,8 +1068,8 @@ void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
1081 1068
1082 const auto parameters{rp.PopRaw<Parameters>()}; 1069 const auto parameters{rp.PopRaw<Parameters>()};
1083 1070
1084 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1071 auto controller = GetResourceManager()->GetNpad();
1085 controller.DisconnectNpad(parameters.npad_id); 1072 controller->DisconnectNpad(parameters.npad_id);
1086 1073
1087 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1074 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1088 parameters.applet_resource_user_id); 1075 parameters.applet_resource_user_id);
@@ -1096,8 +1083,8 @@ void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
1096 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; 1083 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
1097 1084
1098 Core::HID::LedPattern pattern{0, 0, 0, 0}; 1085 Core::HID::LedPattern pattern{0, 0, 0, 0};
1099 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1086 auto controller = GetResourceManager()->GetNpad();
1100 const auto result = controller.GetLedPattern(npad_id, pattern); 1087 const auto result = controller->GetLedPattern(npad_id, pattern);
1101 1088
1102 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); 1089 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
1103 1090
@@ -1109,7 +1096,7 @@ void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
1109void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { 1096void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1110 IPC::RequestParser rp{ctx}; 1097 IPC::RequestParser rp{ctx};
1111 struct Parameters { 1098 struct Parameters {
1112 Controller_NPad::NpadRevision revision; 1099 NPad::NpadRevision revision;
1113 INSERT_PADDING_WORDS_NOINIT(1); 1100 INSERT_PADDING_WORDS_NOINIT(1);
1114 u64 applet_resource_user_id; 1101 u64 applet_resource_user_id;
1115 }; 1102 };
@@ -1120,10 +1107,10 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1120 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, 1107 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
1121 parameters.applet_resource_user_id); 1108 parameters.applet_resource_user_id);
1122 1109
1123 auto& npad = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1110 auto npad = GetResourceManager()->GetNpad();
1124 1111
1125 // TODO: npad->SetRevision(applet_resource_user_id, revision); 1112 // TODO: npad->SetRevision(applet_resource_user_id, revision);
1126 const auto result = npad.Activate(parameters.applet_resource_user_id); 1113 const auto result = npad->Activate(parameters.applet_resource_user_id);
1127 1114
1128 IPC::ResponseBuilder rb{ctx, 2}; 1115 IPC::ResponseBuilder rb{ctx, 2};
1129 rb.Push(result); 1116 rb.Push(result);
@@ -1132,11 +1119,9 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1132void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) { 1119void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
1133 IPC::RequestParser rp{ctx}; 1120 IPC::RequestParser rp{ctx};
1134 const auto applet_resource_user_id{rp.Pop<u64>()}; 1121 const auto applet_resource_user_id{rp.Pop<u64>()};
1135 const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()}; 1122 const auto hold_type{rp.PopEnum<NPad::NpadJoyHoldType>()};
1136 1123
1137 GetResourceManager() 1124 GetResourceManager()->GetNpad()->SetHoldType(hold_type);
1138 ->GetController<Controller_NPad>(HidController::NPad)
1139 .SetHoldType(hold_type);
1140 1125
1141 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", 1126 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
1142 applet_resource_user_id, hold_type); 1127 applet_resource_user_id, hold_type);
@@ -1153,8 +1138,7 @@ void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) {
1153 1138
1154 IPC::ResponseBuilder rb{ctx, 4}; 1139 IPC::ResponseBuilder rb{ctx, 4};
1155 rb.Push(ResultSuccess); 1140 rb.Push(ResultSuccess);
1156 rb.PushEnum( 1141 rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType());
1157 GetResourceManager()->GetController<Controller_NPad>(HidController::NPad).GetHoldType());
1158} 1142}
1159 1143
1160void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { 1144void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
@@ -1169,10 +1153,9 @@ void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx)
1169 const auto parameters{rp.PopRaw<Parameters>()}; 1153 const auto parameters{rp.PopRaw<Parameters>()};
1170 1154
1171 Core::HID::NpadIdType new_npad_id{}; 1155 Core::HID::NpadIdType new_npad_id{};
1172 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1156 auto controller = GetResourceManager()->GetNpad();
1173 controller.SetNpadMode(new_npad_id, parameters.npad_id, 1157 controller->SetNpadMode(new_npad_id, parameters.npad_id, NPad::NpadJoyDeviceType::Left,
1174 Controller_NPad::NpadJoyDeviceType::Left, 1158 NPad::NpadJoyAssignmentMode::Single);
1175 Controller_NPad::NpadJoyAssignmentMode::Single);
1176 1159
1177 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1160 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1178 parameters.applet_resource_user_id); 1161 parameters.applet_resource_user_id);
@@ -1187,16 +1170,16 @@ void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1187 Core::HID::NpadIdType npad_id; 1170 Core::HID::NpadIdType npad_id;
1188 INSERT_PADDING_WORDS_NOINIT(1); 1171 INSERT_PADDING_WORDS_NOINIT(1);
1189 u64 applet_resource_user_id; 1172 u64 applet_resource_user_id;
1190 Controller_NPad::NpadJoyDeviceType npad_joy_device_type; 1173 NPad::NpadJoyDeviceType npad_joy_device_type;
1191 }; 1174 };
1192 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 1175 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1193 1176
1194 const auto parameters{rp.PopRaw<Parameters>()}; 1177 const auto parameters{rp.PopRaw<Parameters>()};
1195 1178
1196 Core::HID::NpadIdType new_npad_id{}; 1179 Core::HID::NpadIdType new_npad_id{};
1197 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1180 auto controller = GetResourceManager()->GetNpad();
1198 controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, 1181 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1199 Controller_NPad::NpadJoyAssignmentMode::Single); 1182 NPad::NpadJoyAssignmentMode::Single);
1200 1183
1201 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1184 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1202 parameters.npad_id, parameters.applet_resource_user_id, 1185 parameters.npad_id, parameters.applet_resource_user_id,
@@ -1218,12 +1201,11 @@ void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
1218 const auto parameters{rp.PopRaw<Parameters>()}; 1201 const auto parameters{rp.PopRaw<Parameters>()};
1219 1202
1220 Core::HID::NpadIdType new_npad_id{}; 1203 Core::HID::NpadIdType new_npad_id{};
1221 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1204 auto controller = GetResourceManager()->GetNpad();
1222 controller.SetNpadMode(new_npad_id, parameters.npad_id, {}, 1205 controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NPad::NpadJoyAssignmentMode::Dual);
1223 Controller_NPad::NpadJoyAssignmentMode::Dual);
1224 1206
1225 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1207 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1226 parameters.applet_resource_user_id); 1208 parameters.applet_resource_user_id); // Spams a lot when controller applet is open
1227 1209
1228 IPC::ResponseBuilder rb{ctx, 2}; 1210 IPC::ResponseBuilder rb{ctx, 2};
1229 rb.Push(ResultSuccess); 1211 rb.Push(ResultSuccess);
@@ -1235,8 +1217,8 @@ void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) {
1235 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; 1217 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1236 const auto applet_resource_user_id{rp.Pop<u64>()}; 1218 const auto applet_resource_user_id{rp.Pop<u64>()};
1237 1219
1238 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1220 auto controller = GetResourceManager()->GetNpad();
1239 const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); 1221 const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
1240 1222
1241 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", 1223 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1242 npad_id_1, npad_id_2, applet_resource_user_id); 1224 npad_id_1, npad_id_2, applet_resource_user_id);
@@ -1249,9 +1231,7 @@ void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) {
1249 IPC::RequestParser rp{ctx}; 1231 IPC::RequestParser rp{ctx};
1250 const auto applet_resource_user_id{rp.Pop<u64>()}; 1232 const auto applet_resource_user_id{rp.Pop<u64>()};
1251 1233
1252 GetResourceManager() 1234 GetResourceManager()->GetNpad()->StartLRAssignmentMode();
1253 ->GetController<Controller_NPad>(HidController::NPad)
1254 .StartLRAssignmentMode();
1255 1235
1256 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1236 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1257 1237
@@ -1263,9 +1243,7 @@ void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) {
1263 IPC::RequestParser rp{ctx}; 1243 IPC::RequestParser rp{ctx};
1264 const auto applet_resource_user_id{rp.Pop<u64>()}; 1244 const auto applet_resource_user_id{rp.Pop<u64>()};
1265 1245
1266 GetResourceManager() 1246 GetResourceManager()->GetNpad()->StopLRAssignmentMode();
1267 ->GetController<Controller_NPad>(HidController::NPad)
1268 .StopLRAssignmentMode();
1269 1247
1270 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1248 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1271 1249
@@ -1276,11 +1254,9 @@ void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) {
1276void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) { 1254void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
1277 IPC::RequestParser rp{ctx}; 1255 IPC::RequestParser rp{ctx};
1278 const auto applet_resource_user_id{rp.Pop<u64>()}; 1256 const auto applet_resource_user_id{rp.Pop<u64>()};
1279 const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()}; 1257 const auto activation_mode{rp.PopEnum<NPad::NpadHandheldActivationMode>()};
1280 1258
1281 GetResourceManager() 1259 GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode);
1282 ->GetController<Controller_NPad>(HidController::NPad)
1283 .SetNpadHandheldActivationMode(activation_mode);
1284 1260
1285 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", 1261 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
1286 applet_resource_user_id, activation_mode); 1262 applet_resource_user_id, activation_mode);
@@ -1297,9 +1273,7 @@ void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
1297 1273
1298 IPC::ResponseBuilder rb{ctx, 4}; 1274 IPC::ResponseBuilder rb{ctx, 4};
1299 rb.Push(ResultSuccess); 1275 rb.Push(ResultSuccess);
1300 rb.PushEnum(GetResourceManager() 1276 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode());
1301 ->GetController<Controller_NPad>(HidController::NPad)
1302 .GetNpadHandheldActivationMode());
1303} 1277}
1304 1278
1305void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { 1279void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) {
@@ -1308,8 +1282,8 @@ void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) {
1308 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; 1282 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1309 const auto applet_resource_user_id{rp.Pop<u64>()}; 1283 const auto applet_resource_user_id{rp.Pop<u64>()};
1310 1284
1311 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1285 auto controller = GetResourceManager()->GetNpad();
1312 const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2); 1286 const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2);
1313 1287
1314 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", 1288 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1315 npad_id_1, npad_id_2, applet_resource_user_id); 1289 npad_id_1, npad_id_2, applet_resource_user_id);
@@ -1330,9 +1304,9 @@ void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext&
1330 const auto parameters{rp.PopRaw<Parameters>()}; 1304 const auto parameters{rp.PopRaw<Parameters>()};
1331 1305
1332 bool is_enabled = false; 1306 bool is_enabled = false;
1333 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1307 auto controller = GetResourceManager()->GetNpad();
1334 const auto result = 1308 const auto result =
1335 controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); 1309 controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1336 1310
1337 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 1311 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1338 parameters.npad_id, parameters.applet_resource_user_id); 1312 parameters.npad_id, parameters.applet_resource_user_id);
@@ -1354,8 +1328,8 @@ void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ct
1354 1328
1355 const auto parameters{rp.PopRaw<Parameters>()}; 1329 const auto parameters{rp.PopRaw<Parameters>()};
1356 1330
1357 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1331 auto controller = GetResourceManager()->GetNpad();
1358 const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( 1332 const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled(
1359 parameters.is_enabled, parameters.npad_id); 1333 parameters.is_enabled, parameters.npad_id);
1360 1334
1361 LOG_DEBUG(Service_HID, 1335 LOG_DEBUG(Service_HID,
@@ -1372,17 +1346,17 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext
1372 Core::HID::NpadIdType npad_id; 1346 Core::HID::NpadIdType npad_id;
1373 INSERT_PADDING_WORDS_NOINIT(1); 1347 INSERT_PADDING_WORDS_NOINIT(1);
1374 u64 applet_resource_user_id; 1348 u64 applet_resource_user_id;
1375 Controller_NPad::NpadJoyDeviceType npad_joy_device_type; 1349 NPad::NpadJoyDeviceType npad_joy_device_type;
1376 }; 1350 };
1377 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 1351 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1378 1352
1379 const auto parameters{rp.PopRaw<Parameters>()}; 1353 const auto parameters{rp.PopRaw<Parameters>()};
1380 1354
1381 Core::HID::NpadIdType new_npad_id{}; 1355 Core::HID::NpadIdType new_npad_id{};
1382 auto& controller = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad); 1356 auto controller = GetResourceManager()->GetNpad();
1383 const auto is_reassigned = 1357 const auto is_reassigned =
1384 controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, 1358 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1385 Controller_NPad::NpadJoyAssignmentMode::Single); 1359 NPad::NpadJoyAssignmentMode::Single);
1386 1360
1387 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1361 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1388 parameters.npad_id, parameters.applet_resource_user_id, 1362 parameters.npad_id, parameters.applet_resource_user_id,
@@ -1405,9 +1379,8 @@ void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
1405 1379
1406 const auto parameters{rp.PopRaw<Parameters>()}; 1380 const auto parameters{rp.PopRaw<Parameters>()};
1407 1381
1408 GetResourceManager() 1382 GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp(
1409 ->GetController<Controller_NPad>(HidController::NPad) 1383 parameters.analog_stick_use_center_clamp);
1410 .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp);
1411 1384
1412 LOG_WARNING(Service_HID, 1385 LOG_WARNING(Service_HID,
1413 "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", 1386 "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}",
@@ -1451,8 +1424,7 @@ void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1451void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { 1424void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1452 IPC::RequestParser rp{ctx}; 1425 IPC::RequestParser rp{ctx};
1453 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; 1426 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1454 const auto& controller = 1427 const auto controller = GetResourceManager()->GetNpad();
1455 GetResourceManager()->GetController<Controller_NPad>(HidController::NPad);
1456 1428
1457 Core::HID::VibrationDeviceInfo vibration_device_info; 1429 Core::HID::VibrationDeviceInfo vibration_device_info;
1458 bool check_device_index = false; 1430 bool check_device_index = false;
@@ -1496,7 +1468,7 @@ void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1496 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", 1468 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
1497 vibration_device_info.type, vibration_device_info.position); 1469 vibration_device_info.type, vibration_device_info.position);
1498 1470
1499 const auto result = controller.IsDeviceHandleValid(vibration_device_handle); 1471 const auto result = IsVibrationHandleValid(vibration_device_handle);
1500 if (result.IsError()) { 1472 if (result.IsError()) {
1501 IPC::ResponseBuilder rb{ctx, 2}; 1473 IPC::ResponseBuilder rb{ctx, 2};
1502 rb.Push(result); 1474 rb.Push(result);
@@ -1520,9 +1492,8 @@ void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
1520 1492
1521 const auto parameters{rp.PopRaw<Parameters>()}; 1493 const auto parameters{rp.PopRaw<Parameters>()};
1522 1494
1523 GetResourceManager() 1495 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
1524 ->GetController<Controller_NPad>(HidController::NPad) 1496 parameters.vibration_value);
1525 .VibrateController(parameters.vibration_device_handle, parameters.vibration_value);
1526 1497
1527 LOG_DEBUG(Service_HID, 1498 LOG_DEBUG(Service_HID,
1528 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 1499 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -1553,9 +1524,8 @@ void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
1553 1524
1554 IPC::ResponseBuilder rb{ctx, 6}; 1525 IPC::ResponseBuilder rb{ctx, 6};
1555 rb.Push(ResultSuccess); 1526 rb.Push(ResultSuccess);
1556 rb.PushRaw(GetResourceManager() 1527 rb.PushRaw(
1557 ->GetController<Controller_NPad>(HidController::NPad) 1528 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle));
1558 .GetLastVibration(parameters.vibration_device_handle));
1559} 1529}
1560 1530
1561void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { 1531void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
@@ -1563,7 +1533,7 @@ void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
1563 1533
1564 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1534 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1565 rb.Push(ResultSuccess); 1535 rb.Push(ResultSuccess);
1566 rb.PushIpcInterface<IActiveVibrationDeviceList>(system, resource_manager); 1536 rb.PushIpcInterface<IActiveVibrationDeviceList>(system, GetResourceManager());
1567} 1537}
1568 1538
1569void IHidServer::PermitVibration(HLERequestContext& ctx) { 1539void IHidServer::PermitVibration(HLERequestContext& ctx) {
@@ -1606,9 +1576,7 @@ void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
1606 auto vibration_values = std::span( 1576 auto vibration_values = std::span(
1607 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); 1577 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
1608 1578
1609 GetResourceManager() 1579 GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values);
1610 ->GetController<Controller_NPad>(HidController::NPad)
1611 .VibrateControllers(vibration_device_handles, vibration_values);
1612 1580
1613 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1581 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1614 1582
@@ -1662,9 +1630,8 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1662 } 1630 }
1663 }(); 1631 }();
1664 1632
1665 GetResourceManager() 1633 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
1666 ->GetController<Controller_NPad>(HidController::NPad) 1634 vibration_value);
1667 .VibrateController(parameters.vibration_device_handle, vibration_value);
1668 1635
1669 LOG_DEBUG(Service_HID, 1636 LOG_DEBUG(Service_HID,
1670 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " 1637 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
@@ -1688,9 +1655,8 @@ void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
1688 1655
1689 const auto parameters{rp.PopRaw<Parameters>()}; 1656 const auto parameters{rp.PopRaw<Parameters>()};
1690 1657
1691 const auto last_vibration = GetResourceManager() 1658 const auto last_vibration =
1692 ->GetController<Controller_NPad>(HidController::NPad) 1659 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle);
1693 .GetLastVibration(parameters.vibration_device_handle);
1694 1660
1695 const auto gc_erm_command = [last_vibration] { 1661 const auto gc_erm_command = [last_vibration] {
1696 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { 1662 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
@@ -1725,9 +1691,7 @@ void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
1725 IPC::RequestParser rp{ctx}; 1691 IPC::RequestParser rp{ctx};
1726 const auto applet_resource_user_id{rp.Pop<u64>()}; 1692 const auto applet_resource_user_id{rp.Pop<u64>()};
1727 1693
1728 GetResourceManager() 1694 GetResourceManager()->GetNpad()->SetPermitVibrationSession(true);
1729 ->GetController<Controller_NPad>(HidController::NPad)
1730 .SetPermitVibrationSession(true);
1731 1695
1732 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1696 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1733 1697
@@ -1736,9 +1700,7 @@ void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
1736} 1700}
1737 1701
1738void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) { 1702void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) {
1739 GetResourceManager() 1703 GetResourceManager()->GetNpad()->SetPermitVibrationSession(false);
1740 ->GetController<Controller_NPad>(HidController::NPad)
1741 .SetPermitVibrationSession(false);
1742 1704
1743 LOG_DEBUG(Service_HID, "called"); 1705 LOG_DEBUG(Service_HID, "called");
1744 1706
@@ -1765,9 +1727,8 @@ void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
1765 1727
1766 IPC::ResponseBuilder rb{ctx, 3}; 1728 IPC::ResponseBuilder rb{ctx, 3};
1767 rb.Push(ResultSuccess); 1729 rb.Push(ResultSuccess);
1768 rb.Push(GetResourceManager() 1730 rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted(
1769 ->GetController<Controller_NPad>(HidController::NPad) 1731 parameters.vibration_device_handle));
1770 .IsVibrationDeviceMounted(parameters.vibration_device_handle));
1771} 1732}
1772 1733
1773void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { 1734void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
@@ -1777,15 +1738,14 @@ void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
1777 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1738 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1778 1739
1779 Result result = ResultSuccess; 1740 Result result = ResultSuccess;
1780 auto console_sixaxis = GetResourceManager()->GetController<Controller_ConsoleSixAxis>( 1741 auto console_sixaxis = GetResourceManager()->GetConsoleSixAxis();
1781 HidController::ConsoleSixAxisSensor);
1782 1742
1783 if (!firmware_settings->IsDeviceManaged()) { 1743 if (!firmware_settings->IsDeviceManaged()) {
1784 result = console_sixaxis.Activate(); 1744 result = console_sixaxis->Activate();
1785 } 1745 }
1786 1746
1787 if (result.IsSuccess()) { 1747 if (result.IsSuccess()) {
1788 result = console_sixaxis.Activate(applet_resource_user_id); 1748 result = console_sixaxis->Activate(applet_resource_user_id);
1789 } 1749 }
1790 1750
1791 IPC::ResponseBuilder rb{ctx, 2}; 1751 IPC::ResponseBuilder rb{ctx, 2};
@@ -1839,15 +1799,14 @@ void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) {
1839 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1799 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1840 1800
1841 Result result = ResultSuccess; 1801 Result result = ResultSuccess;
1842 auto console_sixaxis = GetResourceManager()->GetController<Controller_ConsoleSixAxis>( 1802 auto seven_sixaxis = GetResourceManager()->GetSevenSixAxis();
1843 HidController::ConsoleSixAxisSensor);
1844 1803
1845 if (!firmware_settings->IsDeviceManaged()) { 1804 if (!firmware_settings->IsDeviceManaged()) {
1846 result = console_sixaxis.Activate(); 1805 result = seven_sixaxis->Activate();
1847 } 1806 }
1848 1807
1849 if (result.IsSuccess()) { 1808 if (result.IsSuccess()) {
1850 console_sixaxis.Activate(applet_resource_user_id); 1809 seven_sixaxis->Activate(applet_resource_user_id);
1851 } 1810 }
1852 1811
1853 IPC::ResponseBuilder rb{ctx, 2}; 1812 IPC::ResponseBuilder rb{ctx, 2};
@@ -1911,13 +1870,10 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1911 ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size"); 1870 ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size");
1912 1871
1913 // Activate console six axis controller 1872 // Activate console six axis controller
1914 GetResourceManager() 1873 GetResourceManager()->GetConsoleSixAxis()->Activate();
1915 ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) 1874 GetResourceManager()->GetSevenSixAxis()->Activate();
1916 .Activate();
1917 1875
1918 GetResourceManager() 1876 GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
1919 ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1920 .SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
1921 1877
1922 LOG_WARNING(Service_HID, 1878 LOG_WARNING(Service_HID,
1923 "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, " 1879 "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, "
@@ -1943,9 +1899,7 @@ void IHidServer::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) {
1943 IPC::RequestParser rp{ctx}; 1899 IPC::RequestParser rp{ctx};
1944 const auto applet_resource_user_id{rp.Pop<u64>()}; 1900 const auto applet_resource_user_id{rp.Pop<u64>()};
1945 1901
1946 GetResourceManager() 1902 GetResourceManager()->GetSevenSixAxis()->ResetTimestamp();
1947 ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1948 .ResetTimestamp();
1949 1903
1950 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1904 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1951 1905
@@ -1977,9 +1931,9 @@ void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) {
1977 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 1931 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1978 parameters.npad_id, parameters.applet_resource_user_id); 1932 parameters.npad_id, parameters.applet_resource_user_id);
1979 1933
1980 Controller_Palma::PalmaConnectionHandle handle; 1934 Palma::PalmaConnectionHandle handle;
1981 auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); 1935 auto controller = GetResourceManager()->GetPalma();
1982 const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); 1936 const auto result = controller->GetPalmaConnectionHandle(parameters.npad_id, handle);
1983 1937
1984 IPC::ResponseBuilder rb{ctx, 4}; 1938 IPC::ResponseBuilder rb{ctx, 4};
1985 rb.Push(result); 1939 rb.Push(result);
@@ -1988,12 +1942,12 @@ void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) {
1988 1942
1989void IHidServer::InitializePalma(HLERequestContext& ctx) { 1943void IHidServer::InitializePalma(HLERequestContext& ctx) {
1990 IPC::RequestParser rp{ctx}; 1944 IPC::RequestParser rp{ctx};
1991 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 1945 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1992 1946
1993 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 1947 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1994 1948
1995 auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); 1949 auto controller = GetResourceManager()->GetPalma();
1996 const auto result = controller.InitializePalma(connection_handle); 1950 const auto result = controller->InitializePalma(connection_handle);
1997 1951
1998 IPC::ResponseBuilder rb{ctx, 2}; 1952 IPC::ResponseBuilder rb{ctx, 2};
1999 rb.Push(result); 1953 rb.Push(result);
@@ -2001,27 +1955,27 @@ void IHidServer::InitializePalma(HLERequestContext& ctx) {
2001 1955
2002void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) { 1956void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) {
2003 IPC::RequestParser rp{ctx}; 1957 IPC::RequestParser rp{ctx};
2004 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 1958 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2005 1959
2006 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 1960 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2007 1961
2008 auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); 1962 auto controller = GetResourceManager()->GetPalma();
2009 1963
2010 IPC::ResponseBuilder rb{ctx, 2, 1}; 1964 IPC::ResponseBuilder rb{ctx, 2, 1};
2011 rb.Push(ResultSuccess); 1965 rb.Push(ResultSuccess);
2012 rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); 1966 rb.PushCopyObjects(controller->AcquirePalmaOperationCompleteEvent(connection_handle));
2013} 1967}
2014 1968
2015void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) { 1969void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) {
2016 IPC::RequestParser rp{ctx}; 1970 IPC::RequestParser rp{ctx};
2017 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 1971 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2018 1972
2019 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 1973 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2020 1974
2021 Controller_Palma::PalmaOperationType operation_type; 1975 Palma::PalmaOperationType operation_type;
2022 Controller_Palma::PalmaOperationData data; 1976 Palma::PalmaOperationData data;
2023 auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); 1977 auto controller = GetResourceManager()->GetPalma();
2024 const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); 1978 const auto result = controller->GetPalmaOperationInfo(connection_handle, operation_type, data);
2025 1979
2026 if (result.IsError()) { 1980 if (result.IsError()) {
2027 IPC::ResponseBuilder rb{ctx, 2}; 1981 IPC::ResponseBuilder rb{ctx, 2};
@@ -2036,14 +1990,14 @@ void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) {
2036 1990
2037void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) { 1991void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) {
2038 IPC::RequestParser rp{ctx}; 1992 IPC::RequestParser rp{ctx};
2039 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 1993 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2040 const auto palma_activity{rp.Pop<u64>()}; 1994 const auto palma_activity{rp.Pop<u64>()};
2041 1995
2042 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", 1996 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}",
2043 connection_handle.npad_id, palma_activity); 1997 connection_handle.npad_id, palma_activity);
2044 1998
2045 auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); 1999 auto controller = GetResourceManager()->GetPalma();
2046 const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); 2000 const auto result = controller->PlayPalmaActivity(connection_handle, palma_activity);
2047 2001
2048 IPC::ResponseBuilder rb{ctx, 2}; 2002 IPC::ResponseBuilder rb{ctx, 2};
2049 rb.Push(result); 2003 rb.Push(result);
@@ -2051,14 +2005,14 @@ void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) {
2051 2005
2052void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) { 2006void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) {
2053 IPC::RequestParser rp{ctx}; 2007 IPC::RequestParser rp{ctx};
2054 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2008 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2055 const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()}; 2009 const auto fr_mode{rp.PopEnum<Palma::PalmaFrModeType>()};
2056 2010
2057 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", 2011 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}",
2058 connection_handle.npad_id, fr_mode); 2012 connection_handle.npad_id, fr_mode);
2059 2013
2060 auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); 2014 auto controller = GetResourceManager()->GetPalma();
2061 const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); 2015 const auto result = controller->SetPalmaFrModeType(connection_handle, fr_mode);
2062 2016
2063 IPC::ResponseBuilder rb{ctx, 2}; 2017 IPC::ResponseBuilder rb{ctx, 2};
2064 rb.Push(result); 2018 rb.Push(result);
@@ -2066,12 +2020,12 @@ void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) {
2066 2020
2067void IHidServer::ReadPalmaStep(HLERequestContext& ctx) { 2021void IHidServer::ReadPalmaStep(HLERequestContext& ctx) {
2068 IPC::RequestParser rp{ctx}; 2022 IPC::RequestParser rp{ctx};
2069 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2023 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2070 2024
2071 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 2025 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2072 2026
2073 auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); 2027 auto controller = GetResourceManager()->GetPalma();
2074 const auto result = controller.ReadPalmaStep(connection_handle); 2028 const auto result = controller->ReadPalmaStep(connection_handle);
2075 2029
2076 IPC::ResponseBuilder rb{ctx, 2}; 2030 IPC::ResponseBuilder rb{ctx, 2};
2077 rb.Push(result); 2031 rb.Push(result);
@@ -2082,7 +2036,7 @@ void IHidServer::EnablePalmaStep(HLERequestContext& ctx) {
2082 struct Parameters { 2036 struct Parameters {
2083 bool is_enabled; 2037 bool is_enabled;
2084 INSERT_PADDING_WORDS_NOINIT(1); 2038 INSERT_PADDING_WORDS_NOINIT(1);
2085 Controller_Palma::PalmaConnectionHandle connection_handle; 2039 Palma::PalmaConnectionHandle connection_handle;
2086 }; 2040 };
2087 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 2041 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2088 2042
@@ -2091,9 +2045,9 @@ void IHidServer::EnablePalmaStep(HLERequestContext& ctx) {
2091 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", 2045 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}",
2092 parameters.connection_handle.npad_id, parameters.is_enabled); 2046 parameters.connection_handle.npad_id, parameters.is_enabled);
2093 2047
2094 auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); 2048 auto controller = GetResourceManager()->GetPalma();
2095 const auto result = 2049 const auto result =
2096 controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); 2050 controller->EnablePalmaStep(parameters.connection_handle, parameters.is_enabled);
2097 2051
2098 IPC::ResponseBuilder rb{ctx, 2}; 2052 IPC::ResponseBuilder rb{ctx, 2};
2099 rb.Push(result); 2053 rb.Push(result);
@@ -2101,12 +2055,12 @@ void IHidServer::EnablePalmaStep(HLERequestContext& ctx) {
2101 2055
2102void IHidServer::ResetPalmaStep(HLERequestContext& ctx) { 2056void IHidServer::ResetPalmaStep(HLERequestContext& ctx) {
2103 IPC::RequestParser rp{ctx}; 2057 IPC::RequestParser rp{ctx};
2104 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2058 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2105 2059
2106 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 2060 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2107 2061
2108 auto& controller = GetResourceManager()->GetController<Controller_Palma>(HidController::Palma); 2062 auto controller = GetResourceManager()->GetPalma();
2109 const auto result = controller.ResetPalmaStep(connection_handle); 2063 const auto result = controller->ResetPalmaStep(connection_handle);
2110 2064
2111 IPC::ResponseBuilder rb{ctx, 2}; 2065 IPC::ResponseBuilder rb{ctx, 2};
2112 rb.Push(result); 2066 rb.Push(result);
@@ -2128,13 +2082,11 @@ void IHidServer::WritePalmaApplicationSection(HLERequestContext& ctx) {
2128 2082
2129void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) { 2083void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) {
2130 IPC::RequestParser rp{ctx}; 2084 IPC::RequestParser rp{ctx};
2131 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2085 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2132 2086
2133 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 2087 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2134 2088
2135 GetResourceManager() 2089 GetResourceManager()->GetPalma()->ReadPalmaUniqueCode(connection_handle);
2136 ->GetController<Controller_Palma>(HidController::Palma)
2137 .ReadPalmaUniqueCode(connection_handle);
2138 2090
2139 IPC::ResponseBuilder rb{ctx, 2}; 2091 IPC::ResponseBuilder rb{ctx, 2};
2140 rb.Push(ResultSuccess); 2092 rb.Push(ResultSuccess);
@@ -2142,13 +2094,11 @@ void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) {
2142 2094
2143void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) { 2095void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) {
2144 IPC::RequestParser rp{ctx}; 2096 IPC::RequestParser rp{ctx};
2145 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2097 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2146 2098
2147 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 2099 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2148 2100
2149 GetResourceManager() 2101 GetResourceManager()->GetPalma()->SetPalmaUniqueCodeInvalid(connection_handle);
2150 ->GetController<Controller_Palma>(HidController::Palma)
2151 .SetPalmaUniqueCodeInvalid(connection_handle);
2152 2102
2153 IPC::ResponseBuilder rb{ctx, 2}; 2103 IPC::ResponseBuilder rb{ctx, 2};
2154 rb.Push(ResultSuccess); 2104 rb.Push(ResultSuccess);
@@ -2163,7 +2113,7 @@ void IHidServer::WritePalmaActivityEntry(HLERequestContext& ctx) {
2163 2113
2164void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { 2114void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) {
2165 IPC::RequestParser rp{ctx}; 2115 IPC::RequestParser rp{ctx};
2166 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2116 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2167 const auto unknown{rp.Pop<u64>()}; 2117 const auto unknown{rp.Pop<u64>()};
2168 2118
2169 [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); 2119 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
@@ -2171,9 +2121,7 @@ void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) {
2171 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", 2121 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2172 connection_handle.npad_id, unknown); 2122 connection_handle.npad_id, unknown);
2173 2123
2174 GetResourceManager() 2124 GetResourceManager()->GetPalma()->WritePalmaRgbLedPatternEntry(connection_handle, unknown);
2175 ->GetController<Controller_Palma>(HidController::Palma)
2176 .WritePalmaRgbLedPatternEntry(connection_handle, unknown);
2177 2125
2178 IPC::ResponseBuilder rb{ctx, 2}; 2126 IPC::ResponseBuilder rb{ctx, 2};
2179 rb.Push(ResultSuccess); 2127 rb.Push(ResultSuccess);
@@ -2181,8 +2129,8 @@ void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) {
2181 2129
2182void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) { 2130void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
2183 IPC::RequestParser rp{ctx}; 2131 IPC::RequestParser rp{ctx};
2184 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2132 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2185 const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()}; 2133 const auto wave_set{rp.PopEnum<Palma::PalmaWaveSet>()};
2186 const auto unknown{rp.Pop<u64>()}; 2134 const auto unknown{rp.Pop<u64>()};
2187 const auto t_mem_size{rp.Pop<u64>()}; 2135 const auto t_mem_size{rp.Pop<u64>()};
2188 const auto t_mem_handle{ctx.GetCopyHandle(0)}; 2136 const auto t_mem_handle{ctx.GetCopyHandle(0)};
@@ -2207,9 +2155,8 @@ void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
2207 "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", 2155 "t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
2208 connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); 2156 connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
2209 2157
2210 GetResourceManager() 2158 GetResourceManager()->GetPalma()->WritePalmaWaveEntry(connection_handle, wave_set,
2211 ->GetController<Controller_Palma>(HidController::Palma) 2159 t_mem->GetSourceAddress(), t_mem_size);
2212 .WritePalmaWaveEntry(connection_handle, wave_set, t_mem->GetSourceAddress(), t_mem_size);
2213 2160
2214 IPC::ResponseBuilder rb{ctx, 2}; 2161 IPC::ResponseBuilder rb{ctx, 2};
2215 rb.Push(ResultSuccess); 2162 rb.Push(ResultSuccess);
@@ -2220,7 +2167,7 @@ void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2220 struct Parameters { 2167 struct Parameters {
2221 s32 database_id_version; 2168 s32 database_id_version;
2222 INSERT_PADDING_WORDS_NOINIT(1); 2169 INSERT_PADDING_WORDS_NOINIT(1);
2223 Controller_Palma::PalmaConnectionHandle connection_handle; 2170 Palma::PalmaConnectionHandle connection_handle;
2224 }; 2171 };
2225 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 2172 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2226 2173
@@ -2229,10 +2176,8 @@ void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2229 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", 2176 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}",
2230 parameters.connection_handle.npad_id, parameters.database_id_version); 2177 parameters.connection_handle.npad_id, parameters.database_id_version);
2231 2178
2232 GetResourceManager() 2179 GetResourceManager()->GetPalma()->SetPalmaDataBaseIdentificationVersion(
2233 ->GetController<Controller_Palma>(HidController::Palma) 2180 parameters.connection_handle, parameters.database_id_version);
2234 .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle,
2235 parameters.database_id_version);
2236 2181
2237 IPC::ResponseBuilder rb{ctx, 2}; 2182 IPC::ResponseBuilder rb{ctx, 2};
2238 rb.Push(ResultSuccess); 2183 rb.Push(ResultSuccess);
@@ -2240,13 +2185,11 @@ void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2240 2185
2241void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { 2186void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2242 IPC::RequestParser rp{ctx}; 2187 IPC::RequestParser rp{ctx};
2243 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2188 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2244 2189
2245 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 2190 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2246 2191
2247 GetResourceManager() 2192 GetResourceManager()->GetPalma()->GetPalmaDataBaseIdentificationVersion(connection_handle);
2248 ->GetController<Controller_Palma>(HidController::Palma)
2249 .GetPalmaDataBaseIdentificationVersion(connection_handle);
2250 2193
2251 IPC::ResponseBuilder rb{ctx, 2}; 2194 IPC::ResponseBuilder rb{ctx, 2};
2252 rb.Push(ResultSuccess); 2195 rb.Push(ResultSuccess);
@@ -2261,13 +2204,12 @@ void IHidServer::SuspendPalmaFeature(HLERequestContext& ctx) {
2261 2204
2262void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) { 2205void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) {
2263 IPC::RequestParser rp{ctx}; 2206 IPC::RequestParser rp{ctx};
2264 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2207 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2265 2208
2266 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 2209 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2267 2210
2268 const auto result = GetResourceManager() 2211 const auto result =
2269 ->GetController<Controller_Palma>(HidController::Palma) 2212 GetResourceManager()->GetPalma()->GetPalmaOperationResult(connection_handle);
2270 .GetPalmaOperationResult(connection_handle);
2271 2213
2272 IPC::ResponseBuilder rb{ctx, 2}; 2214 IPC::ResponseBuilder rb{ctx, 2};
2273 rb.Push(result); 2215 rb.Push(result);
@@ -2302,9 +2244,7 @@ void IHidServer::SetIsPalmaAllConnectable(HLERequestContext& ctx) {
2302 "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", 2244 "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}",
2303 parameters.is_palma_all_connectable, parameters.applet_resource_user_id); 2245 parameters.is_palma_all_connectable, parameters.applet_resource_user_id);
2304 2246
2305 GetResourceManager() 2247 GetResourceManager()->GetPalma()->SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
2306 ->GetController<Controller_Palma>(HidController::Palma)
2307 .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
2308 2248
2309 IPC::ResponseBuilder rb{ctx, 2}; 2249 IPC::ResponseBuilder rb{ctx, 2};
2310 rb.Push(ResultSuccess); 2250 rb.Push(ResultSuccess);
@@ -2319,13 +2259,11 @@ void IHidServer::SetIsPalmaPairedConnectable(HLERequestContext& ctx) {
2319 2259
2320void IHidServer::PairPalma(HLERequestContext& ctx) { 2260void IHidServer::PairPalma(HLERequestContext& ctx) {
2321 IPC::RequestParser rp{ctx}; 2261 IPC::RequestParser rp{ctx};
2322 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2262 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2323 2263
2324 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); 2264 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2325 2265
2326 GetResourceManager() 2266 GetResourceManager()->GetPalma()->PairPalma(connection_handle);
2327 ->GetController<Controller_Palma>(HidController::Palma)
2328 .PairPalma(connection_handle);
2329 2267
2330 IPC::ResponseBuilder rb{ctx, 2}; 2268 IPC::ResponseBuilder rb{ctx, 2};
2331 rb.Push(ResultSuccess); 2269 rb.Push(ResultSuccess);
@@ -2337,9 +2275,7 @@ void IHidServer::SetPalmaBoostMode(HLERequestContext& ctx) {
2337 2275
2338 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); 2276 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
2339 2277
2340 GetResourceManager() 2278 GetResourceManager()->GetPalma()->SetPalmaBoostMode(palma_boost_mode);
2341 ->GetController<Controller_Palma>(HidController::Palma)
2342 .SetPalmaBoostMode(palma_boost_mode);
2343 2279
2344 IPC::ResponseBuilder rb{ctx, 2}; 2280 IPC::ResponseBuilder rb{ctx, 2};
2345 rb.Push(ResultSuccess); 2281 rb.Push(ResultSuccess);
@@ -2376,11 +2312,9 @@ void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) {
2376void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) { 2312void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
2377 IPC::RequestParser rp{ctx}; 2313 IPC::RequestParser rp{ctx};
2378 const auto applet_resource_user_id{rp.Pop<u64>()}; 2314 const auto applet_resource_user_id{rp.Pop<u64>()};
2379 const auto communication_mode{rp.PopEnum<Controller_NPad::NpadCommunicationMode>()}; 2315 const auto communication_mode{rp.PopEnum<NPad::NpadCommunicationMode>()};
2380 2316
2381 GetResourceManager() 2317 GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode);
2382 ->GetController<Controller_NPad>(HidController::NPad)
2383 .SetNpadCommunicationMode(communication_mode);
2384 2318
2385 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", 2319 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}",
2386 applet_resource_user_id, communication_mode); 2320 applet_resource_user_id, communication_mode);
@@ -2396,9 +2330,7 @@ void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) {
2396 2330
2397 IPC::ResponseBuilder rb{ctx, 4}; 2331 IPC::ResponseBuilder rb{ctx, 4};
2398 rb.Push(ResultSuccess); 2332 rb.Push(ResultSuccess);
2399 rb.PushEnum(GetResourceManager() 2333 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode());
2400 ->GetController<Controller_NPad>(HidController::NPad)
2401 .GetNpadCommunicationMode());
2402} 2334}
2403 2335
2404void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { 2336void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
@@ -2432,6 +2364,21 @@ void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
2432 rb.Push(false); 2364 rb.Push(false);
2433} 2365}
2434 2366
2367void IHidServer::SetTouchScreenResolution(HLERequestContext& ctx) {
2368 IPC::RequestParser rp{ctx};
2369 const auto width{rp.Pop<u32>()};
2370 const auto height{rp.Pop<u32>()};
2371 const auto applet_resource_user_id{rp.Pop<u64>()};
2372
2373 GetResourceManager()->GetTouchScreen()->SetTouchscreenDimensions(width, height);
2374
2375 LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height,
2376 applet_resource_user_id);
2377
2378 IPC::ResponseBuilder rb{ctx, 2};
2379 rb.Push(ResultSuccess);
2380}
2381
2435std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() { 2382std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
2436 resource_manager->Initialize(); 2383 resource_manager->Initialize();
2437 return resource_manager; 2384 return resource_manager;
diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h
index eb2e8e7f4..cc7c4ebdd 100644
--- a/src/core/hle/service/hid/hid_server.h
+++ b/src/core/hle/service/hid/hid_server.h
@@ -141,6 +141,7 @@ private:
141 void GetNpadCommunicationMode(HLERequestContext& ctx); 141 void GetNpadCommunicationMode(HLERequestContext& ctx);
142 void SetTouchScreenConfiguration(HLERequestContext& ctx); 142 void SetTouchScreenConfiguration(HLERequestContext& ctx);
143 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); 143 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
144 void SetTouchScreenResolution(HLERequestContext& ctx);
144 145
145 std::shared_ptr<ResourceManager> resource_manager; 146 std::shared_ptr<ResourceManager> resource_manager;
146 std::shared_ptr<HidFirmwareSettings> firmware_settings; 147 std::shared_ptr<HidFirmwareSettings> firmware_settings;
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index 83cfadada..b56d0347a 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -16,251 +16,461 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
16 resource_manager{resource} { 16 resource_manager{resource} {
17 // clang-format off 17 // clang-format off
18 static const FunctionInfo functions[] = { 18 static const FunctionInfo functions[] = {
19 {31, nullptr, "SendKeyboardLockKeyEvent"}, 19 {31, nullptr, "SendKeyboardLockKeyEvent"},
20 {101, nullptr, "AcquireHomeButtonEventHandle"}, 20 {101, nullptr, "AcquireHomeButtonEventHandle"},
21 {111, nullptr, "ActivateHomeButton"}, 21 {111, nullptr, "ActivateHomeButton"},
22 {121, nullptr, "AcquireSleepButtonEventHandle"}, 22 {121, nullptr, "AcquireSleepButtonEventHandle"},
23 {131, nullptr, "ActivateSleepButton"}, 23 {131, nullptr, "ActivateSleepButton"},
24 {141, nullptr, "AcquireCaptureButtonEventHandle"}, 24 {141, nullptr, "AcquireCaptureButtonEventHandle"},
25 {151, nullptr, "ActivateCaptureButton"}, 25 {151, nullptr, "ActivateCaptureButton"},
26 {161, nullptr, "GetPlatformConfig"}, 26 {161, nullptr, "GetPlatformConfig"},
27 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, 27 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
28 {211, nullptr, "GetNpadsWithNfc"}, 28 {211, nullptr, "GetNpadsWithNfc"},
29 {212, nullptr, "AcquireNfcActivateEventHandle"}, 29 {212, nullptr, "AcquireNfcActivateEventHandle"},
30 {213, nullptr, "ActivateNfc"}, 30 {213, nullptr, "ActivateNfc"},
31 {214, nullptr, "GetXcdHandleForNpadWithNfc"}, 31 {214, nullptr, "GetXcdHandleForNpadWithNfc"},
32 {215, nullptr, "IsNfcActivated"}, 32 {215, nullptr, "IsNfcActivated"},
33 {230, nullptr, "AcquireIrSensorEventHandle"}, 33 {230, nullptr, "AcquireIrSensorEventHandle"},
34 {231, nullptr, "ActivateIrSensor"}, 34 {231, nullptr, "ActivateIrSensor"},
35 {232, nullptr, "GetIrSensorState"}, 35 {232, nullptr, "GetIrSensorState"},
36 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 36 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
37 {301, nullptr, "ActivateNpadSystem"}, 37 {301, nullptr, "ActivateNpadSystem"},
38 {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, 38 {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
39 {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, 39 {304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"},
40 {305, nullptr, "DisableAssigningSingleOnSlSrPress"}, 40 {305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"},
41 {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"}, 41 {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"},
42 {307, nullptr, "GetNpadSystemExtStyle"}, 42 {307, nullptr, "GetNpadSystemExtStyle"},
43 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, 43 {308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"},
44 {309, nullptr, "GetNpadFullKeyGripColor"}, 44 {309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"},
45 {310, nullptr, "GetMaskedSupportedNpadStyleSet"}, 45 {310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"},
46 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, 46 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
47 {312, nullptr, "SetSupportedNpadStyleSetAll"}, 47 {312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"},
48 {313, nullptr, "GetNpadCaptureButtonAssignment"}, 48 {313, nullptr, "GetNpadCaptureButtonAssignment"},
49 {314, nullptr, "GetAppletFooterUiType"}, 49 {314, nullptr, "GetAppletFooterUiType"},
50 {315, nullptr, "GetAppletDetailedUiType"}, 50 {315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"},
51 {316, nullptr, "GetNpadInterfaceType"}, 51 {316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"},
52 {317, nullptr, "GetNpadLeftRightInterfaceType"}, 52 {317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"},
53 {318, nullptr, "HasBattery"}, 53 {318, &IHidSystemServer::HasBattery, "HasBattery"},
54 {319, nullptr, "HasLeftRightBattery"}, 54 {319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"},
55 {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, 55 {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
56 {322, nullptr, "GetIrSensorState"}, 56 {322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"},
57 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 57 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
58 {324, nullptr, "GetUniquePadButtonSet"}, 58 {324, nullptr, "GetUniquePadButtonSet"},
59 {325, nullptr, "GetUniquePadColor"}, 59 {325, nullptr, "GetUniquePadColor"},
60 {326, nullptr, "GetUniquePadAppletDetailedUiType"}, 60 {326, nullptr, "GetUniquePadAppletDetailedUiType"},
61 {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, 61 {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
62 {328, nullptr, "AttachAbstractedPadToNpad"}, 62 {328, nullptr, "AttachAbstractedPadToNpad"},
63 {329, nullptr, "DetachAbstractedPadAll"}, 63 {329, nullptr, "DetachAbstractedPadAll"},
64 {330, nullptr, "CheckAbstractedPadConnection"}, 64 {330, nullptr, "CheckAbstractedPadConnection"},
65 {500, nullptr, "SetAppletResourceUserId"}, 65 {500, nullptr, "SetAppletResourceUserId"},
66 {501, nullptr, "RegisterAppletResourceUserId"}, 66 {501, nullptr, "RegisterAppletResourceUserId"},
67 {502, nullptr, "UnregisterAppletResourceUserId"}, 67 {502, nullptr, "UnregisterAppletResourceUserId"},
68 {503, nullptr, "EnableAppletToGetInput"}, 68 {503, nullptr, "EnableAppletToGetInput"},
69 {504, nullptr, "SetAruidValidForVibration"}, 69 {504, nullptr, "SetAruidValidForVibration"},
70 {505, nullptr, "EnableAppletToGetSixAxisSensor"}, 70 {505, nullptr, "EnableAppletToGetSixAxisSensor"},
71 {506, nullptr, "EnableAppletToGetPadInput"}, 71 {506, nullptr, "EnableAppletToGetPadInput"},
72 {507, nullptr, "EnableAppletToGetTouchScreen"}, 72 {507, nullptr, "EnableAppletToGetTouchScreen"},
73 {510, nullptr, "SetVibrationMasterVolume"}, 73 {510, nullptr, "SetVibrationMasterVolume"},
74 {511, nullptr, "GetVibrationMasterVolume"}, 74 {511, nullptr, "GetVibrationMasterVolume"},
75 {512, nullptr, "BeginPermitVibrationSession"}, 75 {512, nullptr, "BeginPermitVibrationSession"},
76 {513, nullptr, "EndPermitVibrationSession"}, 76 {513, nullptr, "EndPermitVibrationSession"},
77 {514, nullptr, "Unknown514"}, 77 {514, nullptr, "Unknown514"},
78 {520, nullptr, "EnableHandheldHids"}, 78 {520, nullptr, "EnableHandheldHids"},
79 {521, nullptr, "DisableHandheldHids"}, 79 {521, nullptr, "DisableHandheldHids"},
80 {522, nullptr, "SetJoyConRailEnabled"}, 80 {522, nullptr, "SetJoyConRailEnabled"},
81 {523, nullptr, "IsJoyConRailEnabled"}, 81 {523, nullptr, "IsJoyConRailEnabled"},
82 {524, nullptr, "IsHandheldHidsEnabled"}, 82 {524, nullptr, "IsHandheldHidsEnabled"},
83 {525, nullptr, "IsJoyConAttachedOnAllRail"}, 83 {525, nullptr, "IsJoyConAttachedOnAllRail"},
84 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, 84 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
85 {541, nullptr, "GetPlayReportControllerUsages"}, 85 {541, nullptr, "GetPlayReportControllerUsages"},
86 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, 86 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
87 {543, nullptr, "GetRegisteredDevicesOld"}, 87 {543, nullptr, "GetRegisteredDevicesOld"},
88 {544, nullptr, "AcquireConnectionTriggerTimeoutEvent"}, 88 {544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"},
89 {545, nullptr, "SendConnectionTrigger"}, 89 {545, nullptr, "SendConnectionTrigger"},
90 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, 90 {546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"},
91 {547, nullptr, "GetAllowedBluetoothLinksCount"}, 91 {547, nullptr, "GetAllowedBluetoothLinksCount"},
92 {548, nullptr, "GetRegisteredDevices"}, 92 {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
93 {549, nullptr, "GetConnectableRegisteredDevices"}, 93 {549, nullptr, "GetConnectableRegisteredDevices"},
94 {700, nullptr, "ActivateUniquePad"}, 94 {700, nullptr, "ActivateUniquePad"},
95 {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, 95 {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
96 {703, nullptr, "GetUniquePadIds"}, 96 {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
97 {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, 97 {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
98 {800, nullptr, "ListSixAxisSensorHandles"}, 98 {800, nullptr, "ListSixAxisSensorHandles"},
99 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, 99 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
100 {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, 100 {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
101 {803, nullptr, "StartSixAxisSensorUserCalibration"}, 101 {803, nullptr, "StartSixAxisSensorUserCalibration"},
102 {804, nullptr, "CancelSixAxisSensorUserCalibration"}, 102 {804, nullptr, "CancelSixAxisSensorUserCalibration"},
103 {805, nullptr, "GetUniquePadBluetoothAddress"}, 103 {805, nullptr, "GetUniquePadBluetoothAddress"},
104 {806, nullptr, "DisconnectUniquePad"}, 104 {806, nullptr, "DisconnectUniquePad"},
105 {807, nullptr, "GetUniquePadType"}, 105 {807, nullptr, "GetUniquePadType"},
106 {808, nullptr, "GetUniquePadInterface"}, 106 {808, nullptr, "GetUniquePadInterface"},
107 {809, nullptr, "GetUniquePadSerialNumber"}, 107 {809, nullptr, "GetUniquePadSerialNumber"},
108 {810, nullptr, "GetUniquePadControllerNumber"}, 108 {810, nullptr, "GetUniquePadControllerNumber"},
109 {811, nullptr, "GetSixAxisSensorUserCalibrationStage"}, 109 {811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
110 {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"}, 110 {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
111 {821, nullptr, "StartAnalogStickManualCalibration"}, 111 {821, nullptr, "StartAnalogStickManualCalibration"},
112 {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"}, 112 {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
113 {823, nullptr, "CancelAnalogStickManualCalibration"}, 113 {823, nullptr, "CancelAnalogStickManualCalibration"},
114 {824, nullptr, "ResetAnalogStickManualCalibration"}, 114 {824, nullptr, "ResetAnalogStickManualCalibration"},
115 {825, nullptr, "GetAnalogStickState"}, 115 {825, nullptr, "GetAnalogStickState"},
116 {826, nullptr, "GetAnalogStickManualCalibrationStage"}, 116 {826, nullptr, "GetAnalogStickManualCalibrationStage"},
117 {827, nullptr, "IsAnalogStickButtonPressed"}, 117 {827, nullptr, "IsAnalogStickButtonPressed"},
118 {828, nullptr, "IsAnalogStickInReleasePosition"}, 118 {828, nullptr, "IsAnalogStickInReleasePosition"},
119 {829, nullptr, "IsAnalogStickInCircumference"}, 119 {829, nullptr, "IsAnalogStickInCircumference"},
120 {830, nullptr, "SetNotificationLedPattern"}, 120 {830, nullptr, "SetNotificationLedPattern"},
121 {831, nullptr, "SetNotificationLedPatternWithTimeout"}, 121 {831, nullptr, "SetNotificationLedPatternWithTimeout"},
122 {832, nullptr, "PrepareHidsForNotificationWake"}, 122 {832, nullptr, "PrepareHidsForNotificationWake"},
123 {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, 123 {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
124 {851, nullptr, "EnableUsbFullKeyController"}, 124 {851, nullptr, "EnableUsbFullKeyController"},
125 {852, nullptr, "IsUsbConnected"}, 125 {852, nullptr, "IsUsbConnected"},
126 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, 126 {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"},
127 {900, nullptr, "ActivateInputDetector"}, 127 {900, nullptr, "ActivateInputDetector"},
128 {901, nullptr, "NotifyInputDetector"}, 128 {901, nullptr, "NotifyInputDetector"},
129 {1000, nullptr, "InitializeFirmwareUpdate"}, 129 {1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"},
130 {1001, nullptr, "GetFirmwareVersion"}, 130 {1001, nullptr, "GetFirmwareVersion"},
131 {1002, nullptr, "GetAvailableFirmwareVersion"}, 131 {1002, nullptr, "GetAvailableFirmwareVersion"},
132 {1003, nullptr, "IsFirmwareUpdateAvailable"}, 132 {1003, nullptr, "IsFirmwareUpdateAvailable"},
133 {1004, nullptr, "CheckFirmwareUpdateRequired"}, 133 {1004, nullptr, "CheckFirmwareUpdateRequired"},
134 {1005, nullptr, "StartFirmwareUpdate"}, 134 {1005, nullptr, "StartFirmwareUpdate"},
135 {1006, nullptr, "AbortFirmwareUpdate"}, 135 {1006, nullptr, "AbortFirmwareUpdate"},
136 {1007, nullptr, "GetFirmwareUpdateState"}, 136 {1007, nullptr, "GetFirmwareUpdateState"},
137 {1008, nullptr, "ActivateAudioControl"}, 137 {1008, nullptr, "ActivateAudioControl"},
138 {1009, nullptr, "AcquireAudioControlEventHandle"}, 138 {1009, nullptr, "AcquireAudioControlEventHandle"},
139 {1010, nullptr, "GetAudioControlStates"}, 139 {1010, nullptr, "GetAudioControlStates"},
140 {1011, nullptr, "DeactivateAudioControl"}, 140 {1011, nullptr, "DeactivateAudioControl"},
141 {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"}, 141 {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
142 {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"}, 142 {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
143 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, 143 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
144 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, 144 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
145 {1100, nullptr, "GetHidbusSystemServiceObject"}, 145 {1100, nullptr, "GetHidbusSystemServiceObject"},
146 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, 146 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
147 {1130, nullptr, "InitializeUsbFirmwareUpdate"}, 147 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
148 {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, 148 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
149 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, 149 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
150 {1133, nullptr, "StartUsbFirmwareUpdate"}, 150 {1133, nullptr, "StartUsbFirmwareUpdate"},
151 {1134, nullptr, "GetUsbFirmwareUpdateState"}, 151 {1134, nullptr, "GetUsbFirmwareUpdateState"},
152 {1150, nullptr, "SetTouchScreenMagnification"}, 152 {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
153 {1151, nullptr, "GetTouchScreenFirmwareVersion"}, 153 {1150, nullptr, "SetTouchScreenMagnification"},
154 {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, 154 {1151, nullptr, "GetTouchScreenFirmwareVersion"},
155 {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, 155 {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
156 {1154, nullptr, "IsFirmwareAvailableForNotification"}, 156 {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
157 {1155, nullptr, "SetForceHandheldStyleVibration"}, 157 {1154, nullptr, "IsFirmwareAvailableForNotification"},
158 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, 158 {1155, nullptr, "SetForceHandheldStyleVibration"},
159 {1157, nullptr, "CancelConnectionTrigger"}, 159 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
160 {1200, nullptr, "IsButtonConfigSupported"}, 160 {1157, nullptr, "CancelConnectionTrigger"},
161 {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, 161 {1200, nullptr, "IsButtonConfigSupported"},
162 {1202, nullptr, "DeleteButtonConfig"}, 162 {1201, nullptr, "IsButtonConfigEmbeddedSupported"},
163 {1203, nullptr, "DeleteButtonConfigEmbedded"}, 163 {1202, nullptr, "DeleteButtonConfig"},
164 {1204, nullptr, "SetButtonConfigEnabled"}, 164 {1203, nullptr, "DeleteButtonConfigEmbedded"},
165 {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, 165 {1204, nullptr, "SetButtonConfigEnabled"},
166 {1206, nullptr, "IsButtonConfigEnabled"}, 166 {1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
167 {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, 167 {1206, nullptr, "IsButtonConfigEnabled"},
168 {1208, nullptr, "SetButtonConfigEmbedded"}, 168 {1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
169 {1209, nullptr, "SetButtonConfigFull"}, 169 {1208, nullptr, "SetButtonConfigEmbedded"},
170 {1210, nullptr, "SetButtonConfigLeft"}, 170 {1209, nullptr, "SetButtonConfigFull"},
171 {1211, nullptr, "SetButtonConfigRight"}, 171 {1210, nullptr, "SetButtonConfigLeft"},
172 {1212, nullptr, "GetButtonConfigEmbedded"}, 172 {1211, nullptr, "SetButtonConfigRight"},
173 {1213, nullptr, "GetButtonConfigFull"}, 173 {1212, nullptr, "GetButtonConfigEmbedded"},
174 {1214, nullptr, "GetButtonConfigLeft"}, 174 {1213, nullptr, "GetButtonConfigFull"},
175 {1215, nullptr, "GetButtonConfigRight"}, 175 {1214, nullptr, "GetButtonConfigLeft"},
176 {1250, nullptr, "IsCustomButtonConfigSupported"}, 176 {1215, nullptr, "GetButtonConfigRight"},
177 {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, 177 {1250, nullptr, "IsCustomButtonConfigSupported"},
178 {1252, nullptr, "IsDefaultButtonConfigFull"}, 178 {1251, nullptr, "IsDefaultButtonConfigEmbedded"},
179 {1253, nullptr, "IsDefaultButtonConfigLeft"}, 179 {1252, nullptr, "IsDefaultButtonConfigFull"},
180 {1254, nullptr, "IsDefaultButtonConfigRight"}, 180 {1253, nullptr, "IsDefaultButtonConfigLeft"},
181 {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, 181 {1254, nullptr, "IsDefaultButtonConfigRight"},
182 {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, 182 {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
183 {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, 183 {1256, nullptr, "IsButtonConfigStorageFullEmpty"},
184 {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, 184 {1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
185 {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, 185 {1258, nullptr, "IsButtonConfigStorageRightEmpty"},
186 {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, 186 {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
187 {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, 187 {1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
188 {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, 188 {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
189 {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, 189 {1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
190 {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, 190 {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
191 {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, 191 {1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
192 {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, 192 {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
193 {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, 193 {1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
194 {1268, nullptr, "DeleteButtonConfigStorageFull"}, 194 {1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
195 {1269, nullptr, "DeleteButtonConfigStorageLeft"}, 195 {1268, nullptr, "DeleteButtonConfigStorageFull"},
196 {1270, nullptr, "DeleteButtonConfigStorageRight"}, 196 {1269, nullptr, "DeleteButtonConfigStorageLeft"},
197 {1271, nullptr, "IsUsingCustomButtonConfig"}, 197 {1270, nullptr, "DeleteButtonConfigStorageRight"},
198 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, 198 {1271, nullptr, "IsUsingCustomButtonConfig"},
199 {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, 199 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
200 {1274, nullptr, "SetDefaultButtonConfig"}, 200 {1273, nullptr, "SetAllCustomButtonConfigEnabled"},
201 {1275, nullptr, "SetAllDefaultButtonConfig"}, 201 {1274, nullptr, "SetDefaultButtonConfig"},
202 {1276, nullptr, "SetHidButtonConfigEmbedded"}, 202 {1275, nullptr, "SetAllDefaultButtonConfig"},
203 {1277, nullptr, "SetHidButtonConfigFull"}, 203 {1276, nullptr, "SetHidButtonConfigEmbedded"},
204 {1278, nullptr, "SetHidButtonConfigLeft"}, 204 {1277, nullptr, "SetHidButtonConfigFull"},
205 {1279, nullptr, "SetHidButtonConfigRight"}, 205 {1278, nullptr, "SetHidButtonConfigLeft"},
206 {1280, nullptr, "GetHidButtonConfigEmbedded"}, 206 {1279, nullptr, "SetHidButtonConfigRight"},
207 {1281, nullptr, "GetHidButtonConfigFull"}, 207 {1280, nullptr, "GetHidButtonConfigEmbedded"},
208 {1282, nullptr, "GetHidButtonConfigLeft"}, 208 {1281, nullptr, "GetHidButtonConfigFull"},
209 {1283, nullptr, "GetHidButtonConfigRight"}, 209 {1282, nullptr, "GetHidButtonConfigLeft"},
210 {1284, nullptr, "GetButtonConfigStorageEmbedded"}, 210 {1283, nullptr, "GetHidButtonConfigRight"},
211 {1285, nullptr, "GetButtonConfigStorageFull"}, 211 {1284, nullptr, "GetButtonConfigStorageEmbedded"},
212 {1286, nullptr, "GetButtonConfigStorageLeft"}, 212 {1285, nullptr, "GetButtonConfigStorageFull"},
213 {1287, nullptr, "GetButtonConfigStorageRight"}, 213 {1286, nullptr, "GetButtonConfigStorageLeft"},
214 {1288, nullptr, "SetButtonConfigStorageEmbedded"}, 214 {1287, nullptr, "GetButtonConfigStorageRight"},
215 {1289, nullptr, "SetButtonConfigStorageFull"}, 215 {1288, nullptr, "SetButtonConfigStorageEmbedded"},
216 {1290, nullptr, "DeleteButtonConfigStorageRight"}, 216 {1289, nullptr, "SetButtonConfigStorageFull"},
217 {1291, nullptr, "DeleteButtonConfigStorageRight"}, 217 {1290, nullptr, "DeleteButtonConfigStorageRight"},
218 {1291, nullptr, "DeleteButtonConfigStorageRight"},
218 }; 219 };
219 // clang-format on 220 // clang-format on
220 221
221 RegisterHandlers(functions); 222 RegisterHandlers(functions);
222 223
223 joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent"); 224 joy_detach_event = service_context.CreateEvent("IHidSystemServer::JoyDetachEvent");
225 acquire_device_registered_event =
226 service_context.CreateEvent("IHidSystemServer::AcquireDeviceRegisteredEvent");
227 acquire_connection_trigger_timeout_event =
228 service_context.CreateEvent("IHidSystemServer::AcquireConnectionTriggerTimeoutEvent");
229 unique_pad_connection_event =
230 service_context.CreateEvent("IHidSystemServer::AcquireUniquePadConnectionEventHandle");
224} 231}
225 232
226IHidSystemServer::~IHidSystemServer() { 233IHidSystemServer::~IHidSystemServer() {
227 service_context.CloseEvent(joy_detach_event); 234 service_context.CloseEvent(joy_detach_event);
235 service_context.CloseEvent(acquire_device_registered_event);
236 service_context.CloseEvent(acquire_connection_trigger_timeout_event);
237 service_context.CloseEvent(unique_pad_connection_event);
228}; 238};
229 239
230void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { 240void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
231 LOG_WARNING(Service_HID, "called"); 241 LOG_WARNING(Service_HID, "called");
232 242
233 GetResourceManager() 243 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy();
234 ->GetController<Controller_NPad>(HidController::NPad) 244
235 .ApplyNpadSystemCommonPolicy(); 245 IPC::ResponseBuilder rb{ctx, 2};
246 rb.Push(ResultSuccess);
247}
248
249void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
250 LOG_WARNING(Service_HID, "(STUBBED) called");
251
252 IPC::ResponseBuilder rb{ctx, 2};
253 rb.Push(ResultSuccess);
254}
255
256void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
257 LOG_WARNING(Service_HID, "(STUBBED) called");
236 258
237 IPC::ResponseBuilder rb{ctx, 2}; 259 IPC::ResponseBuilder rb{ctx, 2};
238 rb.Push(ResultSuccess); 260 rb.Push(ResultSuccess);
239} 261}
240 262
241void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) { 263void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
242 LOG_DEBUG(Service_HID, "(STUBBED) called"); 264 LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running
243 265
244 IPC::ResponseBuilder rb{ctx, 3}; 266 IPC::ResponseBuilder rb{ctx, 3};
245 rb.Push(ResultSuccess); 267 rb.Push(ResultSuccess);
246 rb.PushEnum(system.HIDCore().GetLastActiveController()); 268 rb.PushEnum(system.HIDCore().GetLastActiveController());
247} 269}
248 270
271void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) {
272 LOG_WARNING(Service_HID, "called");
273
274 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy();
275
276 IPC::ResponseBuilder rb{ctx, 2};
277 rb.Push(ResultSuccess);
278}
279
280void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) {
281 IPC::RequestParser rp{ctx};
282 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
283
284 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
285 npad_id_type); // Spams a lot when controller applet is running
286
287 Core::HID::NpadColor left_color{};
288 Core::HID::NpadColor right_color{};
289 // TODO: Get colors from Npad
290
291 IPC::ResponseBuilder rb{ctx, 4};
292 rb.Push(ResultSuccess);
293 rb.PushRaw(left_color);
294 rb.PushRaw(right_color);
295}
296
297void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) {
298 IPC::RequestParser rp{ctx};
299
300 LOG_INFO(Service_HID, "(STUBBED) called");
301
302 Core::HID::NpadStyleSet supported_styleset =
303 GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw;
304
305 IPC::ResponseBuilder rb{ctx, 3};
306 rb.Push(ResultSuccess);
307 rb.PushEnum(supported_styleset);
308}
309
310void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) {
311 IPC::RequestParser rp{ctx};
312
313 LOG_INFO(Service_HID, "(STUBBED) called");
314
315 Core::HID::NpadStyleSet supported_styleset =
316 GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw;
317
318 IPC::ResponseBuilder rb{ctx, 3};
319 rb.Push(ResultSuccess);
320 rb.PushEnum(supported_styleset);
321}
322
323void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
324 IPC::RequestParser rp{ctx};
325 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
326
327 LOG_DEBUG(Service_HID, "called, npad_id_type={}",
328 npad_id_type); // Spams a lot when controller applet is running
329
330 const NPad::AppletDetailedUiType detailed_ui_type =
331 GetResourceManager()->GetNpad()->GetAppletDetailedUiType(npad_id_type);
332
333 IPC::ResponseBuilder rb{ctx, 3};
334 rb.Push(ResultSuccess);
335 rb.PushRaw(detailed_ui_type);
336}
337
338void IHidSystemServer::GetNpadInterfaceType(HLERequestContext& ctx) {
339 IPC::RequestParser rp{ctx};
340 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
341
342 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
343 npad_id_type); // Spams a lot when controller applet is running
344
345 IPC::ResponseBuilder rb{ctx, 3};
346 rb.Push(ResultSuccess);
347 rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
348}
349
350void IHidSystemServer::GetNpadLeftRightInterfaceType(HLERequestContext& ctx) {
351 IPC::RequestParser rp{ctx};
352 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
353
354 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
355 npad_id_type); // Spams a lot when controller applet is running
356
357 IPC::ResponseBuilder rb{ctx, 4};
358 rb.Push(ResultSuccess);
359 rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
360 rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
361}
362
363void IHidSystemServer::HasBattery(HLERequestContext& ctx) {
364 IPC::RequestParser rp{ctx};
365 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
366
367 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
368 npad_id_type); // Spams a lot when controller applet is running
369
370 IPC::ResponseBuilder rb{ctx, 3};
371 rb.Push(ResultSuccess);
372 rb.Push(false);
373}
374
375void IHidSystemServer::HasLeftRightBattery(HLERequestContext& ctx) {
376 IPC::RequestParser rp{ctx};
377 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
378
379 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
380 npad_id_type); // Spams a lot when controller applet is running
381
382 struct LeftRightBattery {
383 bool left;
384 bool right;
385 };
386
387 LeftRightBattery left_right_battery{
388 .left = false,
389 .right = false,
390 };
391
392 IPC::ResponseBuilder rb{ctx, 3};
393 rb.Push(ResultSuccess);
394 rb.PushRaw(left_right_battery);
395}
396
249void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) { 397void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) {
250 IPC::RequestParser rp{ctx}; 398 IPC::RequestParser rp{ctx};
251 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; 399 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
252 400
253 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); 401 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
402 npad_id_type); // Spams a lot when controller applet is running
254 403
255 const std::vector<Core::HID::UniquePadId> unique_pads{}; 404 const std::vector<Core::HID::UniquePadId> unique_pads{};
256 405
257 ctx.WriteBuffer(unique_pads); 406 if (!unique_pads.empty()) {
407 ctx.WriteBuffer(unique_pads);
408 }
258 409
259 IPC::ResponseBuilder rb{ctx, 3}; 410 IPC::ResponseBuilder rb{ctx, 3};
260 rb.Push(ResultSuccess); 411 rb.Push(ResultSuccess);
261 rb.Push(static_cast<u32>(unique_pads.size())); 412 rb.Push(static_cast<u32>(unique_pads.size()));
262} 413}
263 414
415void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) {
416 IPC::RequestParser rp{ctx};
417
418 LOG_WARNING(Service_HID, "(STUBBED) called");
419
420 IPC::ResponseBuilder rb{ctx, 2};
421 rb.Push(ResultSuccess);
422}
423
424void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) {
425 LOG_INFO(Service_AM, "(STUBBED) called");
426
427 IPC::ResponseBuilder rb{ctx, 2, 1};
428 rb.Push(ResultSuccess);
429 rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
430}
431
432void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
433 LOG_INFO(Service_HID, "(STUBBED) called");
434
435 IPC::ResponseBuilder rb{ctx, 2, 1};
436 rb.Push(ResultSuccess);
437 rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
438}
439
440void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
441 LOG_WARNING(Service_HID, "(STUBBED) called");
442
443 struct RegisterData {
444 std::array<u8, 0x68> data;
445 };
446 static_assert(sizeof(RegisterData) == 0x68, "RegisterData is an invalid size");
447 std::vector<RegisterData> registered_devices{};
448
449 if (!registered_devices.empty()) {
450 ctx.WriteBuffer(registered_devices);
451 }
452
453 IPC::ResponseBuilder rb{ctx, 4};
454 rb.Push(ResultSuccess);
455 rb.Push<u64>(registered_devices.size());
456}
457
458void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx) {
459 LOG_WARNING(Service_HID, "(STUBBED) called");
460
461 IPC::ResponseBuilder rb{ctx, 2, 1};
462 rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
463 rb.Push(ResultSuccess);
464}
465
466void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) {
467 LOG_WARNING(Service_HID, "(STUBBED) called");
468
469 IPC::ResponseBuilder rb{ctx, 4};
470 rb.Push(ResultSuccess);
471 rb.Push<u64>(0);
472}
473
264void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { 474void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
265 LOG_INFO(Service_AM, "called"); 475 LOG_INFO(Service_AM, "called");
266 476
@@ -279,6 +489,31 @@ void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
279 rb.Push(is_enabled); 489 rb.Push(is_enabled);
280} 490}
281 491
492void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) {
493 const bool button_pressed = false;
494
495 LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}",
496 button_pressed); // Spams a lot when controller applet is open
497
498 IPC::ResponseBuilder rb{ctx, 3};
499 rb.Push(ResultSuccess);
500 rb.Push(button_pressed);
501}
502
503void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) {
504 LOG_WARNING(Service_HID, "(STUBBED) called");
505
506 IPC::ResponseBuilder rb{ctx, 2};
507 rb.Push(ResultSuccess);
508}
509
510void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) {
511 LOG_WARNING(Service_HID, "(STUBBED) called");
512
513 IPC::ResponseBuilder rb{ctx, 2};
514 rb.Push(ResultSuccess);
515}
516
282void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { 517void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
283 LOG_WARNING(Service_HID, "(STUBBED) called"); 518 LOG_WARNING(Service_HID, "(STUBBED) called");
284 519
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
index d4b3910fa..822d5e5b9 100644
--- a/src/core/hle/service/hid/hid_system_server.h
+++ b/src/core/hle/service/hid/hid_system_server.h
@@ -24,15 +24,38 @@ public:
24 24
25private: 25private:
26 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); 26 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
27 void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
28 void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
27 void GetLastActiveNpad(HLERequestContext& ctx); 29 void GetLastActiveNpad(HLERequestContext& ctx);
30 void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx);
31 void GetNpadFullKeyGripColor(HLERequestContext& ctx);
32 void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx);
33 void SetSupportedNpadStyleSetAll(HLERequestContext& ctx);
34 void GetAppletDetailedUiType(HLERequestContext& ctx);
35 void GetNpadInterfaceType(HLERequestContext& ctx);
36 void GetNpadLeftRightInterfaceType(HLERequestContext& ctx);
37 void HasBattery(HLERequestContext& ctx);
38 void HasLeftRightBattery(HLERequestContext& ctx);
28 void GetUniquePadsFromNpad(HLERequestContext& ctx); 39 void GetUniquePadsFromNpad(HLERequestContext& ctx);
40 void GetIrSensorState(HLERequestContext& ctx);
41 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
42 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
43 void GetRegisteredDevices(HLERequestContext& ctx);
44 void AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx);
45 void GetUniquePadIds(HLERequestContext& ctx);
29 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); 46 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
30 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); 47 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
48 void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
49 void InitializeFirmwareUpdate(HLERequestContext& ctx);
50 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
31 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); 51 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
32 52
33 std::shared_ptr<ResourceManager> GetResourceManager(); 53 std::shared_ptr<ResourceManager> GetResourceManager();
34 54
55 Kernel::KEvent* acquire_connection_trigger_timeout_event;
56 Kernel::KEvent* acquire_device_registered_event;
35 Kernel::KEvent* joy_detach_event; 57 Kernel::KEvent* joy_detach_event;
58 Kernel::KEvent* unique_pad_connection_event;
36 KernelHelpers::ServiceContext service_context; 59 KernelHelpers::ServiceContext service_context;
37 std::shared_ptr<ResourceManager> resource_manager; 60 std::shared_ptr<ResourceManager> resource_manager;
38}; 61};
diff --git a/src/core/hle/service/hid/hid_util.h b/src/core/hle/service/hid/hid_util.h
new file mode 100644
index 000000000..b87cc10e3
--- /dev/null
+++ b/src/core/hle/service/hid/hid_util.h
@@ -0,0 +1,146 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "core/hid/hid_types.h"
7#include "core/hle/service/hid/errors.h"
8
9namespace Service::HID {
10
11constexpr bool IsNpadIdValid(const Core::HID::NpadIdType npad_id) {
12 switch (npad_id) {
13 case Core::HID::NpadIdType::Player1:
14 case Core::HID::NpadIdType::Player2:
15 case Core::HID::NpadIdType::Player3:
16 case Core::HID::NpadIdType::Player4:
17 case Core::HID::NpadIdType::Player5:
18 case Core::HID::NpadIdType::Player6:
19 case Core::HID::NpadIdType::Player7:
20 case Core::HID::NpadIdType::Player8:
21 case Core::HID::NpadIdType::Other:
22 case Core::HID::NpadIdType::Handheld:
23 return true;
24 default:
25 return false;
26 }
27}
28
29constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& handle) {
30 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id));
31 const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
32
33 if (!npad_id) {
34 return InvalidNpadId;
35 }
36 if (!device_index) {
37 return NpadDeviceIndexOutOfRange;
38 }
39
40 return ResultSuccess;
41}
42
43constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) {
44 switch (handle.npad_type) {
45 case Core::HID::NpadStyleIndex::ProController:
46 case Core::HID::NpadStyleIndex::Handheld:
47 case Core::HID::NpadStyleIndex::JoyconDual:
48 case Core::HID::NpadStyleIndex::JoyconLeft:
49 case Core::HID::NpadStyleIndex::JoyconRight:
50 case Core::HID::NpadStyleIndex::GameCube:
51 case Core::HID::NpadStyleIndex::N64:
52 case Core::HID::NpadStyleIndex::SystemExt:
53 case Core::HID::NpadStyleIndex::System:
54 // These support vibration
55 break;
56 default:
57 return VibrationInvalidStyleIndex;
58 }
59
60 if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) {
61 return VibrationInvalidNpadId;
62 }
63
64 if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) {
65 return VibrationDeviceIndexOutOfRange;
66 }
67
68 return ResultSuccess;
69}
70
71/// Converts a Core::HID::NpadIdType to an array index.
72constexpr size_t NpadIdTypeToIndex(Core::HID::NpadIdType npad_id_type) {
73 switch (npad_id_type) {
74 case Core::HID::NpadIdType::Player1:
75 return 0;
76 case Core::HID::NpadIdType::Player2:
77 return 1;
78 case Core::HID::NpadIdType::Player3:
79 return 2;
80 case Core::HID::NpadIdType::Player4:
81 return 3;
82 case Core::HID::NpadIdType::Player5:
83 return 4;
84 case Core::HID::NpadIdType::Player6:
85 return 5;
86 case Core::HID::NpadIdType::Player7:
87 return 6;
88 case Core::HID::NpadIdType::Player8:
89 return 7;
90 case Core::HID::NpadIdType::Handheld:
91 return 8;
92 case Core::HID::NpadIdType::Other:
93 return 9;
94 default:
95 return 8;
96 }
97}
98
99/// Converts an array index to a Core::HID::NpadIdType
100constexpr Core::HID::NpadIdType IndexToNpadIdType(size_t index) {
101 switch (index) {
102 case 0:
103 return Core::HID::NpadIdType::Player1;
104 case 1:
105 return Core::HID::NpadIdType::Player2;
106 case 2:
107 return Core::HID::NpadIdType::Player3;
108 case 3:
109 return Core::HID::NpadIdType::Player4;
110 case 4:
111 return Core::HID::NpadIdType::Player5;
112 case 5:
113 return Core::HID::NpadIdType::Player6;
114 case 6:
115 return Core::HID::NpadIdType::Player7;
116 case 7:
117 return Core::HID::NpadIdType::Player8;
118 case 8:
119 return Core::HID::NpadIdType::Handheld;
120 case 9:
121 return Core::HID::NpadIdType::Other;
122 default:
123 return Core::HID::NpadIdType::Invalid;
124 }
125}
126
127constexpr Core::HID::NpadStyleSet GetStylesetByIndex(std::size_t index) {
128 switch (index) {
129 case 0:
130 return Core::HID::NpadStyleSet::Fullkey;
131 case 1:
132 return Core::HID::NpadStyleSet::Handheld;
133 case 2:
134 return Core::HID::NpadStyleSet::JoyDual;
135 case 3:
136 return Core::HID::NpadStyleSet::JoyLeft;
137 case 4:
138 return Core::HID::NpadStyleSet::JoyRight;
139 case 5:
140 return Core::HID::NpadStyleSet::Palma;
141 default:
142 return Core::HID::NpadStyleSet::None;
143 }
144}
145
146} // namespace Service::HID
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index d383a266d..39b9a4474 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -12,6 +12,7 @@
12#include "core/hle/kernel/k_transfer_memory.h" 12#include "core/hle/kernel/k_transfer_memory.h"
13#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
14#include "core/hle/service/hid/errors.h" 14#include "core/hle/service/hid/errors.h"
15#include "core/hle/service/hid/hid_util.h"
15#include "core/hle/service/hid/irs.h" 16#include "core/hle/service/hid/irs.h"
16#include "core/hle/service/hid/irsensor/clustering_processor.h" 17#include "core/hle/service/hid/irsensor/clustering_processor.h"
17#include "core/hle/service/hid/irsensor/image_transfer_processor.h" 18#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
@@ -320,7 +321,7 @@ void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) {
320 } 321 }
321 322
322 Core::IrSensor::IrCameraHandle camera_handle{ 323 Core::IrSensor::IrCameraHandle camera_handle{
323 .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), 324 .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)),
324 .npad_type = Core::HID::NpadStyleIndex::None, 325 .npad_type = Core::HID::NpadStyleIndex::None,
325 }; 326 };
326 327
@@ -545,7 +546,7 @@ void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) {
545 546
546Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { 547Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
547 if (camera_handle.npad_id > 548 if (camera_handle.npad_id >
548 static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { 549 static_cast<u8>(HID::NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
549 return InvalidIrCameraHandle; 550 return InvalidIrCameraHandle;
550 } 551 }
551 if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { 552 if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
index d6f42c646..e76d4eea9 100644
--- a/src/core/hle/service/hid/resource_manager.cpp
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -9,14 +9,15 @@
9#include "core/hle/service/hid/resource_manager.h" 9#include "core/hle/service/hid/resource_manager.h"
10#include "core/hle/service/ipc_helpers.h" 10#include "core/hle/service/ipc_helpers.h"
11 11
12#include "core/hle/service/hid/controllers/console_sixaxis.h" 12#include "core/hle/service/hid/controllers/console_six_axis.h"
13#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/hle/service/hid/controllers/debug_pad.h" 13#include "core/hle/service/hid/controllers/debug_pad.h"
15#include "core/hle/service/hid/controllers/gesture.h" 14#include "core/hle/service/hid/controllers/gesture.h"
16#include "core/hle/service/hid/controllers/keyboard.h" 15#include "core/hle/service/hid/controllers/keyboard.h"
17#include "core/hle/service/hid/controllers/mouse.h" 16#include "core/hle/service/hid/controllers/mouse.h"
18#include "core/hle/service/hid/controllers/npad.h" 17#include "core/hle/service/hid/controllers/npad.h"
19#include "core/hle/service/hid/controllers/palma.h" 18#include "core/hle/service/hid/controllers/palma.h"
19#include "core/hle/service/hid/controllers/seven_six_axis.h"
20#include "core/hle/service/hid/controllers/six_axis.h"
20#include "core/hle/service/hid/controllers/stubbed.h" 21#include "core/hle/service/hid/controllers/stubbed.h"
21#include "core/hle/service/hid/controllers/touchscreen.h" 22#include "core/hle/service/hid/controllers/touchscreen.h"
22#include "core/hle/service/hid/controllers/xpad.h" 23#include "core/hle/service/hid/controllers/xpad.h"
@@ -42,76 +43,132 @@ void ResourceManager::Initialize() {
42 } 43 }
43 44
44 u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); 45 u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
45 MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory); 46 debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory);
46 MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory); 47 mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory);
47 MakeController<Controller_Mouse>(HidController::Mouse, shared_memory); 48 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory);
48 MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory); 49 keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory);
49 MakeController<Controller_XPad>(HidController::XPad, shared_memory); 50 unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory);
50 MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory); 51 npad = std::make_shared<NPad>(system.HIDCore(), shared_memory, service_context);
51 MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory); 52 gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory);
52 MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory); 53 touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory);
53 MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory); 54 xpad = std::make_shared<XPad>(system.HIDCore(), shared_memory);
54 MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory); 55
55 MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); 56 palma = std::make_shared<Palma>(system.HIDCore(), shared_memory, service_context);
56 MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); 57
57 MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory); 58 home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory);
58 MakeController<Controller_Stubbed>(HidController::DebugMouse, shared_memory); 59 sleep_button = std::make_shared<SleepButton>(system.HIDCore(), shared_memory);
59 MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory); 60 capture_button = std::make_shared<CaptureButton>(system.HIDCore(), shared_memory);
61
62 six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
63 console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory);
64 seven_six_axis = std::make_shared<SevenSixAxis>(system);
65
66 home_button->SetCommonHeaderOffset(0x4C00);
67 sleep_button->SetCommonHeaderOffset(0x4E00);
68 capture_button->SetCommonHeaderOffset(0x5000);
69 unique_pad->SetCommonHeaderOffset(0x5A00);
70 debug_mouse->SetCommonHeaderOffset(0x3DC00);
60 71
61 // Homebrew doesn't try to activate some controllers, so we activate them by default 72 // Homebrew doesn't try to activate some controllers, so we activate them by default
62 GetController<Controller_NPad>(HidController::NPad).Activate(); 73 npad->Activate();
63 GetController<Controller_Touchscreen>(HidController::Touchscreen).Activate(); 74 six_axis->Activate();
64 75 touch_screen->Activate();
65 GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
66 GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
67 GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000);
68 GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200);
69 GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
70 GetController<Controller_Stubbed>(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00);
71 76
72 system.HIDCore().ReloadInputDevices(); 77 system.HIDCore().ReloadInputDevices();
73 is_initialized = true; 78 is_initialized = true;
74} 79}
80std::shared_ptr<CaptureButton> ResourceManager::GetCaptureButton() const {
81 return capture_button;
82}
83
84std::shared_ptr<ConsoleSixAxis> ResourceManager::GetConsoleSixAxis() const {
85 return console_six_axis;
86}
87
88std::shared_ptr<DebugMouse> ResourceManager::GetDebugMouse() const {
89 return debug_mouse;
90}
91
92std::shared_ptr<DebugPad> ResourceManager::GetDebugPad() const {
93 return debug_pad;
94}
95
96std::shared_ptr<Gesture> ResourceManager::GetGesture() const {
97 return gesture;
98}
99
100std::shared_ptr<HomeButton> ResourceManager::GetHomeButton() const {
101 return home_button;
102}
103
104std::shared_ptr<Keyboard> ResourceManager::GetKeyboard() const {
105 return keyboard;
106}
107
108std::shared_ptr<Mouse> ResourceManager::GetMouse() const {
109 return mouse;
110}
111
112std::shared_ptr<NPad> ResourceManager::GetNpad() const {
113 return npad;
114}
115
116std::shared_ptr<Palma> ResourceManager::GetPalma() const {
117 return palma;
118}
119
120std::shared_ptr<SevenSixAxis> ResourceManager::GetSevenSixAxis() const {
121 return seven_six_axis;
122}
123
124std::shared_ptr<SixAxis> ResourceManager::GetSixAxis() const {
125 return six_axis;
126}
127
128std::shared_ptr<SleepButton> ResourceManager::GetSleepButton() const {
129 return sleep_button;
130}
131
132std::shared_ptr<TouchScreen> ResourceManager::GetTouchScreen() const {
133 return touch_screen;
134}
135
136std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const {
137 return unique_pad;
138}
75 139
76void ResourceManager::UpdateControllers(std::uintptr_t user_data, 140void ResourceManager::UpdateControllers(std::uintptr_t user_data,
77 std::chrono::nanoseconds ns_late) { 141 std::chrono::nanoseconds ns_late) {
78 auto& core_timing = system.CoreTiming(); 142 auto& core_timing = system.CoreTiming();
79 143 debug_pad->OnUpdate(core_timing);
80 for (const auto& controller : controllers) { 144 unique_pad->OnUpdate(core_timing);
81 // Keyboard has it's own update event 145 gesture->OnUpdate(core_timing);
82 if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) { 146 touch_screen->OnUpdate(core_timing);
83 continue; 147 palma->OnUpdate(core_timing);
84 } 148 home_button->OnUpdate(core_timing);
85 // Mouse has it's own update event 149 sleep_button->OnUpdate(core_timing);
86 if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) { 150 capture_button->OnUpdate(core_timing);
87 continue; 151 xpad->OnUpdate(core_timing);
88 }
89 // Npad has it's own update event
90 if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
91 continue;
92 }
93 controller->OnUpdate(core_timing);
94 }
95} 152}
96 153
97void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 154void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
98 auto& core_timing = system.CoreTiming(); 155 auto& core_timing = system.CoreTiming();
99 156 npad->OnUpdate(core_timing);
100 controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
101} 157}
102 158
103void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, 159void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data,
104 std::chrono::nanoseconds ns_late) { 160 std::chrono::nanoseconds ns_late) {
105 auto& core_timing = system.CoreTiming(); 161 auto& core_timing = system.CoreTiming();
106 162 mouse->OnUpdate(core_timing);
107 controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); 163 debug_mouse->OnUpdate(core_timing);
108 controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing); 164 keyboard->OnUpdate(core_timing);
109} 165}
110 166
111void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 167void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
112 auto& core_timing = system.CoreTiming(); 168 auto& core_timing = system.CoreTiming();
113 169 six_axis->OnUpdate(core_timing);
114 controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing); 170 seven_six_axis->OnUpdate(core_timing);
171 console_six_axis->OnUpdate(core_timing);
115} 172}
116 173
117IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource) 174IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
index 34dbf36bc..2b6a9b5e6 100644
--- a/src/core/hle/service/hid/resource_manager.h
+++ b/src/core/hle/service/hid/resource_manager.h
@@ -3,10 +3,6 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <chrono>
7
8#include "core/core.h"
9#include "core/hle/service/hid/controllers/controller_base.h"
10#include "core/hle/service/kernel_helpers.h" 6#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
12 8
@@ -14,74 +10,85 @@ namespace Core::Timing {
14struct EventType; 10struct EventType;
15} 11}
16 12
17namespace Core::HID {
18class HIDCore;
19}
20
21namespace Service::HID { 13namespace Service::HID {
14class Controller_Stubbed;
15class ConsoleSixAxis;
16class DebugPad;
17class Gesture;
18class Keyboard;
19class Mouse;
20class NPad;
21class Palma;
22class SevenSixAxis;
23class SixAxis;
24class TouchScreen;
25class XPad;
26
27using CaptureButton = Controller_Stubbed;
28using DebugMouse = Controller_Stubbed;
29using HomeButton = Controller_Stubbed;
30using SleepButton = Controller_Stubbed;
31using UniquePad = Controller_Stubbed;
22 32
23enum class HidController : std::size_t {
24 DebugPad,
25 Touchscreen,
26 Mouse,
27 Keyboard,
28 XPad,
29 HomeButton,
30 SleepButton,
31 CaptureButton,
32 InputDetector,
33 UniquePad,
34 NPad,
35 Gesture,
36 ConsoleSixAxisSensor,
37 DebugMouse,
38 Palma,
39
40 MaxControllers,
41};
42class ResourceManager { 33class ResourceManager {
34
43public: 35public:
44 explicit ResourceManager(Core::System& system_); 36 explicit ResourceManager(Core::System& system_);
45 ~ResourceManager(); 37 ~ResourceManager();
46 38
47 template <typename T>
48 T& GetController(HidController controller) {
49 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
50 }
51
52 template <typename T>
53 const T& GetController(HidController controller) const {
54 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
55 }
56
57 void Initialize(); 39 void Initialize();
58 40
41 std::shared_ptr<CaptureButton> GetCaptureButton() const;
42 std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const;
43 std::shared_ptr<DebugMouse> GetDebugMouse() const;
44 std::shared_ptr<DebugPad> GetDebugPad() const;
45 std::shared_ptr<Gesture> GetGesture() const;
46 std::shared_ptr<HomeButton> GetHomeButton() const;
47 std::shared_ptr<Keyboard> GetKeyboard() const;
48 std::shared_ptr<Mouse> GetMouse() const;
49 std::shared_ptr<NPad> GetNpad() const;
50 std::shared_ptr<Palma> GetPalma() const;
51 std::shared_ptr<SevenSixAxis> GetSevenSixAxis() const;
52 std::shared_ptr<SixAxis> GetSixAxis() const;
53 std::shared_ptr<SleepButton> GetSleepButton() const;
54 std::shared_ptr<TouchScreen> GetTouchScreen() const;
55 std::shared_ptr<UniquePad> GetUniquePad() const;
56
59 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 57 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
60 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 58 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
61 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 59 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
62 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 60 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
63 61
64private: 62private:
65 template <typename T>
66 void MakeController(HidController controller, u8* shared_memory) {
67 if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
68 controllers[static_cast<std::size_t>(controller)] =
69 std::make_unique<T>(system, shared_memory);
70 } else {
71 controllers[static_cast<std::size_t>(controller)] =
72 std::make_unique<T>(system.HIDCore(), shared_memory);
73 }
74 }
75
76 template <typename T>
77 void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
78 controllers[static_cast<std::size_t>(controller)] =
79 std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
80 }
81
82 bool is_initialized{false}; 63 bool is_initialized{false};
83 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> 64
84 controllers{}; 65 std::shared_ptr<CaptureButton> capture_button = nullptr;
66 std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr;
67 std::shared_ptr<DebugMouse> debug_mouse = nullptr;
68 std::shared_ptr<DebugPad> debug_pad = nullptr;
69 std::shared_ptr<Gesture> gesture = nullptr;
70 std::shared_ptr<HomeButton> home_button = nullptr;
71 std::shared_ptr<Keyboard> keyboard = nullptr;
72 std::shared_ptr<Mouse> mouse = nullptr;
73 std::shared_ptr<NPad> npad = nullptr;
74 std::shared_ptr<Palma> palma = nullptr;
75 std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr;
76 std::shared_ptr<SixAxis> six_axis = nullptr;
77 std::shared_ptr<SleepButton> sleep_button = nullptr;
78 std::shared_ptr<TouchScreen> touch_screen = nullptr;
79 std::shared_ptr<UniquePad> unique_pad = nullptr;
80 std::shared_ptr<XPad> xpad = nullptr;
81
82 // TODO: Create these resources
83 // std::shared_ptr<AudioControl> audio_control = nullptr;
84 // std::shared_ptr<ButtonConfig> button_config = nullptr;
85 // std::shared_ptr<Config> config = nullptr;
86 // std::shared_ptr<Connection> connection = nullptr;
87 // std::shared_ptr<CustomConfig> custom_config = nullptr;
88 // std::shared_ptr<Digitizer> digitizer = nullptr;
89 // std::shared_ptr<Hdls> hdls = nullptr;
90 // std::shared_ptr<PlayReport> play_report = nullptr;
91 // std::shared_ptr<Rail> rail = nullptr;
85 92
86 Core::System& system; 93 Core::System& system;
87 KernelHelpers::ServiceContext service_context; 94 KernelHelpers::ServiceContext service_context;
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 7927f8264..961f89a14 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -115,12 +115,20 @@ public:
115 {400, nullptr, "InitializeSystem"}, 115 {400, nullptr, "InitializeSystem"},
116 {401, nullptr, "FinalizeSystem"}, 116 {401, nullptr, "FinalizeSystem"},
117 {402, nullptr, "SetOperationMode"}, 117 {402, nullptr, "SetOperationMode"},
118 {403, nullptr, "InitializeSystem2"}, 118 {403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"},
119 }; 119 };
120 // clang-format on 120 // clang-format on
121 121
122 RegisterHandlers(functions); 122 RegisterHandlers(functions);
123 } 123 }
124
125private:
126 void InitializeSystem2(HLERequestContext& ctx) {
127 LOG_WARNING(Service_LDN, "(STUBBED) called");
128
129 IPC::ResponseBuilder rb{ctx, 2};
130 rb.Push(ResultSuccess);
131 }
124}; 132};
125 133
126class IUserLocalCommunicationService final 134class IUserLocalCommunicationService final
diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp
index a019cc9f7..c27646fcf 100644
--- a/src/core/hle/service/mii/types/ver3_store_data.cpp
+++ b/src/core/hle/service/mii/types/ver3_store_data.cpp
@@ -98,7 +98,7 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const {
98} 98}
99 99
100void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { 100void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) {
101 version = 1; 101 version = 3;
102 mii_information.gender.Assign(static_cast<u8>(store_data.GetGender())); 102 mii_information.gender.Assign(static_cast<u8>(store_data.GetGender()));
103 mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor())); 103 mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor()));
104 height = store_data.GetHeight(); 104 height = store_data.GetHeight();
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index e7a00deb3..f97e5b44c 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -401,6 +401,12 @@ Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& time
401} 401}
402 402
403Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) { 403Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) {
404 bool is_corrupted = false;
405
406 if (model_type != NFP::ModelType::Amiibo) {
407 return ResultInvalidArgument;
408 }
409
404 if (device_state != DeviceState::TagFound) { 410 if (device_state != DeviceState::TagFound) {
405 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 411 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
406 return ResultWrongDeviceState; 412 return ResultWrongDeviceState;
@@ -420,26 +426,32 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
420 if (is_plain_amiibo) { 426 if (is_plain_amiibo) {
421 std::vector<u8> data(sizeof(NFP::NTAG215File)); 427 std::vector<u8> data(sizeof(NFP::NTAG215File));
422 memcpy(data.data(), &tag_data, sizeof(tag_data)); 428 memcpy(data.data(), &tag_data, sizeof(tag_data));
423 WriteBackupData(tag_data.uid, data);
424
425 device_state = DeviceState::TagMounted;
426 mount_target = mount_target_;
427 return ResultSuccess;
428 } 429 }
429 430
430 if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { 431 if (!is_plain_amiibo && !NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
431 bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess(); 432 LOG_ERROR(Service_NFP, "Can't decode amiibo");
432 LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); 433 is_corrupted = true;
433 return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
434 } 434 }
435 435
436 std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); 436 if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) {
437 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); 437 LOG_ERROR(Service_NFP, "Invalid mii data");
438 WriteBackupData(encrypted_tag_data.uuid, data); 438 is_corrupted = true;
439 }
439 440
440 device_state = DeviceState::TagMounted; 441 device_state = DeviceState::TagMounted;
441 mount_target = mount_target_; 442 mount_target = mount_target_;
442 443
444 if (!is_corrupted && mount_target != NFP::MountTarget::Rom) {
445 std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
446 memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
447 WriteBackupData(encrypted_tag_data.uuid, data);
448 }
449
450 if (is_corrupted && mount_target != NFP::MountTarget::Rom) {
451 bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess();
452 return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
453 }
454
443 return ResultSuccess; 455 return ResultSuccess;
444} 456}
445 457
@@ -606,6 +618,17 @@ Result NfcDevice::Restore() {
606 } 618 }
607 } 619 }
608 620
621 // Restore mii data in case is corrupted by previous instances of yuzu
622 if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) {
623 LOG_ERROR(Service_NFP, "Regenerating mii data");
624 Mii::StoreData new_mii{};
625 new_mii.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
626 new_mii.SetNickname({u'y', u'u', u'z', u'u', u'\0'});
627
628 tag_data.owner_mii.BuildFromStoreData(new_mii);
629 tag_data.mii_extension.SetFromStoreData(new_mii);
630 }
631
609 // Overwrite tag contents with backup and mount the tag 632 // Overwrite tag contents with backup and mount the tag
610 tag_data = temporary_tag_data; 633 tag_data = temporary_tag_data;
611 encrypted_tag_data = temporary_encrypted_tag_data; 634 encrypted_tag_data = temporary_encrypted_tag_data;
@@ -851,25 +874,6 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
851 return Flush(); 874 return Flush();
852} 875}
853 876
854Result NfcDevice::RestoreAmiibo() {
855 if (device_state != DeviceState::TagMounted) {
856 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
857 if (device_state == DeviceState::TagRemoved) {
858 return ResultTagRemoved;
859 }
860 return ResultWrongDeviceState;
861 }
862
863 if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
864 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
865 return ResultWrongDeviceState;
866 }
867
868 // TODO: Load amiibo from backup on system
869 LOG_ERROR(Service_NFP, "Not Implemented");
870 return ResultSuccess;
871}
872
873Result NfcDevice::Format() { 877Result NfcDevice::Format() {
874 Result result = ResultSuccess; 878 Result result = ResultSuccess;
875 879
@@ -877,7 +881,9 @@ Result NfcDevice::Format() {
877 result = Mount(NFP::ModelType::Amiibo, NFP::MountTarget::All); 881 result = Mount(NFP::ModelType::Amiibo, NFP::MountTarget::All);
878 } 882 }
879 883
880 if (result.IsError()) { 884 // We are formatting all data. Corruption is not an issue.
885 if (result.IsError() &&
886 (result != ResultCorruptedData && result != ResultCorruptedDataWithBackup)) {
881 return result; 887 return result;
882 } 888 }
883 889
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index 0ed1ff34c..d8efe25ec 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -68,7 +68,6 @@ public:
68 68
69 Result DeleteRegisterInfo(); 69 Result DeleteRegisterInfo();
70 Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info); 70 Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info);
71 Result RestoreAmiibo();
72 Result Format(); 71 Result Format();
73 72
74 Result OpenApplicationArea(u32 access_id); 73 Result OpenApplicationArea(u32 access_id);
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index a71d26157..ad534177d 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -7,6 +7,7 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
9#include "core/hle/kernel/k_event.h" 9#include "core/hle/kernel/k_event.h"
10#include "core/hle/service/hid/hid_util.h"
10#include "core/hle/service/ipc_helpers.h" 11#include "core/hle/service/ipc_helpers.h"
11#include "core/hle/service/nfc/common/device.h" 12#include "core/hle/service/nfc/common/device.h"
12#include "core/hle/service/nfc/common/device_manager.h" 13#include "core/hle/service/nfc/common/device_manager.h"
@@ -24,7 +25,7 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex
24 25
25 for (u32 device_index = 0; device_index < devices.size(); device_index++) { 26 for (u32 device_index = 0; device_index < devices.size(); device_index++) {
26 devices[device_index] = 27 devices[device_index] =
27 std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system, 28 std::make_shared<NfcDevice>(HID::IndexToNpadIdType(device_index), system,
28 service_context, availability_change_event); 29 service_context, availability_change_event);
29 } 30 }
30 31
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index ec3af80af..48304e6d1 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -19,19 +19,8 @@
19 19
20namespace Service::Set { 20namespace Service::Set {
21 21
22namespace { 22Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
23constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05; 23 GetFirmwareVersionType type) {
24
25enum class GetFirmwareVersionType {
26 Version1,
27 Version2,
28};
29
30void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx,
31 GetFirmwareVersionType type) {
32 ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100,
33 "FirmwareVersion output buffer must be 0x100 bytes in size!");
34
35 constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; 24 constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809;
36 auto& fsc = system.GetFileSystemController(); 25 auto& fsc = system.GetFileSystemController();
37 26
@@ -45,46 +34,43 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx,
45 nca = bis_system->GetEntry(FirmwareVersionSystemDataId, FileSys::ContentRecordType::Data); 34 nca = bis_system->GetEntry(FirmwareVersionSystemDataId, FileSys::ContentRecordType::Data);
46 } 35 }
47 if (nca) { 36 if (nca) {
48 romfs = FileSys::ExtractRomFS(nca->GetRomFS()); 37 if (auto nca_romfs = nca->GetRomFS(); nca_romfs) {
38 romfs = FileSys::ExtractRomFS(nca_romfs);
39 }
49 } 40 }
50 if (!romfs) { 41 if (!romfs) {
51 romfs = FileSys::ExtractRomFS( 42 romfs = FileSys::ExtractRomFS(
52 FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); 43 FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId));
53 } 44 }
54 45
55 const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { 46 const auto early_exit_failure = [](std::string_view desc, Result code) {
56 LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", 47 LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
57 desc); 48 desc);
58 IPC::ResponseBuilder rb{ctx, 2}; 49 return code;
59 rb.Push(code);
60 }; 50 };
61 51
62 const auto ver_file = romfs->GetFile("file"); 52 const auto ver_file = romfs->GetFile("file");
63 if (ver_file == nullptr) { 53 if (ver_file == nullptr) {
64 early_exit_failure("The system version archive didn't contain the file 'file'.", 54 return early_exit_failure("The system version archive didn't contain the file 'file'.",
65 FileSys::ERROR_INVALID_ARGUMENT); 55 FileSys::ERROR_INVALID_ARGUMENT);
66 return;
67 } 56 }
68 57
69 auto data = ver_file->ReadAllBytes(); 58 auto data = ver_file->ReadAllBytes();
70 if (data.size() != 0x100) { 59 if (data.size() != sizeof(FirmwareVersionFormat)) {
71 early_exit_failure("The system version file 'file' was not the correct size.", 60 return early_exit_failure("The system version file 'file' was not the correct size.",
72 FileSys::ERROR_OUT_OF_BOUNDS); 61 FileSys::ERROR_OUT_OF_BOUNDS);
73 return;
74 } 62 }
75 63
64 std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat));
65
76 // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will 66 // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will
77 // zero out the REVISION_MINOR field. 67 // zero out the REVISION_MINOR field.
78 if (type == GetFirmwareVersionType::Version1) { 68 if (type == GetFirmwareVersionType::Version1) {
79 data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0; 69 out_firmware.revision_minor = 0;
80 } 70 }
81 71
82 ctx.WriteBuffer(data); 72 return ResultSuccess;
83
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(ResultSuccess);
86} 73}
87} // Anonymous namespace
88 74
89void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { 75void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
90 IPC::RequestParser rp{ctx}; 76 IPC::RequestParser rp{ctx};
@@ -98,12 +84,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
98 84
99void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { 85void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) {
100 LOG_DEBUG(Service_SET, "called"); 86 LOG_DEBUG(Service_SET, "called");
101 GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1); 87
88 FirmwareVersionFormat firmware_data{};
89 const auto result =
90 GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1);
91
92 if (result.IsSuccess()) {
93 ctx.WriteBuffer(firmware_data);
94 }
95
96 IPC::ResponseBuilder rb{ctx, 2};
97 rb.Push(result);
102} 98}
103 99
104void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { 100void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) {
105 LOG_DEBUG(Service_SET, "called"); 101 LOG_DEBUG(Service_SET, "called");
106 GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2); 102
103 FirmwareVersionFormat firmware_data{};
104 const auto result =
105 GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2);
106
107 if (result.IsSuccess()) {
108 ctx.WriteBuffer(firmware_data);
109 }
110
111 IPC::ResponseBuilder rb{ctx, 2};
112 rb.Push(result);
107} 113}
108 114
109void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { 115void SET_SYS::GetAccountSettings(HLERequestContext& ctx) {
@@ -431,8 +437,7 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) {
431void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { 437void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) {
432 u8 battery_percentage_flag{1}; 438 u8 battery_percentage_flag{1};
433 439
434 LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", 440 LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag);
435 battery_percentage_flag);
436 441
437 IPC::ResponseBuilder rb{ctx, 3}; 442 IPC::ResponseBuilder rb{ctx, 3};
438 rb.Push(ResultSuccess); 443 rb.Push(ResultSuccess);
@@ -492,6 +497,29 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) {
492 rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); 497 rb.PushEnum(ChineseTraditionalInputMethod::Unknown0);
493} 498}
494 499
500void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) {
501 LOG_DEBUG(Service_SET, "(STUBBED) called");
502
503 const HomeMenuScheme default_color = {
504 .main = 0xFF323232,
505 .back = 0xFF323232,
506 .sub = 0xFFFFFFFF,
507 .bezel = 0xFFFFFFFF,
508 .extra = 0xFF000000,
509 };
510
511 IPC::ResponseBuilder rb{ctx, 7};
512 rb.Push(ResultSuccess);
513 rb.PushRaw(default_color);
514}
515
516void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
517 LOG_WARNING(Service_SET, "(STUBBED) called");
518
519 IPC::ResponseBuilder rb{ctx, 3};
520 rb.Push(ResultSuccess);
521 rb.Push(0);
522}
495void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { 523void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) {
496 LOG_WARNING(Service_SET, "(STUBBED) called"); 524 LOG_WARNING(Service_SET, "(STUBBED) called");
497 525
@@ -674,7 +702,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
674 {171, nullptr, "SetChineseTraditionalInputMethod"}, 702 {171, nullptr, "SetChineseTraditionalInputMethod"},
675 {172, nullptr, "GetPtmCycleCountReliability"}, 703 {172, nullptr, "GetPtmCycleCountReliability"},
676 {173, nullptr, "SetPtmCycleCountReliability"}, 704 {173, nullptr, "SetPtmCycleCountReliability"},
677 {174, nullptr, "GetHomeMenuScheme"}, 705 {174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"},
678 {175, nullptr, "GetThemeSettings"}, 706 {175, nullptr, "GetThemeSettings"},
679 {176, nullptr, "SetThemeSettings"}, 707 {176, nullptr, "SetThemeSettings"},
680 {177, nullptr, "GetThemeKey"}, 708 {177, nullptr, "GetThemeKey"},
@@ -685,7 +713,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
685 {182, nullptr, "SetT"}, 713 {182, nullptr, "SetT"},
686 {183, nullptr, "GetPlatformRegion"}, 714 {183, nullptr, "GetPlatformRegion"},
687 {184, nullptr, "SetPlatformRegion"}, 715 {184, nullptr, "SetPlatformRegion"},
688 {185, nullptr, "GetHomeMenuSchemeModel"}, 716 {185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
689 {186, nullptr, "GetMemoryUsageRateFlag"}, 717 {186, nullptr, "GetMemoryUsageRateFlag"},
690 {187, nullptr, "GetTouchScreenMode"}, 718 {187, nullptr, "GetTouchScreenMode"},
691 {188, nullptr, "SetTouchScreenMode"}, 719 {188, nullptr, "SetTouchScreenMode"},
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index c7dba2a9e..5f770fd32 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "common/uuid.h" 6#include "common/uuid.h"
7#include "core/hle/result.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8#include "core/hle/service/time/clock_types.h" 9#include "core/hle/service/time/clock_types.h"
9 10
@@ -12,6 +13,29 @@ class System;
12} 13}
13 14
14namespace Service::Set { 15namespace Service::Set {
16enum class LanguageCode : u64;
17enum class GetFirmwareVersionType {
18 Version1,
19 Version2,
20};
21
22struct FirmwareVersionFormat {
23 u8 major;
24 u8 minor;
25 u8 micro;
26 INSERT_PADDING_BYTES(1);
27 u8 revision_major;
28 u8 revision_minor;
29 INSERT_PADDING_BYTES(2);
30 std::array<char, 0x20> platform;
31 std::array<u8, 0x40> version_hash;
32 std::array<char, 0x18> display_version;
33 std::array<char, 0x80> display_title;
34};
35static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size");
36
37Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
38 GetFirmwareVersionType type);
15 39
16class SET_SYS final : public ServiceFramework<SET_SYS> { 40class SET_SYS final : public ServiceFramework<SET_SYS> {
17public: 41public:
@@ -269,6 +293,16 @@ private:
269 }; 293 };
270 static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); 294 static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
271 295
296 /// This is nn::settings::system::HomeMenuScheme
297 struct HomeMenuScheme {
298 u32 main;
299 u32 back;
300 u32 sub;
301 u32 bezel;
302 u32 extra;
303 };
304 static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size");
305
272 void SetLanguageCode(HLERequestContext& ctx); 306 void SetLanguageCode(HLERequestContext& ctx);
273 void GetFirmwareVersion(HLERequestContext& ctx); 307 void GetFirmwareVersion(HLERequestContext& ctx);
274 void GetFirmwareVersion2(HLERequestContext& ctx); 308 void GetFirmwareVersion2(HLERequestContext& ctx);
@@ -305,6 +339,8 @@ private:
305 void GetKeyboardLayout(HLERequestContext& ctx); 339 void GetKeyboardLayout(HLERequestContext& ctx);
306 void GetChineseTraditionalInputMethod(HLERequestContext& ctx); 340 void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
307 void GetFieldTestingFlag(HLERequestContext& ctx); 341 void GetFieldTestingFlag(HLERequestContext& ctx);
342 void GetHomeMenuScheme(HLERequestContext& ctx);
343 void GetHomeMenuSchemeModel(HLERequestContext& ctx);
308 344
309 AccountSettings account_settings{ 345 AccountSettings account_settings{
310 .flags = {}, 346 .flags = {},
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index 9fc01ea90..7149fffeb 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -11,6 +11,11 @@
11#include "core/hle/service/time/errors.h" 11#include "core/hle/service/time/errors.h"
12#include "core/hle/service/time/time_zone_types.h" 12#include "core/hle/service/time/time_zone_types.h"
13 13
14// Defined by WinBase.h on Windows
15#ifdef GetCurrentTime
16#undef GetCurrentTime
17#endif
18
14namespace Service::Time::Clock { 19namespace Service::Time::Clock {
15 20
16enum class TimeType : u8 { 21enum class TimeType : u8 {
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 5c36b71e5..60ee78e89 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -3,6 +3,7 @@
3 3
4#include <cstring> 4#include <cstring>
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/settings.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/file_sys/content_archive.h" 8#include "core/file_sys/content_archive.h"
8#include "core/file_sys/control_metadata.h" 9#include "core/file_sys/control_metadata.h"
@@ -14,6 +15,10 @@
14#include "core/loader/deconstructed_rom_directory.h" 15#include "core/loader/deconstructed_rom_directory.h"
15#include "core/loader/nso.h" 16#include "core/loader/nso.h"
16 17
18#ifdef HAS_NCE
19#include "core/arm/nce/patcher.h"
20#endif
21
17namespace Loader { 22namespace Loader {
18 23
19AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, 24AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
@@ -124,21 +129,43 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
124 } 129 }
125 metadata.Print(); 130 metadata.Print();
126 131
127 const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", 132 // Enable NCE only for programs with 39-bit address space.
128 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", 133 const bool is_39bit =
129 "subsdk8", "subsdk9", "sdk"}; 134 metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit;
135 Settings::SetNceEnabled(is_39bit);
136
137 const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
138 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
139 "subsdk8", "subsdk9", "sdk"};
130 140
131 // Use the NSO module loader to figure out the code layout
132 std::size_t code_size{}; 141 std::size_t code_size{};
133 for (const auto& module : static_modules) { 142
143 // Define an nce patch context for each potential module.
144#ifdef HAS_NCE
145 std::array<Core::NCE::Patcher, 13> module_patchers;
146#endif
147
148 const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
149#ifdef HAS_NCE
150 if (Settings::IsNceEnabled()) {
151 return &module_patchers[i];
152 }
153#endif
154 return nullptr;
155 };
156
157 // Use the NSO module loader to figure out the code layout
158 for (size_t i = 0; i < static_modules.size(); i++) {
159 const auto& module = static_modules[i];
134 const FileSys::VirtualFile module_file{dir->GetFile(module)}; 160 const FileSys::VirtualFile module_file{dir->GetFile(module)};
135 if (!module_file) { 161 if (!module_file) {
136 continue; 162 continue;
137 } 163 }
138 164
139 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 165 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
140 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( 166 const auto tentative_next_load_addr =
141 process, system, *module_file, code_size, should_pass_arguments, false); 167 AppLoader_NSO::LoadModule(process, system, *module_file, code_size,
168 should_pass_arguments, false, {}, GetPatcher(i));
142 if (!tentative_next_load_addr) { 169 if (!tentative_next_load_addr) {
143 return {ResultStatus::ErrorLoadingNSO, {}}; 170 return {ResultStatus::ErrorLoadingNSO, {}};
144 } 171 }
@@ -146,8 +173,18 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
146 code_size = *tentative_next_load_addr; 173 code_size = *tentative_next_load_addr;
147 } 174 }
148 175
176 // Enable direct memory mapping in case of NCE.
177 const u64 fastmem_base = [&]() -> size_t {
178 if (Settings::IsNceEnabled()) {
179 auto& buffer = system.DeviceMemory().buffer;
180 buffer.EnableDirectMappedAddress();
181 return reinterpret_cast<u64>(buffer.VirtualBasePointer());
182 }
183 return 0;
184 }();
185
149 // Setup the process code layout 186 // Setup the process code layout
150 if (process.LoadFromMetadata(metadata, code_size, is_hbl).IsError()) { 187 if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) {
151 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; 188 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
152 } 189 }
153 190
@@ -157,7 +194,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
157 VAddr next_load_addr{base_address}; 194 VAddr next_load_addr{base_address};
158 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), 195 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
159 system.GetContentProvider()}; 196 system.GetContentProvider()};
160 for (const auto& module : static_modules) { 197 for (size_t i = 0; i < static_modules.size(); i++) {
198 const auto& module = static_modules[i];
161 const FileSys::VirtualFile module_file{dir->GetFile(module)}; 199 const FileSys::VirtualFile module_file{dir->GetFile(module)};
162 if (!module_file) { 200 if (!module_file) {
163 continue; 201 continue;
@@ -165,15 +203,16 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
165 203
166 const VAddr load_addr{next_load_addr}; 204 const VAddr load_addr{next_load_addr};
167 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 205 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
168 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( 206 const auto tentative_next_load_addr =
169 process, system, *module_file, load_addr, should_pass_arguments, true, pm); 207 AppLoader_NSO::LoadModule(process, system, *module_file, load_addr,
208 should_pass_arguments, true, pm, GetPatcher(i));
170 if (!tentative_next_load_addr) { 209 if (!tentative_next_load_addr) {
171 return {ResultStatus::ErrorLoadingNSO, {}}; 210 return {ResultStatus::ErrorLoadingNSO, {}};
172 } 211 }
173 212
174 next_load_addr = *tentative_next_load_addr; 213 next_load_addr = *tentative_next_load_addr;
175 modules.insert_or_assign(load_addr, module); 214 modules.insert_or_assign(load_addr, module);
176 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 215 LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr);
177 } 216 }
178 217
179 // Find the RomFS by searching for a ".romfs" file in this directory 218 // Find the RomFS by searching for a ".romfs" file in this directory
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index bf56a08b4..cd6982921 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -91,7 +91,8 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
91 91
92 // Setup the process code layout 92 // Setup the process code layout
93 if (process 93 if (process
94 .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), false) 94 .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), 0,
95 false)
95 .IsError()) { 96 .IsError()) {
96 return {ResultStatus::ErrorNotInitialized, {}}; 97 return {ResultStatus::ErrorNotInitialized, {}};
97 } 98 }
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 4feb6968a..814407535 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -74,10 +74,8 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S
74 return load_result; 74 return load_result;
75 } 75 }
76 76
77 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { 77 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
78 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( 78 *this, system.GetContentProvider(), system.GetFileSystemController()));
79 *this, system.GetContentProvider(), system.GetFileSystemController()));
80 }
81 79
82 is_loaded = true; 80 is_loaded = true;
83 return load_result; 81 return load_result;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 69f1a54ed..e74697cda 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -22,6 +22,10 @@
22#include "core/loader/nso.h" 22#include "core/loader/nso.h"
23#include "core/memory.h" 23#include "core/memory.h"
24 24
25#ifdef HAS_NCE
26#include "core/arm/nce/patcher.h"
27#endif
28
25namespace Loader { 29namespace Loader {
26 30
27struct NroSegmentHeader { 31struct NroSegmentHeader {
@@ -139,7 +143,8 @@ static constexpr u32 PageAlignSize(u32 size) {
139 return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); 143 return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
140} 144}
141 145
142static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) { 146static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
147 const std::vector<u8>& data) {
143 if (data.size() < sizeof(NroHeader)) { 148 if (data.size() < sizeof(NroHeader)) {
144 return {}; 149 return {};
145 } 150 }
@@ -194,14 +199,61 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
194 199
195 codeset.DataSegment().size += bss_size; 200 codeset.DataSegment().size += bss_size;
196 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 201 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
202 size_t image_size = program_image.size();
203
204#ifdef HAS_NCE
205 const auto& code = codeset.CodeSegment();
206
207 // NROs always have a 39-bit address space.
208 Settings::SetNceEnabled(true);
209
210 // Create NCE patcher
211 Core::NCE::Patcher patch{};
212
213 if (Settings::IsNceEnabled()) {
214 // Patch SVCs and MRS calls in the guest code
215 patch.PatchText(program_image, code);
216
217 // We only support PostData patching for NROs.
218 ASSERT(patch.GetPatchMode() == Core::NCE::PatchMode::PostData);
219
220 // Update patch section.
221 auto& patch_segment = codeset.PatchSegment();
222 patch_segment.addr = image_size;
223 patch_segment.size = static_cast<u32>(patch.GetSectionSize());
224
225 // Add patch section size to the module size.
226 image_size += patch_segment.size;
227 }
228#endif
229
230 // Enable direct memory mapping in case of NCE.
231 const u64 fastmem_base = [&]() -> size_t {
232 if (Settings::IsNceEnabled()) {
233 auto& buffer = system.DeviceMemory().buffer;
234 buffer.EnableDirectMappedAddress();
235 return reinterpret_cast<u64>(buffer.VirtualBasePointer());
236 }
237 return 0;
238 }();
197 239
198 // Setup the process code layout 240 // Setup the process code layout
199 if (process 241 if (process
200 .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), false) 242 .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base,
243 false)
201 .IsError()) { 244 .IsError()) {
202 return false; 245 return false;
203 } 246 }
204 247
248 // Relocate code patch and copy to the program_image if running under NCE.
249 // This needs to be after LoadFromMetadata so we can use the process entry point.
250#ifdef HAS_NCE
251 if (Settings::IsNceEnabled()) {
252 patch.RelocateAndCopy(process.GetEntryPoint(), code, program_image,
253 &process.GetPostHandlers());
254 }
255#endif
256
205 // Load codeset for current process 257 // Load codeset for current process
206 codeset.memory = std::move(program_image); 258 codeset.memory = std::move(program_image);
207 process.LoadModule(std::move(codeset), process.GetEntryPoint()); 259 process.LoadModule(std::move(codeset), process.GetEntryPoint());
@@ -209,8 +261,9 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
209 return true; 261 return true;
210} 262}
211 263
212bool AppLoader_NRO::LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file) { 264bool AppLoader_NRO::LoadNro(Core::System& system, Kernel::KProcess& process,
213 return LoadNroImpl(process, nro_file.ReadAllBytes()); 265 const FileSys::VfsFile& nro_file) {
266 return LoadNroImpl(system, process, nro_file.ReadAllBytes());
214} 267}
215 268
216AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) { 269AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) {
@@ -218,7 +271,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S
218 return {ResultStatus::ErrorAlreadyLoaded, {}}; 271 return {ResultStatus::ErrorAlreadyLoaded, {}};
219 } 272 }
220 273
221 if (!LoadNro(process, *file)) { 274 if (!LoadNro(system, process, *file)) {
222 return {ResultStatus::ErrorLoadingNRO, {}}; 275 return {ResultStatus::ErrorLoadingNRO, {}};
223 } 276 }
224 277
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 8de6eebc6..d2928cba0 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -54,7 +54,7 @@ public:
54 bool IsRomFSUpdatable() const override; 54 bool IsRomFSUpdatable() const override;
55 55
56private: 56private:
57 bool LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file); 57 bool LoadNro(Core::System& system, Kernel::KProcess& process, const FileSys::VfsFile& nro_file);
58 58
59 std::vector<u8> icon_data; 59 std::vector<u8> icon_data;
60 std::unique_ptr<FileSys::NACP> nacp; 60 std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 1350da8dc..b053a0d14 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -20,6 +20,10 @@
20#include "core/loader/nso.h" 20#include "core/loader/nso.h"
21#include "core/memory.h" 21#include "core/memory.h"
22 22
23#ifdef HAS_NCE
24#include "core/arm/nce/patcher.h"
25#endif
26
23namespace Loader { 27namespace Loader {
24namespace { 28namespace {
25struct MODHeader { 29struct MODHeader {
@@ -72,7 +76,8 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) {
72std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, 76std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system,
73 const FileSys::VfsFile& nso_file, VAddr load_base, 77 const FileSys::VfsFile& nso_file, VAddr load_base,
74 bool should_pass_arguments, bool load_into_process, 78 bool should_pass_arguments, bool load_into_process,
75 std::optional<FileSys::PatchManager> pm) { 79 std::optional<FileSys::PatchManager> pm,
80 Core::NCE::Patcher* patch) {
76 if (nso_file.GetSize() < sizeof(NSOHeader)) { 81 if (nso_file.GetSize() < sizeof(NSOHeader)) {
77 return std::nullopt; 82 return std::nullopt;
78 } 83 }
@@ -86,6 +91,16 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
86 return std::nullopt; 91 return std::nullopt;
87 } 92 }
88 93
94 // Allocate some space at the beginning if we are patching in PreText mode.
95 const size_t module_start = [&]() -> size_t {
96#ifdef HAS_NCE
97 if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) {
98 return patch->GetSectionSize();
99 }
100#endif
101 return 0;
102 }();
103
89 // Build program image 104 // Build program image
90 Kernel::CodeSet codeset; 105 Kernel::CodeSet codeset;
91 Kernel::PhysicalMemory program_image; 106 Kernel::PhysicalMemory program_image;
@@ -95,11 +110,12 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
95 if (nso_header.IsSegmentCompressed(i)) { 110 if (nso_header.IsSegmentCompressed(i)) {
96 data = DecompressSegment(data, nso_header.segments[i]); 111 data = DecompressSegment(data, nso_header.segments[i]);
97 } 112 }
98 program_image.resize(nso_header.segments[i].location + static_cast<u32>(data.size())); 113 program_image.resize(module_start + nso_header.segments[i].location +
99 std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(), 114 static_cast<u32>(data.size()));
100 data.size()); 115 std::memcpy(program_image.data() + module_start + nso_header.segments[i].location,
101 codeset.segments[i].addr = nso_header.segments[i].location; 116 data.data(), data.size());
102 codeset.segments[i].offset = nso_header.segments[i].location; 117 codeset.segments[i].addr = module_start + nso_header.segments[i].location;
118 codeset.segments[i].offset = module_start + nso_header.segments[i].location;
103 codeset.segments[i].size = nso_header.segments[i].size; 119 codeset.segments[i].size = nso_header.segments[i].size;
104 } 120 }
105 121
@@ -118,7 +134,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
118 } 134 }
119 135
120 codeset.DataSegment().size += nso_header.segments[2].bss_size; 136 codeset.DataSegment().size += nso_header.segments[2].bss_size;
121 const u32 image_size{ 137 u32 image_size{
122 PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)}; 138 PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)};
123 program_image.resize(image_size); 139 program_image.resize(image_size);
124 140
@@ -129,15 +145,44 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
129 // Apply patches if necessary 145 // Apply patches if necessary
130 const auto name = nso_file.GetName(); 146 const auto name = nso_file.GetName();
131 if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) { 147 if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) {
132 std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size()); 148 std::span<u8> patchable_section(program_image.data() + module_start,
149 program_image.size() - module_start);
150 std::vector<u8> pi_header(sizeof(NSOHeader) + patchable_section.size());
133 std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); 151 std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader));
134 std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(), 152 std::memcpy(pi_header.data() + sizeof(NSOHeader), patchable_section.data(),
135 program_image.size()); 153 patchable_section.size());
136 154
137 pi_header = pm->PatchNSO(pi_header, name); 155 pi_header = pm->PatchNSO(pi_header, name);
138 156
139 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data()); 157 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), patchable_section.data());
158 }
159
160#ifdef HAS_NCE
161 // If we are computing the process code layout and using nce backend, patch.
162 const auto& code = codeset.CodeSegment();
163 if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) {
164 // Patch SVCs and MRS calls in the guest code
165 patch->PatchText(program_image, code);
166
167 // Add patch section size to the module size.
168 image_size += static_cast<u32>(patch->GetSectionSize());
169 } else if (patch) {
170 // Relocate code patch and copy to the program_image.
171 patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers());
172
173 // Update patch section.
174 auto& patch_segment = codeset.PatchSegment();
175 patch_segment.addr =
176 patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size;
177 patch_segment.size = static_cast<u32>(patch->GetSectionSize());
178
179 // Add patch section size to the module size. In PreText mode image_size
180 // already contains the patch segment as part of module_start.
181 if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) {
182 image_size += patch_segment.size;
183 }
140 } 184 }
185#endif
141 186
142 // If we aren't actually loading (i.e. just computing the process code layout), we are done 187 // If we aren't actually loading (i.e. just computing the process code layout), we are done
143 if (!load_into_process) { 188 if (!load_into_process) {
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 0b53b4ecd..29b86ed4c 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -15,6 +15,10 @@ namespace Core {
15class System; 15class System;
16} 16}
17 17
18namespace Core::NCE {
19class Patcher;
20}
21
18namespace Kernel { 22namespace Kernel {
19class KProcess; 23class KProcess;
20} 24}
@@ -88,7 +92,8 @@ public:
88 static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system, 92 static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system,
89 const FileSys::VfsFile& nso_file, VAddr load_base, 93 const FileSys::VfsFile& nso_file, VAddr load_base,
90 bool should_pass_arguments, bool load_into_process, 94 bool should_pass_arguments, bool load_into_process,
91 std::optional<FileSys::PatchManager> pm = {}); 95 std::optional<FileSys::PatchManager> pm = {},
96 Core::NCE::Patcher* patch = nullptr);
92 97
93 LoadResult Load(Kernel::KProcess& process, Core::System& system) override; 98 LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
94 99
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index a3431772a..5b376b202 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -53,7 +53,7 @@ struct Memory::Impl {
53 } 53 }
54 54
55 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 55 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
56 Common::PhysicalAddress target) { 56 Common::PhysicalAddress target, Common::MemoryPermission perms) {
57 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); 57 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
58 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); 58 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
59 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", 59 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}",
@@ -63,7 +63,7 @@ struct Memory::Impl {
63 63
64 if (Settings::IsFastmemEnabled()) { 64 if (Settings::IsFastmemEnabled()) {
65 system.DeviceMemory().buffer.Map(GetInteger(base), 65 system.DeviceMemory().buffer.Map(GetInteger(base),
66 GetInteger(target) - DramMemoryMap::Base, size); 66 GetInteger(target) - DramMemoryMap::Base, size, perms);
67 } 67 }
68 } 68 }
69 69
@@ -78,6 +78,51 @@ struct Memory::Impl {
78 } 78 }
79 } 79 }
80 80
81 void ProtectRegion(Common::PageTable& page_table, VAddr vaddr, u64 size,
82 Common::MemoryPermission perms) {
83 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
84 ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr);
85
86 if (!Settings::IsFastmemEnabled()) {
87 return;
88 }
89
90 const bool is_r = True(perms & Common::MemoryPermission::Read);
91 const bool is_w = True(perms & Common::MemoryPermission::Write);
92 const bool is_x =
93 True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled();
94
95 if (!current_page_table) {
96 system.DeviceMemory().buffer.Protect(vaddr, size, is_r, is_w, is_x);
97 return;
98 }
99
100 u64 protect_bytes{};
101 u64 protect_begin{};
102 for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
103 const Common::PageType page_type{
104 current_page_table->pointers[addr >> YUZU_PAGEBITS].Type()};
105 switch (page_type) {
106 case Common::PageType::RasterizerCachedMemory:
107 if (protect_bytes > 0) {
108 system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w,
109 is_x);
110 protect_bytes = 0;
111 }
112 break;
113 default:
114 if (protect_bytes == 0) {
115 protect_begin = addr;
116 }
117 protect_bytes += YUZU_PAGESIZE;
118 }
119 }
120
121 if (protect_bytes > 0) {
122 system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, is_x);
123 }
124 }
125
81 [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const { 126 [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
82 const Common::PhysicalAddress paddr{ 127 const Common::PhysicalAddress paddr{
83 current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]}; 128 current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]};
@@ -831,14 +876,19 @@ void Memory::SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) {
831} 876}
832 877
833void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 878void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
834 Common::PhysicalAddress target) { 879 Common::PhysicalAddress target, Common::MemoryPermission perms) {
835 impl->MapMemoryRegion(page_table, base, size, target); 880 impl->MapMemoryRegion(page_table, base, size, target, perms);
836} 881}
837 882
838void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { 883void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) {
839 impl->UnmapRegion(page_table, base, size); 884 impl->UnmapRegion(page_table, base, size);
840} 885}
841 886
887void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size,
888 Common::MemoryPermission perms) {
889 impl->ProtectRegion(page_table, GetInteger(vaddr), size, perms);
890}
891
842bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { 892bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
843 const Kernel::KProcess& process = *system.ApplicationProcess(); 893 const Kernel::KProcess& process = *system.ApplicationProcess();
844 const auto& page_table = process.GetPageTable().GetImpl(); 894 const auto& page_table = process.GetPageTable().GetImpl();
@@ -1001,4 +1051,17 @@ void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
1001 impl->FlushRegion(dest_addr, size); 1051 impl->FlushRegion(dest_addr, size);
1002} 1052}
1003 1053
1054bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
1055 bool mapped = true;
1056 u8* const ptr = impl->GetPointerImpl(
1057 GetInteger(vaddr),
1058 [&] {
1059 LOG_ERROR(HW_Memory, "Unmapped InvalidateNCE for {} bytes @ {:#x}", size,
1060 GetInteger(vaddr));
1061 mapped = false;
1062 },
1063 [&] { impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); });
1064 return mapped && ptr != nullptr;
1065}
1066
1004} // namespace Core::Memory 1067} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index 13047a545..ed8ebb5eb 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -15,8 +15,9 @@
15#include "core/hle/result.h" 15#include "core/hle/result.h"
16 16
17namespace Common { 17namespace Common {
18enum class MemoryPermission : u32;
18struct PageTable; 19struct PageTable;
19} 20} // namespace Common
20 21
21namespace Core { 22namespace Core {
22class System; 23class System;
@@ -82,9 +83,10 @@ public:
82 * @param size The amount of bytes to map. Must be page-aligned. 83 * @param size The amount of bytes to map. Must be page-aligned.
83 * @param target Buffer with the memory backing the mapping. Must be of length at least 84 * @param target Buffer with the memory backing the mapping. Must be of length at least
84 * `size`. 85 * `size`.
86 * @param perms The permissions to map the memory with.
85 */ 87 */
86 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 88 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
87 Common::PhysicalAddress target); 89 Common::PhysicalAddress target, Common::MemoryPermission perms);
88 90
89 /** 91 /**
90 * Unmaps a region of the emulated process address space. 92 * Unmaps a region of the emulated process address space.
@@ -96,6 +98,17 @@ public:
96 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size); 98 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size);
97 99
98 /** 100 /**
101 * Protects a region of the emulated process address space with the new permissions.
102 *
103 * @param page_table The page table of the emulated process.
104 * @param base The start address to re-protect. Must be page-aligned.
105 * @param size The amount of bytes to protect. Must be page-aligned.
106 * @param perms The permissions the address range is mapped.
107 */
108 void ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
109 Common::MemoryPermission perms);
110
111 /**
99 * Checks whether or not the supplied address is a valid virtual 112 * Checks whether or not the supplied address is a valid virtual
100 * address for the current process. 113 * address for the current process.
101 * 114 *
@@ -472,6 +485,7 @@ public:
472 485
473 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); 486 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
474 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); 487 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
488 bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
475 void FlushRegion(Common::ProcessAddress dest_addr, size_t size); 489 void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
476 490
477private: 491private:
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index da140c01c..db30ba598 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -68,10 +68,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
68 return 0; 68 return 0;
69 } 69 }
70 70
71 const auto press_state = 71 const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
72 applet_resource
73 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
74 .GetAndResetPressState();
75 return static_cast<u64>(press_state & HID::NpadButton::All); 72 return static_cast<u64>(press_state & HID::NpadButton::All);
76} 73}
77 74
diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt
new file mode 100644
index 000000000..22e9337c4
--- /dev/null
+++ b/src/frontend_common/CMakeLists.txt
@@ -0,0 +1,10 @@
1# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4add_library(frontend_common STATIC
5 config.cpp
6 config.h
7)
8
9create_target_directory_groups(frontend_common)
10target_link_libraries(frontend_common PUBLIC core SimpleIni::SimpleIni PRIVATE common Boost::headers)
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
new file mode 100644
index 000000000..1a0491c2c
--- /dev/null
+++ b/src/frontend_common/config.cpp
@@ -0,0 +1,1010 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <array>
6#include "common/fs/fs.h"
7#include "common/fs/path_util.h"
8#include "common/settings.h"
9#include "common/settings_common.h"
10#include "common/settings_enums.h"
11#include "config.h"
12#include "core/core.h"
13#include "core/hle/service/acc/profile_manager.h"
14#include "core/hle/service/hid/controllers/npad.h"
15#include "network/network.h"
16
17#include <boost/algorithm/string/replace.hpp>
18
19#include "common/string_util.h"
20
21namespace FS = Common::FS;
22
23Config::Config(const ConfigType config_type)
24 : type(config_type), global{config_type == ConfigType::GlobalConfig} {}
25
26void Config::Initialize(const std::string& config_name) {
27 const std::filesystem::path fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
28 const auto config_file = fmt::format("{}.ini", config_name);
29
30 switch (type) {
31 case ConfigType::GlobalConfig:
32 config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
33 void(FS::CreateParentDir(config_loc));
34 SetUpIni();
35 Reload();
36 break;
37 case ConfigType::PerGameConfig:
38 config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
39 void(FS::CreateParentDir(config_loc));
40 SetUpIni();
41 Reload();
42 break;
43 case ConfigType::InputProfile:
44 config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
45 void(FS::CreateParentDir(config_loc));
46 SetUpIni();
47 break;
48 }
49}
50
51void Config::Initialize(const std::optional<std::string> config_path) {
52 const std::filesystem::path default_sdl_config_path =
53 FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
54 config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path));
55 void(FS::CreateParentDir(config_loc));
56 SetUpIni();
57 Reload();
58}
59
60void Config::WriteToIni() const {
61 FILE* fp = nullptr;
62#ifdef _WIN32
63 fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb");
64#else
65 fp = fopen(config_loc.c_str(), "wb");
66#endif
67
68 if (fp == nullptr) {
69 LOG_ERROR(Frontend, "Config file could not be saved!");
70 return;
71 }
72
73 CSimpleIniA::FileWriter writer(fp);
74 const SI_Error rc = config->Save(writer, false);
75 if (rc < 0) {
76 LOG_ERROR(Frontend, "Config file could not be saved!");
77 }
78 fclose(fp);
79}
80
81void Config::SetUpIni() {
82 config = std::make_unique<CSimpleIniA>();
83 config->SetUnicode(true);
84 config->SetSpaces(false);
85
86 FILE* fp = nullptr;
87#ifdef _WIN32
88 _wfopen_s(&fp, Common::UTF8ToUTF16W(config_loc).data(), L"rb, ccs=UTF-8");
89 if (fp == nullptr) {
90 fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb, ccs=UTF-8");
91 }
92#else
93 fp = fopen(config_loc.c_str(), "rb");
94 if (fp == nullptr) {
95 fp = fopen(config_loc.c_str(), "wb");
96 }
97#endif
98
99 if (fp == nullptr) {
100 LOG_ERROR(Frontend, "Config file could not be loaded!");
101 return;
102 }
103
104 if (SI_Error rc = config->LoadFile(fp); rc < 0) {
105 LOG_ERROR(Frontend, "Config file could not be loaded!");
106 }
107 fclose(fp);
108}
109
110bool Config::IsCustomConfig() const {
111 return type == ConfigType::PerGameConfig;
112}
113
114void Config::ReadPlayerValues(const std::size_t player_index) {
115 std::string player_prefix;
116 if (type != ConfigType::InputProfile) {
117 player_prefix.append("player_").append(ToString(player_index)).append("_");
118 }
119
120 auto& player = Settings::values.players.GetValue()[player_index];
121 if (IsCustomConfig()) {
122 const auto profile_name =
123 ReadStringSetting(std::string(player_prefix).append("profile_name"));
124 if (profile_name.empty()) {
125 // Use the global input config
126 player = Settings::values.players.GetValue(true)[player_index];
127 return;
128 }
129 player.profile_name = profile_name;
130 }
131
132 if (player_prefix.empty() && Settings::IsConfiguringGlobal()) {
133 const auto controller = static_cast<Settings::ControllerType>(
134 ReadIntegerSetting(std::string(player_prefix).append("type"),
135 static_cast<u8>(Settings::ControllerType::ProController)));
136
137 if (controller == Settings::ControllerType::LeftJoycon ||
138 controller == Settings::ControllerType::RightJoycon) {
139 player.controller_type = controller;
140 }
141 } else {
142 std::string connected_key = player_prefix;
143 player.connected = ReadBooleanSetting(connected_key.append("connected"),
144 std::make_optional(player_index == 0));
145
146 player.controller_type = static_cast<Settings::ControllerType>(
147 ReadIntegerSetting(std::string(player_prefix).append("type"),
148 static_cast<u8>(Settings::ControllerType::ProController)));
149
150 player.vibration_enabled = ReadBooleanSetting(
151 std::string(player_prefix).append("vibration_enabled"), std::make_optional(true));
152
153 player.vibration_strength = static_cast<int>(
154 ReadIntegerSetting(std::string(player_prefix).append("vibration_strength"), 100));
155
156 player.body_color_left = static_cast<u32>(ReadIntegerSetting(
157 std::string(player_prefix).append("body_color_left"), Settings::JOYCON_BODY_NEON_BLUE));
158 player.body_color_right = static_cast<u32>(ReadIntegerSetting(
159 std::string(player_prefix).append("body_color_right"), Settings::JOYCON_BODY_NEON_RED));
160 player.button_color_left = static_cast<u32>(
161 ReadIntegerSetting(std::string(player_prefix).append("button_color_left"),
162 Settings::JOYCON_BUTTONS_NEON_BLUE));
163 player.button_color_right = static_cast<u32>(
164 ReadIntegerSetting(std::string(player_prefix).append("button_color_right"),
165 Settings::JOYCON_BUTTONS_NEON_RED));
166 }
167}
168
169void Config::ReadTouchscreenValues() {
170 Settings::values.touchscreen.enabled =
171 ReadBooleanSetting(std::string("touchscreen_enabled"), std::make_optional(true));
172 Settings::values.touchscreen.rotation_angle =
173 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0));
174 Settings::values.touchscreen.diameter_x =
175 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15));
176 Settings::values.touchscreen.diameter_y =
177 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15));
178}
179
180void Config::ReadAudioValues() {
181 BeginGroup(Settings::TranslateCategory(Settings::Category::Audio));
182
183 ReadCategory(Settings::Category::Audio);
184 ReadCategory(Settings::Category::UiAudio);
185
186 EndGroup();
187}
188
189void Config::ReadControlValues() {
190 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
191
192 ReadCategory(Settings::Category::Controls);
193
194 Settings::values.players.SetGlobal(!IsCustomConfig());
195 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
196 ReadPlayerValues(p);
197 }
198
199 // Disable docked mode if handheld is selected
200 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
201 if (controller_type == Settings::ControllerType::Handheld) {
202 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
203 Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
204 }
205
206 if (IsCustomConfig()) {
207 EndGroup();
208 return;
209 }
210 ReadTouchscreenValues();
211 ReadMotionTouchValues();
212
213 EndGroup();
214}
215
216void Config::ReadMotionTouchValues() {
217 int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps"));
218
219 if (num_touch_from_button_maps > 0) {
220 for (int i = 0; i < num_touch_from_button_maps; ++i) {
221 SetArrayIndex(i);
222
223 Settings::TouchFromButtonMap map;
224 map.name = ReadStringSetting(std::string("name"), std::string("default"));
225
226 const int num_touch_maps = BeginArray(std::string("entries"));
227 map.buttons.reserve(num_touch_maps);
228 for (int j = 0; j < num_touch_maps; j++) {
229 SetArrayIndex(j);
230 std::string touch_mapping = ReadStringSetting(std::string("bind"));
231 map.buttons.emplace_back(std::move(touch_mapping));
232 }
233 EndArray(); // entries
234 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
235 }
236 } else {
237 Settings::values.touch_from_button_maps.emplace_back(
238 Settings::TouchFromButtonMap{"default", {}});
239 num_touch_from_button_maps = 1;
240 }
241 EndArray(); // touch_from_button_maps
242
243 Settings::values.touch_from_button_map_index = std::clamp(
244 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
245}
246
247void Config::ReadCoreValues() {
248 BeginGroup(Settings::TranslateCategory(Settings::Category::Core));
249
250 ReadCategory(Settings::Category::Core);
251
252 EndGroup();
253}
254
255void Config::ReadDataStorageValues() {
256 BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
257
258 FS::SetYuzuPath(FS::YuzuPath::NANDDir, ReadStringSetting(std::string("nand_directory")));
259 FS::SetYuzuPath(FS::YuzuPath::SDMCDir, ReadStringSetting(std::string("sdmc_directory")));
260 FS::SetYuzuPath(FS::YuzuPath::LoadDir, ReadStringSetting(std::string("load_directory")));
261 FS::SetYuzuPath(FS::YuzuPath::DumpDir, ReadStringSetting(std::string("dump_directory")));
262 FS::SetYuzuPath(FS::YuzuPath::TASDir, ReadStringSetting(std::string("tas_directory")));
263
264 ReadCategory(Settings::Category::DataStorage);
265
266 EndGroup();
267}
268
269void Config::ReadDebuggingValues() {
270 BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
271
272 // Intentionally not using the QT default setting as this is intended to be changed in the ini
273 Settings::values.record_frame_times =
274 ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false));
275
276 ReadCategory(Settings::Category::Debugging);
277 ReadCategory(Settings::Category::DebuggingGraphics);
278
279 EndGroup();
280}
281
282void Config::ReadServiceValues() {
283 BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
284
285 ReadCategory(Settings::Category::Services);
286
287 EndGroup();
288}
289
290void Config::ReadDisabledAddOnValues() {
291 // Custom config section
292 BeginGroup(std::string("DisabledAddOns"));
293
294 const int size = BeginArray(std::string(""));
295 for (int i = 0; i < size; ++i) {
296 SetArrayIndex(i);
297 const auto title_id = ReadUnsignedIntegerSetting(std::string("title_id"), 0);
298 std::vector<std::string> out;
299 const int d_size = BeginArray("disabled");
300 for (int j = 0; j < d_size; ++j) {
301 SetArrayIndex(j);
302 out.push_back(ReadStringSetting(std::string("d"), std::string("")));
303 }
304 EndArray(); // d
305 Settings::values.disabled_addons.insert_or_assign(title_id, out);
306 }
307 EndArray(); // Base disabled addons array - Has no base key
308
309 EndGroup();
310}
311
312void Config::ReadMiscellaneousValues() {
313 BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous));
314
315 ReadCategory(Settings::Category::Miscellaneous);
316
317 EndGroup();
318}
319
320void Config::ReadCpuValues() {
321 BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu));
322
323 ReadCategory(Settings::Category::Cpu);
324 ReadCategory(Settings::Category::CpuDebug);
325 ReadCategory(Settings::Category::CpuUnsafe);
326
327 EndGroup();
328}
329
330void Config::ReadRendererValues() {
331 BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer));
332
333 ReadCategory(Settings::Category::Renderer);
334 ReadCategory(Settings::Category::RendererAdvanced);
335 ReadCategory(Settings::Category::RendererDebug);
336
337 EndGroup();
338}
339
340void Config::ReadScreenshotValues() {
341 BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
342
343 ReadCategory(Settings::Category::Screenshots);
344 FS::SetYuzuPath(FS::YuzuPath::ScreenshotsDir,
345 ReadStringSetting(std::string("screenshot_path")));
346
347 EndGroup();
348}
349
350void Config::ReadSystemValues() {
351 BeginGroup(Settings::TranslateCategory(Settings::Category::System));
352
353 ReadCategory(Settings::Category::System);
354 ReadCategory(Settings::Category::SystemAudio);
355
356 EndGroup();
357}
358
359void Config::ReadWebServiceValues() {
360 BeginGroup(Settings::TranslateCategory(Settings::Category::WebService));
361
362 ReadCategory(Settings::Category::WebService);
363
364 EndGroup();
365}
366
367void Config::ReadNetworkValues() {
368 BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
369
370 ReadCategory(Settings::Category::Network);
371
372 EndGroup();
373}
374
375void Config::ReadValues() {
376 if (global) {
377 ReadDataStorageValues();
378 ReadDebuggingValues();
379 ReadDisabledAddOnValues();
380 ReadNetworkValues();
381 ReadServiceValues();
382 ReadWebServiceValues();
383 ReadMiscellaneousValues();
384 }
385 ReadControlValues();
386 ReadCoreValues();
387 ReadCpuValues();
388 ReadRendererValues();
389 ReadAudioValues();
390 ReadSystemValues();
391}
392
393void Config::SavePlayerValues(const std::size_t player_index) {
394 std::string player_prefix;
395 if (type != ConfigType::InputProfile) {
396 player_prefix = std::string("player_").append(ToString(player_index)).append("_");
397 }
398
399 const auto& player = Settings::values.players.GetValue()[player_index];
400 if (IsCustomConfig()) {
401 if (player.profile_name.empty()) {
402 // No custom profile selected
403 return;
404 }
405 WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
406 std::make_optional(std::string("")));
407 }
408
409 WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
410 std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
411
412 if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) {
413 WriteSetting(std::string(player_prefix).append("connected"), player.connected,
414 std::make_optional(player_index == 0));
415 WriteSetting(std::string(player_prefix).append("vibration_enabled"),
416 player.vibration_enabled, std::make_optional(true));
417 WriteSetting(std::string(player_prefix).append("vibration_strength"),
418 player.vibration_strength, std::make_optional(100));
419 WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left,
420 std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
421 WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right,
422 std::make_optional(Settings::JOYCON_BODY_NEON_RED));
423 WriteSetting(std::string(player_prefix).append("button_color_left"),
424 player.button_color_left,
425 std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
426 WriteSetting(std::string(player_prefix).append("button_color_right"),
427 player.button_color_right,
428 std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
429 }
430}
431
432void Config::SaveTouchscreenValues() {
433 const auto& touchscreen = Settings::values.touchscreen;
434
435 WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true));
436
437 WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
438 std::make_optional(static_cast<u32>(0)));
439 WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
440 std::make_optional(static_cast<u32>(15)));
441 WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
442 std::make_optional(static_cast<u32>(15)));
443}
444
445void Config::SaveMotionTouchValues() {
446 BeginArray(std::string("touch_from_button_maps"));
447 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
448 SetArrayIndex(static_cast<int>(p));
449 WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
450 std::make_optional(std::string("default")));
451
452 BeginArray(std::string("entries"));
453 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
454 ++q) {
455 SetArrayIndex(static_cast<int>(q));
456 WriteSetting(std::string("bind"),
457 Settings::values.touch_from_button_maps[p].buttons[q]);
458 }
459 EndArray(); // entries
460 }
461 EndArray(); // touch_from_button_maps
462}
463
464void Config::SaveValues() {
465 if (global) {
466 SaveDataStorageValues();
467 SaveDebuggingValues();
468 SaveDisabledAddOnValues();
469 SaveNetworkValues();
470 SaveWebServiceValues();
471 SaveMiscellaneousValues();
472 }
473 SaveControlValues();
474 SaveCoreValues();
475 SaveCpuValues();
476 SaveRendererValues();
477 SaveAudioValues();
478 SaveSystemValues();
479
480 WriteToIni();
481}
482
483void Config::SaveAudioValues() {
484 BeginGroup(Settings::TranslateCategory(Settings::Category::Audio));
485
486 WriteCategory(Settings::Category::Audio);
487 WriteCategory(Settings::Category::UiAudio);
488
489 EndGroup();
490}
491
492void Config::SaveControlValues() {
493 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
494
495 WriteCategory(Settings::Category::Controls);
496
497 Settings::values.players.SetGlobal(!IsCustomConfig());
498 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
499 SavePlayerValues(p);
500 }
501 if (IsCustomConfig()) {
502 EndGroup();
503 return;
504 }
505 SaveTouchscreenValues();
506 SaveMotionTouchValues();
507
508 EndGroup();
509}
510
511void Config::SaveCoreValues() {
512 BeginGroup(Settings::TranslateCategory(Settings::Category::Core));
513
514 WriteCategory(Settings::Category::Core);
515
516 EndGroup();
517}
518
519void Config::SaveDataStorageValues() {
520 BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
521
522 WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
523 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
524 WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
525 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
526 WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
527 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
528 WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
529 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
530 WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
531 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
532
533 WriteCategory(Settings::Category::DataStorage);
534
535 EndGroup();
536}
537
538void Config::SaveDebuggingValues() {
539 BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
540
541 // Intentionally not using the QT default setting as this is intended to be changed in the ini
542 WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
543
544 WriteCategory(Settings::Category::Debugging);
545 WriteCategory(Settings::Category::DebuggingGraphics);
546
547 EndGroup();
548}
549
550void Config::SaveNetworkValues() {
551 BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
552
553 WriteCategory(Settings::Category::Network);
554
555 EndGroup();
556}
557
558void Config::SaveDisabledAddOnValues() {
559 // Custom config section
560 BeginGroup(std::string("DisabledAddOns"));
561
562 int i = 0;
563 BeginArray(std::string(""));
564 for (const auto& elem : Settings::values.disabled_addons) {
565 SetArrayIndex(i);
566 WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0)));
567 BeginArray(std::string("disabled"));
568 for (std::size_t j = 0; j < elem.second.size(); ++j) {
569 SetArrayIndex(static_cast<int>(j));
570 WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string("")));
571 }
572 EndArray(); // disabled
573 ++i;
574 }
575 EndArray(); // Base disabled addons array - Has no base key
576
577 EndGroup();
578}
579
580void Config::SaveMiscellaneousValues() {
581 BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous));
582
583 WriteCategory(Settings::Category::Miscellaneous);
584
585 EndGroup();
586}
587
588void Config::SaveCpuValues() {
589 BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu));
590
591 WriteCategory(Settings::Category::Cpu);
592 WriteCategory(Settings::Category::CpuDebug);
593 WriteCategory(Settings::Category::CpuUnsafe);
594
595 EndGroup();
596}
597
598void Config::SaveRendererValues() {
599 BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer));
600
601 WriteCategory(Settings::Category::Renderer);
602 WriteCategory(Settings::Category::RendererAdvanced);
603 WriteCategory(Settings::Category::RendererDebug);
604
605 EndGroup();
606}
607
608void Config::SaveScreenshotValues() {
609 BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
610
611 WriteSetting(std::string("screenshot_path"),
612 FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
613 WriteCategory(Settings::Category::Screenshots);
614
615 EndGroup();
616}
617
618void Config::SaveSystemValues() {
619 BeginGroup(Settings::TranslateCategory(Settings::Category::System));
620
621 WriteCategory(Settings::Category::System);
622 WriteCategory(Settings::Category::SystemAudio);
623
624 EndGroup();
625}
626
627void Config::SaveWebServiceValues() {
628 BeginGroup(Settings::TranslateCategory(Settings::Category::WebService));
629
630 WriteCategory(Settings::Category::WebService);
631
632 EndGroup();
633}
634
635bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
636 std::string full_key = GetFullKey(key, false);
637 if (!default_value.has_value()) {
638 return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), false);
639 }
640
641 if (config->GetBoolValue(GetSection().c_str(),
642 std::string(full_key).append("\\default").c_str(), false)) {
643 return static_cast<bool>(default_value.value());
644 } else {
645 return config->GetBoolValue(GetSection().c_str(), full_key.c_str(),
646 static_cast<bool>(default_value.value()));
647 }
648}
649
650s64 Config::ReadIntegerSetting(const std::string& key, const std::optional<s64> default_value) {
651 std::string full_key = GetFullKey(key, false);
652 if (!default_value.has_value()) {
653 try {
654 return std::stoll(
655 std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0")));
656 } catch (...) {
657 return 0;
658 }
659 }
660
661 s64 result = 0;
662 if (config->GetBoolValue(GetSection().c_str(),
663 std::string(full_key).append("\\default").c_str(), true)) {
664 result = default_value.value();
665 } else {
666 try {
667 result = std::stoll(std::string(config->GetValue(
668 GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str())));
669 } catch (...) {
670 result = default_value.value();
671 }
672 }
673 return result;
674}
675
676u64 Config::ReadUnsignedIntegerSetting(const std::string& key,
677 const std::optional<u64> default_value) {
678 std::string full_key = GetFullKey(key, false);
679 if (!default_value.has_value()) {
680 try {
681 return std::stoull(
682 std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0")));
683 } catch (...) {
684 return 0;
685 }
686 }
687
688 u64 result = 0;
689 if (config->GetBoolValue(GetSection().c_str(),
690 std::string(full_key).append("\\default").c_str(), true)) {
691 result = default_value.value();
692 } else {
693 try {
694 result = std::stoull(std::string(config->GetValue(
695 GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str())));
696 } catch (...) {
697 result = default_value.value();
698 }
699 }
700 return result;
701}
702
703double Config::ReadDoubleSetting(const std::string& key,
704 const std::optional<double> default_value) {
705 std::string full_key = GetFullKey(key, false);
706 if (!default_value.has_value()) {
707 return config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), 0);
708 }
709
710 double result;
711 if (config->GetBoolValue(GetSection().c_str(),
712 std::string(full_key).append("\\default").c_str(), true)) {
713 result = default_value.value();
714 } else {
715 result =
716 config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), default_value.value());
717 }
718 return result;
719}
720
721std::string Config::ReadStringSetting(const std::string& key,
722 const std::optional<std::string> default_value) {
723 std::string result;
724 std::string full_key = GetFullKey(key, false);
725 if (!default_value.has_value()) {
726 result = config->GetValue(GetSection().c_str(), full_key.c_str(), "");
727 boost::replace_all(result, "\"", "");
728 return result;
729 }
730
731 if (config->GetBoolValue(GetSection().c_str(),
732 std::string(full_key).append("\\default").c_str(), true)) {
733 result = default_value.value();
734 } else {
735 result =
736 config->GetValue(GetSection().c_str(), full_key.c_str(), default_value.value().c_str());
737 }
738 boost::replace_all(result, "\"", "");
739 boost::replace_all(result, "//", "/");
740 return result;
741}
742
743bool Config::Exists(const std::string& section, const std::string& key) const {
744 const std::string value = config->GetValue(section.c_str(), key.c_str(), "");
745 return !value.empty();
746}
747
748template <typename Type>
749void Config::WriteSetting(const std::string& key, const Type& value,
750 const std::optional<Type>& default_value,
751 const std::optional<bool>& use_global) {
752 std::string full_key = GetFullKey(key, false);
753
754 std::string saved_value;
755 std::string string_default;
756 if constexpr (std::is_same_v<Type, std::string>) {
757 saved_value.append(AdjustOutputString(value));
758 if (default_value.has_value()) {
759 string_default.append(AdjustOutputString(default_value.value()));
760 }
761 } else {
762 saved_value.append(AdjustOutputString(ToString(value)));
763 if (default_value.has_value()) {
764 string_default.append(ToString(default_value.value()));
765 }
766 }
767
768 if (default_value.has_value() && use_global.has_value()) {
769 if (!global) {
770 WriteSettingInternal(std::string(full_key).append("\\global"),
771 ToString(use_global.value()));
772 }
773 if (global || use_global.value() == false) {
774 WriteSettingInternal(std::string(full_key).append("\\default"),
775 ToString(string_default == saved_value));
776 WriteSettingInternal(full_key, saved_value);
777 }
778 } else if (default_value.has_value() && !use_global.has_value()) {
779 WriteSettingInternal(std::string(full_key).append("\\default"),
780 ToString(string_default == saved_value));
781 WriteSettingInternal(full_key, saved_value);
782 } else {
783 WriteSettingInternal(full_key, saved_value);
784 }
785}
786
787void Config::WriteSettingInternal(const std::string& key, const std::string& value) {
788 config->SetValue(GetSection().c_str(), key.c_str(), value.c_str());
789}
790
791void Config::Reload() {
792 ReadValues();
793 // To apply default value changes
794 SaveValues();
795}
796
797void Config::Save() {
798 SaveValues();
799}
800
801void Config::ClearControlPlayerValues() const {
802 // If key is an empty string, all keys in the current group() are removed.
803 const char* section = Settings::TranslateCategory(Settings::Category::Controls);
804 CSimpleIniA::TNamesDepend keys;
805 config->GetAllKeys(section, keys);
806 for (const auto& key : keys) {
807 if (std::string(config->GetValue(section, key.pItem)).empty()) {
808 config->Delete(section, key.pItem);
809 }
810 }
811}
812
813const std::string& Config::GetConfigFilePath() const {
814 return config_loc;
815}
816
817void Config::ReadCategory(const Settings::Category category) {
818 const auto& settings = FindRelevantList(category);
819 std::ranges::for_each(settings, [&](const auto& setting) { ReadSettingGeneric(setting); });
820}
821
822void Config::WriteCategory(const Settings::Category category) {
823 const auto& settings = FindRelevantList(category);
824 std::ranges::for_each(settings, [&](const auto& setting) { WriteSettingGeneric(setting); });
825}
826
827void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) {
828 if (!setting->Save() || (!setting->Switchable() && !global)) {
829 return;
830 }
831
832 const std::string key = AdjustKey(setting->GetLabel());
833 const std::string default_value(setting->DefaultToString());
834
835 bool use_global = true;
836 if (setting->Switchable() && !global) {
837 use_global =
838 ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true));
839 setting->SetGlobal(use_global);
840 }
841
842 if (global || !use_global) {
843 const bool is_default =
844 ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true));
845 if (!is_default) {
846 const std::string setting_string = ReadStringSetting(key, default_value);
847 setting->LoadString(setting_string);
848 } else {
849 // Empty string resets the Setting to default
850 setting->LoadString("");
851 }
852 }
853}
854
855void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) {
856 if (!setting->Save()) {
857 return;
858 }
859
860 std::string key = AdjustKey(setting->GetLabel());
861 if (setting->Switchable()) {
862 if (!global) {
863 WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
864 }
865 if (global || !setting->UsingGlobal()) {
866 WriteSetting(std::string(key).append("\\default"),
867 setting->ToString() == setting->DefaultToString());
868 WriteSetting(key, setting->ToString());
869 }
870 } else if (global) {
871 WriteSetting(std::string(key).append("\\default"),
872 setting->ToString() == setting->DefaultToString());
873 WriteSetting(key, setting->ToString());
874 }
875}
876
877void Config::BeginGroup(const std::string& group) {
878 // You can't begin a group while reading/writing from a config array
879 ASSERT(array_stack.empty());
880
881 key_stack.push_back(AdjustKey(group));
882}
883
884void Config::EndGroup() {
885 // You can't end a group if you haven't started one yet
886 ASSERT(!key_stack.empty());
887
888 // You can't end a group when reading/writing from a config array
889 ASSERT(array_stack.empty());
890
891 key_stack.pop_back();
892}
893
894std::string Config::GetSection() {
895 if (key_stack.empty()) {
896 return std::string{""};
897 }
898
899 return key_stack.front();
900}
901
902std::string Config::GetGroup() const {
903 if (key_stack.size() <= 1) {
904 return std::string{""};
905 }
906
907 std::string key;
908 for (size_t i = 1; i < key_stack.size(); ++i) {
909 key.append(key_stack[i]).append("\\");
910 }
911 return key;
912}
913
914std::string Config::AdjustKey(const std::string& key) {
915 std::string adjusted_key(key);
916 boost::replace_all(adjusted_key, "/", "\\");
917 boost::replace_all(adjusted_key, " ", "%20");
918 return adjusted_key;
919}
920
921std::string Config::AdjustOutputString(const std::string& string) {
922 std::string adjusted_string(string);
923 boost::replace_all(adjusted_string, "\\", "/");
924
925 // Windows requires that two forward slashes are used at the start of a path for unmapped
926 // network drives so we have to watch for that here
927#ifndef ANDROID
928 if (string.substr(0, 2) == "//") {
929 boost::replace_all(adjusted_string, "//", "/");
930 adjusted_string.insert(0, "/");
931 } else {
932 boost::replace_all(adjusted_string, "//", "/");
933 }
934#endif
935
936 // Needed for backwards compatibility with QSettings deserialization
937 for (const auto& special_character : special_characters) {
938 if (adjusted_string.find(special_character) != std::string::npos) {
939 adjusted_string.insert(0, "\"");
940 adjusted_string.append("\"");
941 break;
942 }
943 }
944 return adjusted_string;
945}
946
947std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) {
948 if (array_stack.empty()) {
949 return std::string(GetGroup()).append(AdjustKey(key));
950 }
951
952 std::string array_key;
953 for (size_t i = 0; i < array_stack.size(); ++i) {
954 if (!array_stack[i].name.empty()) {
955 array_key.append(array_stack[i].name).append("\\");
956 }
957
958 if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) {
959 array_key.append(ToString(array_stack[i].index)).append("\\");
960 }
961 }
962 std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key));
963 return final_key;
964}
965
966int Config::BeginArray(const std::string& array) {
967 array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0});
968 const int size = config->GetLongValue(GetSection().c_str(),
969 GetFullKey(std::string("size"), true).c_str(), 0);
970 array_stack.back().size = size;
971 return size;
972}
973
974void Config::EndArray() {
975 // You can't end a config array before starting one
976 ASSERT(!array_stack.empty());
977
978 // Set the array size to 0 if the array is ended without changing the index
979 int size = 0;
980 if (array_stack.back().index != 0) {
981 size = array_stack.back().size;
982 }
983
984 // Write out the size to config
985 if (key_stack.size() == 1 && array_stack.back().name.empty()) {
986 // Edge-case where the first array created doesn't have a name
987 config->SetValue(GetSection().c_str(), std::string("size").c_str(), ToString(size).c_str());
988 } else {
989 const auto key = GetFullKey(std::string("size"), true);
990 config->SetValue(GetSection().c_str(), key.c_str(), ToString(size).c_str());
991 }
992
993 array_stack.pop_back();
994}
995
996void Config::SetArrayIndex(const int index) {
997 // You can't set the array index if you haven't started one yet
998 ASSERT(!array_stack.empty());
999
1000 const int array_index = index + 1;
1001
1002 // You can't exceed the known max size of the array by more than 1
1003 ASSERT(array_stack.front().size + 1 >= array_index);
1004
1005 // Change the config array size to the current index since you may want
1006 // to reduce the number of elements that you read back from the config
1007 // in the future.
1008 array_stack.back().size = array_index;
1009 array_stack.back().index = array_index;
1010}
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h
new file mode 100644
index 000000000..b3812af17
--- /dev/null
+++ b/src/frontend_common/config.h
@@ -0,0 +1,211 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <string>
8#include "common/settings.h"
9
10#define SI_NO_CONVERSION
11#include <SimpleIni.h>
12#include <boost/algorithm/string/replace.hpp>
13
14// Workaround for conflicting definition in libloaderapi.h caused by SimpleIni
15#undef LoadString
16#undef CreateFile
17#undef DeleteFile
18#undef CopyFile
19#undef CreateDirectory
20#undef MoveFile
21
22namespace Core {
23class System;
24}
25
26class Config {
27public:
28 enum class ConfigType {
29 GlobalConfig,
30 PerGameConfig,
31 InputProfile,
32 };
33
34 virtual ~Config() = default;
35
36 void ClearControlPlayerValues() const;
37
38 [[nodiscard]] const std::string& GetConfigFilePath() const;
39
40 [[nodiscard]] bool Exists(const std::string& section, const std::string& key) const;
41
42protected:
43 explicit Config(ConfigType config_type = ConfigType::GlobalConfig);
44
45 void Initialize(const std::string& config_name = "config");
46 void Initialize(std::optional<std::string> config_path);
47
48 void WriteToIni() const;
49
50 void SetUpIni();
51 [[nodiscard]] bool IsCustomConfig() const;
52
53 void Reload();
54 void Save();
55
56 /**
57 * Derived config classes must implement this so they can reload all platform-specific
58 * values and global ones.
59 */
60 virtual void ReloadAllValues() = 0;
61
62 /**
63 * Derived config classes must implement this so they can save all platform-specific
64 * and global values.
65 */
66 virtual void SaveAllValues() = 0;
67
68 void ReadValues();
69 void ReadPlayerValues(std::size_t player_index);
70
71 void ReadTouchscreenValues();
72 void ReadMotionTouchValues();
73
74 // Read functions bases off the respective config section names.
75 void ReadAudioValues();
76 void ReadControlValues();
77 void ReadCoreValues();
78 void ReadDataStorageValues();
79 void ReadDebuggingValues();
80 void ReadServiceValues();
81 void ReadDisabledAddOnValues();
82 void ReadMiscellaneousValues();
83 void ReadCpuValues();
84 void ReadRendererValues();
85 void ReadScreenshotValues();
86 void ReadSystemValues();
87 void ReadWebServiceValues();
88 void ReadNetworkValues();
89
90 // Read platform specific sections
91 virtual void ReadHidbusValues() = 0;
92 virtual void ReadDebugControlValues() = 0;
93 virtual void ReadPathValues() = 0;
94 virtual void ReadShortcutValues() = 0;
95 virtual void ReadUIValues() = 0;
96 virtual void ReadUIGamelistValues() = 0;
97 virtual void ReadUILayoutValues() = 0;
98 virtual void ReadMultiplayerValues() = 0;
99
100 void SaveValues();
101 void SavePlayerValues(std::size_t player_index);
102 void SaveTouchscreenValues();
103 void SaveMotionTouchValues();
104
105 // Save functions based off the respective config section names.
106 void SaveAudioValues();
107 void SaveControlValues();
108 void SaveCoreValues();
109 void SaveDataStorageValues();
110 void SaveDebuggingValues();
111 void SaveNetworkValues();
112 void SaveDisabledAddOnValues();
113 void SaveMiscellaneousValues();
114 void SaveCpuValues();
115 void SaveRendererValues();
116 void SaveScreenshotValues();
117 void SaveSystemValues();
118 void SaveWebServiceValues();
119
120 // Save platform specific sections
121 virtual void SaveHidbusValues() = 0;
122 virtual void SaveDebugControlValues() = 0;
123 virtual void SavePathValues() = 0;
124 virtual void SaveShortcutValues() = 0;
125 virtual void SaveUIValues() = 0;
126 virtual void SaveUIGamelistValues() = 0;
127 virtual void SaveUILayoutValues() = 0;
128 virtual void SaveMultiplayerValues() = 0;
129
130 virtual std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) = 0;
131
132 /**
133 * Reads a setting from the qt_config.
134 *
135 * @param key The setting's identifier
136 * @param default_value The value to use when the setting is not already present in the config
137 */
138 bool ReadBooleanSetting(const std::string& key,
139 std::optional<bool> default_value = std::nullopt);
140 s64 ReadIntegerSetting(const std::string& key, std::optional<s64> default_value = std::nullopt);
141 u64 ReadUnsignedIntegerSetting(const std::string& key,
142 std::optional<u64> default_value = std::nullopt);
143 double ReadDoubleSetting(const std::string& key,
144 std::optional<double> default_value = std::nullopt);
145 std::string ReadStringSetting(const std::string& key,
146 std::optional<std::string> default_value = std::nullopt);
147
148 /**
149 * Writes a setting to the qt_config.
150 *
151 * @param key The setting's idetentifier
152 * @param value Value of the setting
153 * @param default_value Default of the setting if not present in config
154 * @param use_global Specifies if the custom or global config should be in use, for custom
155 * configs
156 */
157 template <typename Type = int>
158 void WriteSetting(const std::string& key, const Type& value,
159 const std::optional<Type>& default_value = std::nullopt,
160 const std::optional<bool>& use_global = std::nullopt);
161 void WriteSettingInternal(const std::string& key, const std::string& value);
162
163 void ReadCategory(Settings::Category category);
164 void WriteCategory(Settings::Category category);
165 void ReadSettingGeneric(Settings::BasicSetting* setting);
166 void WriteSettingGeneric(const Settings::BasicSetting* setting);
167
168 template <typename T>
169 [[nodiscard]] std::string ToString(const T& value_) {
170 if constexpr (std::is_same_v<T, std::string>) {
171 return value_;
172 } else if constexpr (std::is_same_v<T, std::optional<u32>>) {
173 return value_.has_value() ? std::to_string(*value_) : "none";
174 } else if constexpr (std::is_same_v<T, bool>) {
175 return value_ ? "true" : "false";
176 } else if constexpr (std::is_same_v<T, u64>) {
177 return std::to_string(static_cast<u64>(value_));
178 } else {
179 return std::to_string(static_cast<s64>(value_));
180 }
181 }
182
183 void BeginGroup(const std::string& group);
184 void EndGroup();
185 std::string GetSection();
186 [[nodiscard]] std::string GetGroup() const;
187 static std::string AdjustKey(const std::string& key);
188 static std::string AdjustOutputString(const std::string& string);
189 std::string GetFullKey(const std::string& key, bool skipArrayIndex);
190 int BeginArray(const std::string& array);
191 void EndArray();
192 void SetArrayIndex(int index);
193
194 const ConfigType type;
195 std::unique_ptr<CSimpleIniA> config;
196 std::string config_loc;
197 const bool global;
198
199private:
200 inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*',
201 '|', ';', '\'', '\"', ',', '<', '.',
202 '>', '?', '`', '~', '='};
203
204 struct ConfigArray {
205 std::string name;
206 int size;
207 int index;
208 };
209 std::vector<ConfigArray> array_stack;
210 std::vector<std::string> key_stack;
211};
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
index 2705ab140..9319ea007 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
@@ -5,6 +5,7 @@
5#include "shader_recompiler/backend/glasm/glasm_emit_context.h" 5#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
6#include "shader_recompiler/frontend/ir/program.h" 6#include "shader_recompiler/frontend/ir/program.h"
7#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
8#include "shader_recompiler/profile.h"
8#include "shader_recompiler/runtime_info.h" 9#include "shader_recompiler/runtime_info.h"
9 10
10namespace Shader::Backend::GLASM { 11namespace Shader::Backend::GLASM {
@@ -35,7 +36,9 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std
35 continue; 36 continue;
36 } 37 }
37 const auto& ssbo{ctx.info.storage_buffers_descriptors[index]}; 38 const auto& ssbo{ctx.info.storage_buffers_descriptors[index]};
38 ctx.Add("LDC.U64 DC.x,c{}[{}];" // ssbo_addr 39 const u64 ssbo_align_mask{~(ctx.profile.min_ssbo_alignment - 1U)};
40 ctx.Add("LDC.U64 DC.x,c{}[{}];" // unaligned_ssbo_addr
41 "AND.U64 DC.x,DC.x,{};" // ssbo_addr = unaligned_ssbo_addr & ssbo_align_mask
39 "LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32 42 "LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32
40 "CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32 43 "CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32
41 "ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size 44 "ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size
@@ -44,8 +47,8 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std
44 "AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b 47 "AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b
45 "IF NE.x;" // if cond 48 "IF NE.x;" // if cond
46 "SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr 49 "SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr
47 ssbo.cbuf_index, ssbo.cbuf_offset, ssbo.cbuf_index, ssbo.cbuf_offset + 8, address, 50 ssbo.cbuf_index, ssbo.cbuf_offset, ssbo_align_mask, ssbo.cbuf_index,
48 address, address); 51 ssbo.cbuf_offset + 8, address, address, address);
49 if (pointer_based) { 52 if (pointer_based) {
50 ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf 53 ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf
51 "ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset 54 "ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index 9ff4028c2..b2ceeefc4 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 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/div_ceil.h"
4#include "shader_recompiler/backend/bindings.h" 5#include "shader_recompiler/backend/bindings.h"
5#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
6#include "shader_recompiler/frontend/ir/program.h" 7#include "shader_recompiler/frontend/ir/program.h"
@@ -431,9 +432,11 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
431 } 432 }
432 for (const auto& desc : info.constant_buffer_descriptors) { 433 for (const auto& desc : info.constant_buffer_descriptors) {
433 const auto cbuf_type{profile.has_gl_cbuf_ftou_bug ? "uvec4" : "vec4"}; 434 const auto cbuf_type{profile.has_gl_cbuf_ftou_bug ? "uvec4" : "vec4"};
435 const u32 cbuf_used_size{Common::DivCeil(info.constant_buffer_used_sizes[desc.index], 16U)};
436 const u32 cbuf_binding_size{info.uses_global_memory ? 0x1000U : cbuf_used_size};
434 header += fmt::format("layout(std140,binding={}) uniform {}_cbuf_{}{{{} {}_cbuf{}[{}];}};", 437 header += fmt::format("layout(std140,binding={}) uniform {}_cbuf_{}{{{} {}_cbuf{}[{}];}};",
435 bindings.uniform_buffer, stage_name, desc.index, cbuf_type, 438 bindings.uniform_buffer, stage_name, desc.index, cbuf_type,
436 stage_name, desc.index, 4 * 1024); 439 stage_name, desc.index, cbuf_binding_size);
437 bindings.uniform_buffer += desc.count; 440 bindings.uniform_buffer += desc.count;
438 } 441 }
439} 442}
@@ -601,7 +604,10 @@ std::string EmitContext::DefineGlobalMemoryFunctions() {
601 addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc)); 604 addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc));
602 size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc)); 605 size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc));
603 } 606 }
604 const auto addr_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])}; 607 const u32 ssbo_align_mask{~(static_cast<u32>(profile.min_ssbo_alignment) - 1U)};
608 const auto aligned_low_addr{fmt::format("{}&{}", addr_xy[0], ssbo_align_mask)};
609 const auto aligned_addr{fmt::format("uvec2({},{})", aligned_low_addr, addr_xy[1])};
610 const auto addr_pack{fmt::format("packUint2x32({})", aligned_addr)};
605 const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)}; 611 const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)};
606 func += addr_statment; 612 func += addr_statment;
607 613
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 34592a01f..0031fa5fb 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -407,7 +407,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
407 } 407 }
408 ctx.AddCapability(spv::Capability::DemoteToHelperInvocation); 408 ctx.AddCapability(spv::Capability::DemoteToHelperInvocation);
409 } 409 }
410 if (info.stores[IR::Attribute::ViewportIndex]) { 410 if (info.stores[IR::Attribute::ViewportIndex] && profile.support_multi_viewport) {
411 ctx.AddCapability(spv::Capability::MultiViewport); 411 ctx.AddCapability(spv::Capability::MultiViewport);
412 } 412 }
413 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) { 413 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 1d77426e0..e5a78a914 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -84,6 +84,10 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
84 } 84 }
85 return std::nullopt; 85 return std::nullopt;
86 case IR::Attribute::ViewportIndex: 86 case IR::Attribute::ViewportIndex:
87 if (!ctx.profile.support_multi_viewport) {
88 LOG_WARNING(Shader, "Ignoring viewport index store on non-supporting driver");
89 return std::nullopt;
90 }
87 if (ctx.profile.support_viewport_index_layer_non_geometry || 91 if (ctx.profile.support_viewport_index_layer_non_geometry ||
88 ctx.stage == Shader::Stage::Geometry) { 92 ctx.stage == Shader::Stage::Geometry) {
89 return OutAttr{ctx.viewport_index, ctx.U32[1]}; 93 return OutAttr{ctx.viewport_index, ctx.U32[1]};
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 57df6fc34..3350f1f85 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -891,7 +891,9 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
891 const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, 891 const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32,
892 zero, ssbo_size_cbuf_offset)}; 892 zero, ssbo_size_cbuf_offset)};
893 893
894 const Id ssbo_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; 894 const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)};
895 const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))};
896 const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))};
895 const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; 897 const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))};
896 const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; 898 const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)};
897 const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), 899 const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr),
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 8fac6bad3..321ea625b 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -298,7 +298,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
298 298
299 Optimization::PositionPass(env, program); 299 Optimization::PositionPass(env, program);
300 300
301 Optimization::GlobalMemoryToStorageBufferPass(program); 301 Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
302 Optimization::TexturePass(env, program, host_info); 302 Optimization::TexturePass(env, program, host_info);
303 303
304 if (Settings::values.resolution_info.active) { 304 if (Settings::values.resolution_info.active) {
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index 7d2ded907..1b53404fc 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -16,6 +16,7 @@ struct HostTranslateInfo {
16 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered 16 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
17 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers 17 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
18 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS 18 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
19 u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
19 bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry 20 bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
20 ///< passthrough shaders 21 ///< passthrough shaders
21 bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional 22 bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
index d1e59f22e..0cea79945 100644
--- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
+++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
@@ -11,6 +11,7 @@
11#include "shader_recompiler/frontend/ir/breadth_first_search.h" 11#include "shader_recompiler/frontend/ir/breadth_first_search.h"
12#include "shader_recompiler/frontend/ir/ir_emitter.h" 12#include "shader_recompiler/frontend/ir/ir_emitter.h"
13#include "shader_recompiler/frontend/ir/value.h" 13#include "shader_recompiler/frontend/ir/value.h"
14#include "shader_recompiler/host_translate_info.h"
14#include "shader_recompiler/ir_opt/passes.h" 15#include "shader_recompiler/ir_opt/passes.h"
15 16
16namespace Shader::Optimization { 17namespace Shader::Optimization {
@@ -408,7 +409,7 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info)
408} 409}
409 410
410/// Returns the offset in indices (not bytes) for an equivalent storage instruction 411/// Returns the offset in indices (not bytes) for an equivalent storage instruction
411IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) { 412IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer, u32 alignment) {
412 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; 413 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
413 IR::U32 offset; 414 IR::U32 offset;
414 if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) { 415 if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) {
@@ -421,7 +422,10 @@ IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer
421 } 422 }
422 // Subtract the least significant 32 bits from the guest offset. The result is the storage 423 // Subtract the least significant 32 bits from the guest offset. The result is the storage
423 // buffer offset in bytes. 424 // buffer offset in bytes.
424 const IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; 425 IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))};
426
427 // Align the offset base to match the host alignment requirements
428 low_cbuf = ir.BitwiseAnd(low_cbuf, ir.Imm32(~(alignment - 1U)));
425 return ir.ISub(offset, low_cbuf); 429 return ir.ISub(offset, low_cbuf);
426} 430}
427 431
@@ -516,7 +520,7 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index,
516} 520}
517} // Anonymous namespace 521} // Anonymous namespace
518 522
519void GlobalMemoryToStorageBufferPass(IR::Program& program) { 523void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) {
520 StorageInfo info; 524 StorageInfo info;
521 for (IR::Block* const block : program.post_order_blocks) { 525 for (IR::Block* const block : program.post_order_blocks) {
522 for (IR::Inst& inst : block->Instructions()) { 526 for (IR::Inst& inst : block->Instructions()) {
@@ -540,7 +544,8 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program) {
540 const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}}; 544 const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}};
541 IR::Block* const block{storage_inst.block}; 545 IR::Block* const block{storage_inst.block};
542 IR::Inst* const inst{storage_inst.inst}; 546 IR::Inst* const inst{storage_inst.inst};
543 const IR::U32 offset{StorageOffset(*block, *inst, storage_buffer)}; 547 const IR::U32 offset{
548 StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)};
544 Replace(*block, *inst, index, offset); 549 Replace(*block, *inst, index, offset);
545 } 550 }
546} 551}
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index d4d5285e5..1e637cb23 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -16,7 +16,7 @@ void CollectShaderInfoPass(Environment& env, IR::Program& program);
16void ConditionalBarrierPass(IR::Program& program); 16void ConditionalBarrierPass(IR::Program& program);
17void ConstantPropagationPass(Environment& env, IR::Program& program); 17void ConstantPropagationPass(Environment& env, IR::Program& program);
18void DeadCodeEliminationPass(IR::Program& program); 18void DeadCodeEliminationPass(IR::Program& program);
19void GlobalMemoryToStorageBufferPass(IR::Program& program); 19void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info);
20void IdentityRemovalPass(IR::Program& program); 20void IdentityRemovalPass(IR::Program& program);
21void LowerFp64ToFp32(IR::Program& program); 21void LowerFp64ToFp32(IR::Program& program);
22void LowerFp16ToFp32(IR::Program& program); 22void LowerFp16ToFp32(IR::Program& program);
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 38d820db2..66901a965 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -43,6 +43,7 @@ struct Profile {
43 bool support_gl_sparse_textures{}; 43 bool support_gl_sparse_textures{};
44 bool support_gl_derivative_control{}; 44 bool support_gl_derivative_control{};
45 bool support_scaled_attributes{}; 45 bool support_scaled_attributes{};
46 bool support_multi_viewport{};
46 47
47 bool warp_size_potentially_larger_than_guest{}; 48 bool warp_size_potentially_larger_than_guest{};
48 49
@@ -84,6 +85,8 @@ struct Profile {
84 85
85 /// Maxwell and earlier nVidia architectures have broken robust support 86 /// Maxwell and earlier nVidia architectures have broken robust support
86 bool has_broken_robust{}; 87 bool has_broken_robust{};
88
89 u64 min_ssbo_alignment{};
87}; 90};
88 91
89} // namespace Shader 92} // namespace Shader
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp
index 1b014b632..1a28e862b 100644
--- a/src/tests/common/host_memory.cpp
+++ b/src/tests/common/host_memory.cpp
@@ -11,6 +11,7 @@ using namespace Common::Literals;
11 11
12static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; 12static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
13static constexpr size_t BACKING_SIZE = 4_GiB; 13static constexpr size_t BACKING_SIZE = 4_GiB;
14static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
14 15
15TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { 16TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
16 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } 17 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
@@ -19,7 +20,7 @@ TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
19 20
20TEST_CASE("HostMemory: Simple map", "[common]") { 21TEST_CASE("HostMemory: Simple map", "[common]") {
21 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 22 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
22 mem.Map(0x5000, 0x8000, 0x1000); 23 mem.Map(0x5000, 0x8000, 0x1000, PERMS);
23 24
24 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 25 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
25 data[0] = 50; 26 data[0] = 50;
@@ -28,8 +29,8 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
28 29
29TEST_CASE("HostMemory: Simple mirror map", "[common]") { 30TEST_CASE("HostMemory: Simple mirror map", "[common]") {
30 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 31 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
31 mem.Map(0x5000, 0x3000, 0x2000); 32 mem.Map(0x5000, 0x3000, 0x2000, PERMS);
32 mem.Map(0x8000, 0x4000, 0x1000); 33 mem.Map(0x8000, 0x4000, 0x1000, PERMS);
33 34
34 volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000; 35 volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000;
35 volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000; 36 volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000;
@@ -39,7 +40,7 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
39 40
40TEST_CASE("HostMemory: Simple unmap", "[common]") { 41TEST_CASE("HostMemory: Simple unmap", "[common]") {
41 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 42 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
42 mem.Map(0x5000, 0x3000, 0x2000); 43 mem.Map(0x5000, 0x3000, 0x2000, PERMS);
43 44
44 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 45 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
45 data[75] = 50; 46 data[75] = 50;
@@ -50,7 +51,7 @@ TEST_CASE("HostMemory: Simple unmap", "[common]") {
50 51
51TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { 52TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
52 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 53 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
53 mem.Map(0x5000, 0x3000, 0x2000); 54 mem.Map(0x5000, 0x3000, 0x2000, PERMS);
54 55
55 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 56 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
56 data[0] = 50; 57 data[0] = 50;
@@ -58,79 +59,79 @@ TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
58 59
59 mem.Unmap(0x5000, 0x2000); 60 mem.Unmap(0x5000, 0x2000);
60 61
61 mem.Map(0x5000, 0x3000, 0x2000); 62 mem.Map(0x5000, 0x3000, 0x2000, PERMS);
62 REQUIRE(data[0] == 50); 63 REQUIRE(data[0] == 50);
63 64
64 mem.Map(0x7000, 0x2000, 0x5000); 65 mem.Map(0x7000, 0x2000, 0x5000, PERMS);
65 REQUIRE(data[0x3000] == 50); 66 REQUIRE(data[0x3000] == 50);
66} 67}
67 68
68TEST_CASE("HostMemory: Nieche allocation", "[common]") { 69TEST_CASE("HostMemory: Nieche allocation", "[common]") {
69 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 70 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
70 mem.Map(0x0000, 0, 0x20000); 71 mem.Map(0x0000, 0, 0x20000, PERMS);
71 mem.Unmap(0x0000, 0x4000); 72 mem.Unmap(0x0000, 0x4000);
72 mem.Map(0x1000, 0, 0x2000); 73 mem.Map(0x1000, 0, 0x2000, PERMS);
73 mem.Map(0x3000, 0, 0x1000); 74 mem.Map(0x3000, 0, 0x1000, PERMS);
74 mem.Map(0, 0, 0x1000); 75 mem.Map(0, 0, 0x1000, PERMS);
75} 76}
76 77
77TEST_CASE("HostMemory: Full unmap", "[common]") { 78TEST_CASE("HostMemory: Full unmap", "[common]") {
78 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 79 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
79 mem.Map(0x8000, 0, 0x4000); 80 mem.Map(0x8000, 0, 0x4000, PERMS);
80 mem.Unmap(0x8000, 0x4000); 81 mem.Unmap(0x8000, 0x4000);
81 mem.Map(0x6000, 0, 0x16000); 82 mem.Map(0x6000, 0, 0x16000, PERMS);
82} 83}
83 84
84TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { 85TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
85 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 86 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
86 mem.Map(0x0000, 0, 0x4000); 87 mem.Map(0x0000, 0, 0x4000, PERMS);
87 mem.Unmap(0x2000, 0x4000); 88 mem.Unmap(0x2000, 0x4000);
88 mem.Map(0x2000, 0x80000, 0x4000); 89 mem.Map(0x2000, 0x80000, 0x4000, PERMS);
89} 90}
90 91
91TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { 92TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
92 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 93 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
93 mem.Map(0x8000, 0, 0x4000); 94 mem.Map(0x8000, 0, 0x4000, PERMS);
94 mem.Unmap(0x6000, 0x4000); 95 mem.Unmap(0x6000, 0x4000);
95 mem.Map(0x8000, 0, 0x2000); 96 mem.Map(0x8000, 0, 0x2000, PERMS);
96} 97}
97 98
98TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { 99TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
99 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 100 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
100 mem.Map(0x0000, 0, 0x4000); 101 mem.Map(0x0000, 0, 0x4000, PERMS);
101 mem.Map(0x4000, 0, 0x1b000); 102 mem.Map(0x4000, 0, 0x1b000, PERMS);
102 mem.Unmap(0x3000, 0x1c000); 103 mem.Unmap(0x3000, 0x1c000);
103 mem.Map(0x3000, 0, 0x20000); 104 mem.Map(0x3000, 0, 0x20000, PERMS);
104} 105}
105 106
106TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { 107TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
107 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 108 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
108 mem.Map(0x0000, 0, 0x4000); 109 mem.Map(0x0000, 0, 0x4000, PERMS);
109 mem.Map(0x4000, 0, 0x4000); 110 mem.Map(0x4000, 0, 0x4000, PERMS);
110 mem.Unmap(0x2000, 0x4000); 111 mem.Unmap(0x2000, 0x4000);
111 mem.Map(0x2000, 0, 0x4000); 112 mem.Map(0x2000, 0, 0x4000, PERMS);
112} 113}
113 114
114TEST_CASE("HostMemory: Unmap to origin", "[common]") { 115TEST_CASE("HostMemory: Unmap to origin", "[common]") {
115 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 116 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
116 mem.Map(0x4000, 0, 0x4000); 117 mem.Map(0x4000, 0, 0x4000, PERMS);
117 mem.Map(0x8000, 0, 0x4000); 118 mem.Map(0x8000, 0, 0x4000, PERMS);
118 mem.Unmap(0x4000, 0x4000); 119 mem.Unmap(0x4000, 0x4000);
119 mem.Map(0, 0, 0x4000); 120 mem.Map(0, 0, 0x4000, PERMS);
120 mem.Map(0x4000, 0, 0x4000); 121 mem.Map(0x4000, 0, 0x4000, PERMS);
121} 122}
122 123
123TEST_CASE("HostMemory: Unmap to right", "[common]") { 124TEST_CASE("HostMemory: Unmap to right", "[common]") {
124 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 125 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
125 mem.Map(0x4000, 0, 0x4000); 126 mem.Map(0x4000, 0, 0x4000, PERMS);
126 mem.Map(0x8000, 0, 0x4000); 127 mem.Map(0x8000, 0, 0x4000, PERMS);
127 mem.Unmap(0x8000, 0x4000); 128 mem.Unmap(0x8000, 0x4000);
128 mem.Map(0x8000, 0, 0x4000); 129 mem.Map(0x8000, 0, 0x4000, PERMS);
129} 130}
130 131
131TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { 132TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
132 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 133 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
133 mem.Map(0x4000, 0x10000, 0x4000); 134 mem.Map(0x4000, 0x10000, 0x4000, PERMS);
134 135
135 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 136 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
136 ptr[0x1000] = 17; 137 ptr[0x1000] = 17;
@@ -142,7 +143,7 @@ TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
142 143
143TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { 144TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
144 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 145 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
145 mem.Map(0x4000, 0x10000, 0x4000); 146 mem.Map(0x4000, 0x10000, 0x4000, PERMS);
146 147
147 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 148 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
148 ptr[0x3000] = 19; 149 ptr[0x3000] = 19;
@@ -156,7 +157,7 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
156 157
157TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { 158TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
158 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 159 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
159 mem.Map(0x4000, 0x10000, 0x4000); 160 mem.Map(0x4000, 0x10000, 0x4000, PERMS);
160 161
161 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 162 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
162 ptr[0x0000] = 19; 163 ptr[0x0000] = 19;
@@ -170,8 +171,8 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
170 171
171TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { 172TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
172 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 173 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
173 mem.Map(0x4000, 0x10000, 0x2000); 174 mem.Map(0x4000, 0x10000, 0x2000, PERMS);
174 mem.Map(0x6000, 0x20000, 0x2000); 175 mem.Map(0x6000, 0x20000, 0x2000, PERMS);
175 176
176 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 177 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
177 ptr[0x0000] = 19; 178 ptr[0x0000] = 19;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index cf9266d54..c22c7631c 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -4,7 +4,7 @@
4add_subdirectory(host_shaders) 4add_subdirectory(host_shaders)
5 5
6if(LIBVA_FOUND) 6if(LIBVA_FOUND)
7 set_source_files_properties(host1x/codecs/codec.cpp 7 set_source_files_properties(host1x/ffmpeg/ffmpeg.cpp
8 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) 8 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
9 list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES}) 9 list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
10endif() 10endif()
@@ -15,6 +15,7 @@ add_library(video_core STATIC
15 buffer_cache/buffer_cache.cpp 15 buffer_cache/buffer_cache.cpp
16 buffer_cache/buffer_cache.h 16 buffer_cache/buffer_cache.h
17 buffer_cache/memory_tracker_base.h 17 buffer_cache/memory_tracker_base.h
18 buffer_cache/usage_tracker.h
18 buffer_cache/word_manager.h 19 buffer_cache/word_manager.h
19 cache_types.h 20 cache_types.h
20 cdma_pusher.cpp 21 cdma_pusher.cpp
@@ -66,6 +67,8 @@ add_library(video_core STATIC
66 host1x/codecs/vp9.cpp 67 host1x/codecs/vp9.cpp
67 host1x/codecs/vp9.h 68 host1x/codecs/vp9.h
68 host1x/codecs/vp9_types.h 69 host1x/codecs/vp9_types.h
70 host1x/ffmpeg/ffmpeg.cpp
71 host1x/ffmpeg/ffmpeg.h
69 host1x/control.cpp 72 host1x/control.cpp
70 host1x/control.h 73 host1x/control.h
71 host1x/host1x.cpp 74 host1x/host1x.cpp
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index f5b10411b..6d1fc3887 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -67,6 +67,7 @@ void BufferCache<P>::TickFrame() {
67 if (!channel_state) { 67 if (!channel_state) {
68 return; 68 return;
69 } 69 }
70 runtime.TickFrame(slot_buffers);
70 71
71 // Calculate hits and shots and move hit bits to the right 72 // Calculate hits and shots and move hit bits to the right
72 const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(), 73 const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(),
@@ -230,7 +231,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
230 for (const IntervalType& add_interval : tmp_intervals) { 231 for (const IntervalType& add_interval : tmp_intervals) {
231 common_ranges.add(add_interval); 232 common_ranges.add(add_interval);
232 } 233 }
233 runtime.CopyBuffer(dest_buffer, src_buffer, copies); 234 const auto& copy = copies[0];
235 src_buffer.MarkUsage(copy.src_offset, copy.size);
236 dest_buffer.MarkUsage(copy.dst_offset, copy.size);
237 runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
234 if (has_new_downloads) { 238 if (has_new_downloads) {
235 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); 239 memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
236 } 240 }
@@ -258,9 +262,10 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
258 common_ranges.subtract(subtract_interval); 262 common_ranges.subtract(subtract_interval);
259 263
260 const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); 264 const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
261 auto& dest_buffer = slot_buffers[buffer]; 265 Buffer& dest_buffer = slot_buffers[buffer];
262 const u32 offset = dest_buffer.Offset(*cpu_dst_address); 266 const u32 offset = dest_buffer.Offset(*cpu_dst_address);
263 runtime.ClearBuffer(dest_buffer, offset, size, value); 267 runtime.ClearBuffer(dest_buffer, offset, size, value);
268 dest_buffer.MarkUsage(offset, size);
264 return true; 269 return true;
265} 270}
266 271
@@ -603,6 +608,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
603 VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset); 608 VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
604 const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size}; 609 const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
605 async_downloads += std::make_pair(base_interval, 1); 610 async_downloads += std::make_pair(base_interval, 1);
611 buffer.MarkUsage(copy.src_offset, copy.size);
606 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); 612 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
607 normalized_copies.push_back(second_copy); 613 normalized_copies.push_back(second_copy);
608 } 614 }
@@ -621,8 +627,9 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
621 // Have in mind the staging buffer offset for the copy 627 // Have in mind the staging buffer offset for the copy
622 copy.dst_offset += download_staging.offset; 628 copy.dst_offset += download_staging.offset;
623 const std::array copies{copy}; 629 const std::array copies{copy};
624 runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, 630 Buffer& buffer = slot_buffers[buffer_id];
625 false); 631 buffer.MarkUsage(copy.src_offset, copy.size);
632 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
626 } 633 }
627 runtime.PostCopyBarrier(); 634 runtime.PostCopyBarrier();
628 runtime.Finish(); 635 runtime.Finish();
@@ -742,7 +749,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
742 {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}}; 749 {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
743 std::memcpy(upload_staging.mapped_span.data(), 750 std::memcpy(upload_staging.mapped_span.data(),
744 draw_state.inline_index_draw_indexes.data(), size); 751 draw_state.inline_index_draw_indexes.data(), size);
745 runtime.CopyBuffer(buffer, upload_staging.buffer, copies); 752 runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true);
746 } else { 753 } else {
747 buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); 754 buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes);
748 } 755 }
@@ -754,6 +761,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
754 offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); 761 offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
755 runtime.BindIndexBuffer(buffer, new_offset, size); 762 runtime.BindIndexBuffer(buffer, new_offset, size);
756 } else { 763 } else {
764 buffer.MarkUsage(offset, size);
757 runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, 765 runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
758 draw_state.index_buffer.first, draw_state.index_buffer.count, 766 draw_state.index_buffer.first, draw_state.index_buffer.count,
759 buffer, offset, size); 767 buffer, offset, size);
@@ -790,6 +798,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
790 798
791 const u32 stride = maxwell3d->regs.vertex_streams[index].stride; 799 const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
792 const u32 offset = buffer.Offset(binding.cpu_addr); 800 const u32 offset = buffer.Offset(binding.cpu_addr);
801 buffer.MarkUsage(offset, binding.size);
793 802
794 host_bindings.buffers.push_back(&buffer); 803 host_bindings.buffers.push_back(&buffer);
795 host_bindings.offsets.push_back(offset); 804 host_bindings.offsets.push_back(offset);
@@ -895,6 +904,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
895 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { 904 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
896 channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; 905 channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
897 } 906 }
907 buffer.MarkUsage(offset, size);
898 if constexpr (NEEDS_BIND_UNIFORM_INDEX) { 908 if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
899 runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size); 909 runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size);
900 } else { 910 } else {
@@ -913,6 +923,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
913 SynchronizeBuffer(buffer, binding.cpu_addr, size); 923 SynchronizeBuffer(buffer, binding.cpu_addr, size);
914 924
915 const u32 offset = buffer.Offset(binding.cpu_addr); 925 const u32 offset = buffer.Offset(binding.cpu_addr);
926 buffer.MarkUsage(offset, size);
916 const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; 927 const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
917 928
918 if (is_written) { 929 if (is_written) {
@@ -943,6 +954,7 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
943 954
944 const u32 offset = buffer.Offset(binding.cpu_addr); 955 const u32 offset = buffer.Offset(binding.cpu_addr);
945 const PixelFormat format = binding.format; 956 const PixelFormat format = binding.format;
957 buffer.MarkUsage(offset, size);
946 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { 958 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
947 if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) { 959 if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) {
948 runtime.BindImageBuffer(buffer, offset, size, format); 960 runtime.BindImageBuffer(buffer, offset, size, format);
@@ -975,9 +987,10 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
975 MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); 987 MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size);
976 988
977 const u32 offset = buffer.Offset(binding.cpu_addr); 989 const u32 offset = buffer.Offset(binding.cpu_addr);
990 buffer.MarkUsage(offset, size);
978 host_bindings.buffers.push_back(&buffer); 991 host_bindings.buffers.push_back(&buffer);
979 host_bindings.offsets.push_back(offset); 992 host_bindings.offsets.push_back(offset);
980 host_bindings.sizes.push_back(binding.size); 993 host_bindings.sizes.push_back(size);
981 } 994 }
982 if (host_bindings.buffers.size() > 0) { 995 if (host_bindings.buffers.size() > 0) {
983 runtime.BindTransformFeedbackBuffers(host_bindings); 996 runtime.BindTransformFeedbackBuffers(host_bindings);
@@ -1001,6 +1014,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
1001 SynchronizeBuffer(buffer, binding.cpu_addr, size); 1014 SynchronizeBuffer(buffer, binding.cpu_addr, size);
1002 1015
1003 const u32 offset = buffer.Offset(binding.cpu_addr); 1016 const u32 offset = buffer.Offset(binding.cpu_addr);
1017 buffer.MarkUsage(offset, size);
1004 if constexpr (NEEDS_BIND_UNIFORM_INDEX) { 1018 if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
1005 runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size); 1019 runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size);
1006 ++binding_index; 1020 ++binding_index;
@@ -1021,6 +1035,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
1021 SynchronizeBuffer(buffer, binding.cpu_addr, size); 1035 SynchronizeBuffer(buffer, binding.cpu_addr, size);
1022 1036
1023 const u32 offset = buffer.Offset(binding.cpu_addr); 1037 const u32 offset = buffer.Offset(binding.cpu_addr);
1038 buffer.MarkUsage(offset, size);
1024 const bool is_written = 1039 const bool is_written =
1025 ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; 1040 ((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
1026 1041
@@ -1053,6 +1068,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
1053 1068
1054 const u32 offset = buffer.Offset(binding.cpu_addr); 1069 const u32 offset = buffer.Offset(binding.cpu_addr);
1055 const PixelFormat format = binding.format; 1070 const PixelFormat format = binding.format;
1071 buffer.MarkUsage(offset, size);
1056 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { 1072 if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
1057 if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) { 1073 if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) {
1058 runtime.BindImageBuffer(buffer, offset, size, format); 1074 runtime.BindImageBuffer(buffer, offset, size, format);
@@ -1172,10 +1188,11 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1172 if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { 1188 if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) {
1173 size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); 1189 size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
1174 } 1190 }
1191 const BufferId buffer_id = FindBuffer(*cpu_addr, size);
1175 channel_state->vertex_buffers[index] = Binding{ 1192 channel_state->vertex_buffers[index] = Binding{
1176 .cpu_addr = *cpu_addr, 1193 .cpu_addr = *cpu_addr,
1177 .size = size, 1194 .size = size,
1178 .buffer_id = FindBuffer(*cpu_addr, size), 1195 .buffer_id = buffer_id,
1179 }; 1196 };
1180} 1197}
1181 1198
@@ -1401,7 +1418,8 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
1401 .dst_offset = dst_base_offset, 1418 .dst_offset = dst_base_offset,
1402 .size = overlap.SizeBytes(), 1419 .size = overlap.SizeBytes(),
1403 }); 1420 });
1404 runtime.CopyBuffer(new_buffer, overlap, copies); 1421 new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size);
1422 runtime.CopyBuffer(new_buffer, overlap, copies, true);
1405 DeleteBuffer(overlap_id, true); 1423 DeleteBuffer(overlap_id, true);
1406} 1424}
1407 1425
@@ -1414,7 +1432,9 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
1414 const u32 size = static_cast<u32>(overlap.end - overlap.begin); 1432 const u32 size = static_cast<u32>(overlap.end - overlap.begin);
1415 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); 1433 const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
1416 auto& new_buffer = slot_buffers[new_buffer_id]; 1434 auto& new_buffer = slot_buffers[new_buffer_id];
1417 runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0); 1435 const size_t size_bytes = new_buffer.SizeBytes();
1436 runtime.ClearBuffer(new_buffer, 0, size_bytes, 0);
1437 new_buffer.MarkUsage(0, size_bytes);
1418 for (const BufferId overlap_id : overlap.ids) { 1438 for (const BufferId overlap_id : overlap.ids) {
1419 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); 1439 JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
1420 } 1440 }
@@ -1467,11 +1487,6 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
1467 1487
1468template <class P> 1488template <class P>
1469bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { 1489bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
1470 return SynchronizeBufferImpl(buffer, cpu_addr, size);
1471}
1472
1473template <class P>
1474bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
1475 boost::container::small_vector<BufferCopy, 4> copies; 1490 boost::container::small_vector<BufferCopy, 4> copies;
1476 u64 total_size_bytes = 0; 1491 u64 total_size_bytes = 0;
1477 u64 largest_copy = 0; 1492 u64 largest_copy = 0;
@@ -1494,51 +1509,6 @@ bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
1494} 1509}
1495 1510
1496template <class P> 1511template <class P>
1497bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) {
1498 boost::container::small_vector<BufferCopy, 4> copies;
1499 u64 total_size_bytes = 0;
1500 u64 largest_copy = 0;
1501 IntervalSet found_sets{};
1502 auto make_copies = [&] {
1503 for (auto& interval : found_sets) {
1504 const std::size_t sub_size = interval.upper() - interval.lower();
1505 const VAddr cpu_addr_ = interval.lower();
1506 copies.push_back(BufferCopy{
1507 .src_offset = total_size_bytes,
1508 .dst_offset = cpu_addr_ - buffer.CpuAddr(),
1509 .size = sub_size,
1510 });
1511 total_size_bytes += sub_size;
1512 largest_copy = std::max<u64>(largest_copy, sub_size);
1513 }
1514 const std::span<BufferCopy> copies_span(copies.data(), copies.size());
1515 UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
1516 };
1517 memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
1518 const VAddr base_adr = cpu_addr_out;
1519 const VAddr end_adr = base_adr + range_size;
1520 const IntervalType add_interval{base_adr, end_adr};
1521 found_sets.add(add_interval);
1522 });
1523 if (found_sets.empty()) {
1524 return true;
1525 }
1526 const IntervalType search_interval{cpu_addr, cpu_addr + size};
1527 auto it = common_ranges.lower_bound(search_interval);
1528 auto it_end = common_ranges.upper_bound(search_interval);
1529 if (it == common_ranges.end()) {
1530 make_copies();
1531 return false;
1532 }
1533 while (it != it_end) {
1534 found_sets.subtract(*it);
1535 it++;
1536 }
1537 make_copies();
1538 return false;
1539}
1540
1541template <class P>
1542void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, 1512void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
1543 std::span<BufferCopy> copies) { 1513 std::span<BufferCopy> copies) {
1544 if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { 1514 if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
@@ -1586,7 +1556,8 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
1586 // Apply the staging offset 1556 // Apply the staging offset
1587 copy.src_offset += upload_staging.offset; 1557 copy.src_offset += upload_staging.offset;
1588 } 1558 }
1589 runtime.CopyBuffer(buffer, upload_staging.buffer, copies); 1559 const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
1560 runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
1590 } 1561 }
1591} 1562}
1592 1563
@@ -1628,7 +1599,8 @@ void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_
1628 }}; 1599 }};
1629 u8* const src_pointer = upload_staging.mapped_span.data(); 1600 u8* const src_pointer = upload_staging.mapped_span.data();
1630 std::memcpy(src_pointer, inlined_buffer.data(), copy_size); 1601 std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
1631 runtime.CopyBuffer(buffer, upload_staging.buffer, copies); 1602 const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
1603 runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
1632 } else { 1604 } else {
1633 buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); 1605 buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size));
1634 } 1606 }
@@ -1681,8 +1653,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
1681 for (BufferCopy& copy : copies) { 1653 for (BufferCopy& copy : copies) {
1682 // Modify copies to have the staging offset in mind 1654 // Modify copies to have the staging offset in mind
1683 copy.dst_offset += download_staging.offset; 1655 copy.dst_offset += download_staging.offset;
1656 buffer.MarkUsage(copy.src_offset, copy.size);
1684 } 1657 }
1685 runtime.CopyBuffer(download_staging.buffer, buffer, copies_span); 1658 runtime.CopyBuffer(download_staging.buffer, buffer, copies_span, true);
1686 runtime.Finish(); 1659 runtime.Finish();
1687 for (const BufferCopy& copy : copies) { 1660 for (const BufferCopy& copy : copies) {
1688 const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset; 1661 const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
@@ -1780,15 +1753,25 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
1780 const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr)); 1753 const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr));
1781 return std::min(memory_layout_size, static_cast<u32>(8_MiB)); 1754 return std::min(memory_layout_size, static_cast<u32>(8_MiB));
1782 }(); 1755 }();
1783 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); 1756 // Alignment only applies to the offset of the buffer
1784 if (!cpu_addr || size == 0) { 1757 const u32 alignment = runtime.GetStorageBufferAlignment();
1758 const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment);
1759 const u32 aligned_size = static_cast<u32>(gpu_addr - aligned_gpu_addr) + size;
1760
1761 const std::optional<VAddr> aligned_cpu_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr);
1762 if (!aligned_cpu_addr || size == 0) {
1785 LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index); 1763 LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index);
1786 return NULL_BINDING; 1764 return NULL_BINDING;
1787 } 1765 }
1788 const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, YUZU_PAGESIZE); 1766 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
1767 ASSERT_MSG(cpu_addr, "Unaligned storage buffer address not found for cbuf index {}",
1768 cbuf_index);
1769 // The end address used for size calculation does not need to be aligned
1770 const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE);
1771
1789 const Binding binding{ 1772 const Binding binding{
1790 .cpu_addr = *cpu_addr, 1773 .cpu_addr = *aligned_cpu_addr,
1791 .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr), 1774 .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_cpu_addr),
1792 .buffer_id = BufferId{}, 1775 .buffer_id = BufferId{},
1793 }; 1776 };
1794 return binding; 1777 return binding;
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index eed267361..d6d696d8c 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -529,10 +529,6 @@ private:
529 529
530 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); 530 bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
531 531
532 bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
533
534 bool SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size);
535
536 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, 532 void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
537 std::span<BufferCopy> copies); 533 std::span<BufferCopy> copies);
538 534
diff --git a/src/video_core/buffer_cache/usage_tracker.h b/src/video_core/buffer_cache/usage_tracker.h
new file mode 100644
index 000000000..5f8688d31
--- /dev/null
+++ b/src/video_core/buffer_cache/usage_tracker.h
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/alignment.h"
7#include "common/common_types.h"
8
9namespace VideoCommon {
10
11class UsageTracker {
12 static constexpr size_t BYTES_PER_BIT_SHIFT = 6;
13 static constexpr size_t PAGE_SHIFT = 6 + BYTES_PER_BIT_SHIFT;
14 static constexpr size_t PAGE_BYTES = 1 << PAGE_SHIFT;
15
16public:
17 explicit UsageTracker(size_t size) {
18 const size_t num_pages = (size >> PAGE_SHIFT) + 1;
19 pages.resize(num_pages, 0ULL);
20 }
21
22 void Reset() noexcept {
23 std::ranges::fill(pages, 0ULL);
24 }
25
26 void Track(u64 offset, u64 size) noexcept {
27 const size_t page = offset >> PAGE_SHIFT;
28 const size_t page_end = (offset + size) >> PAGE_SHIFT;
29 TrackPage(page, offset, size);
30 if (page == page_end) {
31 return;
32 }
33 for (size_t i = page + 1; i < page_end; i++) {
34 pages[i] = ~u64{0};
35 }
36 const size_t offset_end = offset + size;
37 const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES);
38 TrackPage(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
39 }
40
41 [[nodiscard]] bool IsUsed(u64 offset, u64 size) const noexcept {
42 const size_t page = offset >> PAGE_SHIFT;
43 const size_t page_end = (offset + size) >> PAGE_SHIFT;
44 if (IsPageUsed(page, offset, size)) {
45 return true;
46 }
47 for (size_t i = page + 1; i < page_end; i++) {
48 if (pages[i] != 0) {
49 return true;
50 }
51 }
52 const size_t offset_end = offset + size;
53 const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES);
54 return IsPageUsed(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
55 }
56
57private:
58 void TrackPage(u64 page, u64 offset, u64 size) noexcept {
59 const size_t offset_in_page = offset % PAGE_BYTES;
60 const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT;
61 const size_t num_bits = std::min<size_t>(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT;
62 const size_t mask = ~u64{0} >> (64 - num_bits);
63 pages[page] |= (~u64{0} & mask) << first_bit;
64 }
65
66 bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept {
67 const size_t offset_in_page = offset % PAGE_BYTES;
68 const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT;
69 const size_t num_bits = std::min<size_t>(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT;
70 const size_t mask = ~u64{0} >> (64 - num_bits);
71 const size_t mask2 = (~u64{0} & mask) << first_bit;
72 return (pages[page] & mask2) != 0;
73 }
74
75private:
76 std::vector<u64> pages;
77};
78
79} // namespace VideoCommon
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp
index dbcf508e5..1030db681 100644
--- a/src/video_core/host1x/codecs/codec.cpp
+++ b/src/video_core/host1x/codecs/codec.cpp
@@ -1,11 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 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 <algorithm>
5#include <fstream>
6#include <vector>
7#include "common/assert.h" 4#include "common/assert.h"
8#include "common/scope_exit.h"
9#include "common/settings.h" 5#include "common/settings.h"
10#include "video_core/host1x/codecs/codec.h" 6#include "video_core/host1x/codecs/codec.h"
11#include "video_core/host1x/codecs/h264.h" 7#include "video_core/host1x/codecs/h264.h"
@@ -14,242 +10,17 @@
14#include "video_core/host1x/host1x.h" 10#include "video_core/host1x/host1x.h"
15#include "video_core/memory_manager.h" 11#include "video_core/memory_manager.h"
16 12
17extern "C" {
18#include <libavfilter/buffersink.h>
19#include <libavfilter/buffersrc.h>
20#include <libavutil/opt.h>
21#ifdef LIBVA_FOUND
22// for querying VAAPI driver information
23#include <libavutil/hwcontext_vaapi.h>
24#endif
25}
26
27namespace Tegra { 13namespace Tegra {
28namespace {
29constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
30constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
31constexpr std::array PREFERRED_GPU_DECODERS = {
32 AV_HWDEVICE_TYPE_CUDA,
33#ifdef _WIN32
34 AV_HWDEVICE_TYPE_D3D11VA,
35 AV_HWDEVICE_TYPE_DXVA2,
36#elif defined(__unix__)
37 AV_HWDEVICE_TYPE_VAAPI,
38 AV_HWDEVICE_TYPE_VDPAU,
39#endif
40 // last resort for Linux Flatpak (w/ NVIDIA)
41 AV_HWDEVICE_TYPE_VULKAN,
42};
43
44void AVPacketDeleter(AVPacket* ptr) {
45 av_packet_free(&ptr);
46}
47
48using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>;
49
50AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
51 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
52 if (*p == av_codec_ctx->pix_fmt) {
53 return av_codec_ctx->pix_fmt;
54 }
55 }
56 LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
57 av_buffer_unref(&av_codec_ctx->hw_device_ctx);
58 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
59 return PREFERRED_CPU_FMT;
60}
61
62// List all the currently available hwcontext in ffmpeg
63std::vector<AVHWDeviceType> ListSupportedContexts() {
64 std::vector<AVHWDeviceType> contexts{};
65 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
66 do {
67 current_device_type = av_hwdevice_iterate_types(current_device_type);
68 contexts.push_back(current_device_type);
69 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
70 return contexts;
71}
72
73} // namespace
74
75void AVFrameDeleter(AVFrame* ptr) {
76 av_frame_free(&ptr);
77}
78 14
79Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) 15Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
80 : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)), 16 : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
81 vp8_decoder(std::make_unique<Decoder::VP8>(host1x)), 17 vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
82 vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {} 18 vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
83 19
84Codec::~Codec() { 20Codec::~Codec() = default;
85 if (!initialized) {
86 return;
87 }
88 // Free libav memory
89 avcodec_free_context(&av_codec_ctx);
90 av_buffer_unref(&av_gpu_decoder);
91
92 if (filters_initialized) {
93 avfilter_graph_free(&av_filter_graph);
94 }
95}
96
97bool Codec::CreateGpuAvDevice() {
98 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
99 static const auto supported_contexts = ListSupportedContexts();
100 for (const auto& type : PREFERRED_GPU_DECODERS) {
101 if (std::none_of(supported_contexts.begin(), supported_contexts.end(),
102 [&type](const auto& context) { return context == type; })) {
103 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
104 continue;
105 }
106 // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create
107 av_buffer_unref(&av_gpu_decoder);
108 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
109 if (hwdevice_res < 0) {
110 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
111 av_hwdevice_get_type_name(type), hwdevice_res);
112 continue;
113 }
114#ifdef LIBVA_FOUND
115 if (type == AV_HWDEVICE_TYPE_VAAPI) {
116 // we need to determine if this is an impersonated VAAPI driver
117 AVHWDeviceContext* hwctx =
118 static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data));
119 AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
120 const char* vendor_name = vaQueryVendorString(vactx->display);
121 if (strstr(vendor_name, "VDPAU backend")) {
122 // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them
123 LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver");
124 continue;
125 } else {
126 // according to some user testing, certain vaapi driver (Intel?) could be buggy
127 // so let's log the driver name which may help the developers/supporters
128 LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name);
129 }
130 }
131#endif
132 for (int i = 0;; i++) {
133 const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
134 if (!config) {
135 LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.",
136 av_codec->name, av_hwdevice_get_type_name(type));
137 break;
138 }
139 if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
140 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
141 av_codec_ctx->pix_fmt = config->pix_fmt;
142 return true;
143 }
144 }
145 }
146 return false;
147}
148
149void Codec::InitializeAvCodecContext() {
150 av_codec_ctx = avcodec_alloc_context3(av_codec);
151 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
152 av_codec_ctx->thread_count = 0;
153 av_codec_ctx->thread_type &= ~FF_THREAD_FRAME;
154}
155
156void Codec::InitializeGpuDecoder() {
157 if (!CreateGpuAvDevice()) {
158 av_buffer_unref(&av_gpu_decoder);
159 return;
160 }
161 auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder);
162 ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
163 av_codec_ctx->hw_device_ctx = hw_device_ctx;
164 av_codec_ctx->get_format = GetGpuFormat;
165}
166
167void Codec::InitializeAvFilters(AVFrame* frame) {
168 const AVFilter* buffer_src = avfilter_get_by_name("buffer");
169 const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
170 AVFilterInOut* inputs = avfilter_inout_alloc();
171 AVFilterInOut* outputs = avfilter_inout_alloc();
172 SCOPE_EXIT({
173 avfilter_inout_free(&inputs);
174 avfilter_inout_free(&outputs);
175 });
176
177 // Don't know how to get the accurate time_base but it doesn't matter for yadif filter
178 // so just use 1/1 to make buffer filter happy
179 std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame->width,
180 frame->height, frame->format);
181
182 av_filter_graph = avfilter_graph_alloc();
183 int ret = avfilter_graph_create_filter(&av_filter_src_ctx, buffer_src, "in", args.c_str(),
184 nullptr, av_filter_graph);
185 if (ret < 0) {
186 LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter source error: {}", ret);
187 return;
188 }
189
190 ret = avfilter_graph_create_filter(&av_filter_sink_ctx, buffer_sink, "out", nullptr, nullptr,
191 av_filter_graph);
192 if (ret < 0) {
193 LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter sink error: {}", ret);
194 return;
195 }
196
197 inputs->name = av_strdup("out");
198 inputs->filter_ctx = av_filter_sink_ctx;
199 inputs->pad_idx = 0;
200 inputs->next = nullptr;
201
202 outputs->name = av_strdup("in");
203 outputs->filter_ctx = av_filter_src_ctx;
204 outputs->pad_idx = 0;
205 outputs->next = nullptr;
206
207 const char* description = "yadif=1:-1:0";
208 ret = avfilter_graph_parse_ptr(av_filter_graph, description, &inputs, &outputs, nullptr);
209 if (ret < 0) {
210 LOG_ERROR(Service_NVDRV, "avfilter_graph_parse_ptr error: {}", ret);
211 return;
212 }
213
214 ret = avfilter_graph_config(av_filter_graph, nullptr);
215 if (ret < 0) {
216 LOG_ERROR(Service_NVDRV, "avfilter_graph_config error: {}", ret);
217 return;
218 }
219
220 filters_initialized = true;
221}
222 21
223void Codec::Initialize() { 22void Codec::Initialize() {
224 const AVCodecID codec = [&] { 23 initialized = decode_api.Initialize(current_codec);
225 switch (current_codec) {
226 case Host1x::NvdecCommon::VideoCodec::H264:
227 return AV_CODEC_ID_H264;
228 case Host1x::NvdecCommon::VideoCodec::VP8:
229 return AV_CODEC_ID_VP8;
230 case Host1x::NvdecCommon::VideoCodec::VP9:
231 return AV_CODEC_ID_VP9;
232 default:
233 UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
234 return AV_CODEC_ID_NONE;
235 }
236 }();
237 av_codec = avcodec_find_decoder(codec);
238
239 InitializeAvCodecContext();
240 if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
241 InitializeGpuDecoder();
242 }
243 if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
244 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res);
245 avcodec_free_context(&av_codec_ctx);
246 av_buffer_unref(&av_gpu_decoder);
247 return;
248 }
249 if (!av_codec_ctx->hw_device_ctx) {
250 LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
251 }
252 initialized = true;
253} 24}
254 25
255void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) { 26void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
@@ -264,14 +35,18 @@ void Codec::Decode() {
264 if (is_first_frame) { 35 if (is_first_frame) {
265 Initialize(); 36 Initialize();
266 } 37 }
38
267 if (!initialized) { 39 if (!initialized) {
268 return; 40 return;
269 } 41 }
42
43 // Assemble bitstream.
270 bool vp9_hidden_frame = false; 44 bool vp9_hidden_frame = false;
271 const auto& frame_data = [&]() { 45 size_t configuration_size = 0;
46 const auto packet_data = [&]() {
272 switch (current_codec) { 47 switch (current_codec) {
273 case Tegra::Host1x::NvdecCommon::VideoCodec::H264: 48 case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
274 return h264_decoder->ComposeFrame(state, is_first_frame); 49 return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame);
275 case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: 50 case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
276 return vp8_decoder->ComposeFrame(state); 51 return vp8_decoder->ComposeFrame(state);
277 case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: 52 case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
@@ -283,89 +58,35 @@ void Codec::Decode() {
283 return std::span<const u8>{}; 58 return std::span<const u8>{};
284 } 59 }
285 }(); 60 }();
286 AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; 61
287 if (!packet) { 62 // Send assembled bitstream to decoder.
288 LOG_ERROR(Service_NVDRV, "av_packet_alloc failed"); 63 if (!decode_api.SendPacket(packet_data, configuration_size)) {
289 return;
290 }
291 packet->data = const_cast<u8*>(frame_data.data());
292 packet->size = static_cast<s32>(frame_data.size());
293 if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) {
294 LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res);
295 return; 64 return;
296 } 65 }
297 // Only receive/store visible frames 66
67 // Only receive/store visible frames.
298 if (vp9_hidden_frame) { 68 if (vp9_hidden_frame) {
299 return; 69 return;
300 } 70 }
301 AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter};
302 AVFramePtr final_frame{nullptr, AVFrameDeleter};
303 ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed");
304 if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) {
305 LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
306 return;
307 }
308 if (initial_frame->width == 0 || initial_frame->height == 0) {
309 LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
310 return;
311 }
312 bool is_interlaced = initial_frame->interlaced_frame != 0;
313 if (av_codec_ctx->hw_device_ctx) {
314 final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
315 ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
316 // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
317 // because Intel drivers crash unless using AV_PIX_FMT_NV12
318 final_frame->format = PREFERRED_GPU_FMT;
319 const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
320 ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
321 } else {
322 final_frame = std::move(initial_frame);
323 }
324 if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) {
325 UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
326 return;
327 }
328 if (!is_interlaced) {
329 av_frames.push(std::move(final_frame));
330 } else {
331 if (!filters_initialized) {
332 InitializeAvFilters(final_frame.get());
333 }
334 if (const int ret = av_buffersrc_add_frame_flags(av_filter_src_ctx, final_frame.get(),
335 AV_BUFFERSRC_FLAG_KEEP_REF);
336 ret) {
337 LOG_DEBUG(Service_NVDRV, "av_buffersrc_add_frame_flags error {}", ret);
338 return;
339 }
340 while (true) {
341 auto filter_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
342 71
343 int ret = av_buffersink_get_frame(av_filter_sink_ctx, filter_frame.get()); 72 // Receive output frames from decoder.
73 decode_api.ReceiveFrames(frames);
344 74
345 if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) 75 while (frames.size() > 10) {
346 break; 76 LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame");
347 if (ret < 0) { 77 frames.pop();
348 LOG_DEBUG(Service_NVDRV, "av_buffersink_get_frame error {}", ret);
349 return;
350 }
351
352 av_frames.push(std::move(filter_frame));
353 }
354 }
355 while (av_frames.size() > 10) {
356 LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
357 av_frames.pop();
358 } 78 }
359} 79}
360 80
361AVFramePtr Codec::GetCurrentFrame() { 81std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() {
362 // Sometimes VIC will request more frames than have been decoded. 82 // Sometimes VIC will request more frames than have been decoded.
363 // in this case, return a nullptr and don't overwrite previous frame data 83 // in this case, return a blank frame and don't overwrite previous data.
364 if (av_frames.empty()) { 84 if (frames.empty()) {
365 return AVFramePtr{nullptr, AVFrameDeleter}; 85 return {};
366 } 86 }
367 AVFramePtr frame = std::move(av_frames.front()); 87
368 av_frames.pop(); 88 auto frame = std::move(frames.front());
89 frames.pop();
369 return frame; 90 return frame;
370} 91}
371 92
diff --git a/src/video_core/host1x/codecs/codec.h b/src/video_core/host1x/codecs/codec.h
index 06fe00a4b..f700ae129 100644
--- a/src/video_core/host1x/codecs/codec.h
+++ b/src/video_core/host1x/codecs/codec.h
@@ -4,28 +4,15 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <optional>
7#include <string_view> 8#include <string_view>
8#include <queue> 9#include <queue>
9#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/host1x/ffmpeg/ffmpeg.h"
10#include "video_core/host1x/nvdec_common.h" 12#include "video_core/host1x/nvdec_common.h"
11 13
12extern "C" {
13#if defined(__GNUC__) || defined(__clang__)
14#pragma GCC diagnostic push
15#pragma GCC diagnostic ignored "-Wconversion"
16#endif
17#include <libavcodec/avcodec.h>
18#include <libavfilter/avfilter.h>
19#if defined(__GNUC__) || defined(__clang__)
20#pragma GCC diagnostic pop
21#endif
22}
23
24namespace Tegra { 14namespace Tegra {
25 15
26void AVFrameDeleter(AVFrame* ptr);
27using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>;
28
29namespace Decoder { 16namespace Decoder {
30class H264; 17class H264;
31class VP8; 18class VP8;
@@ -51,7 +38,7 @@ public:
51 void Decode(); 38 void Decode();
52 39
53 /// Returns next decoded frame 40 /// Returns next decoded frame
54 [[nodiscard]] AVFramePtr GetCurrentFrame(); 41 [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame();
55 42
56 /// Returns the value of current_codec 43 /// Returns the value of current_codec
57 [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const; 44 [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
@@ -60,25 +47,9 @@ public:
60 [[nodiscard]] std::string_view GetCurrentCodecName() const; 47 [[nodiscard]] std::string_view GetCurrentCodecName() const;
61 48
62private: 49private:
63 void InitializeAvCodecContext();
64
65 void InitializeAvFilters(AVFrame* frame);
66
67 void InitializeGpuDecoder();
68
69 bool CreateGpuAvDevice();
70
71 bool initialized{}; 50 bool initialized{};
72 bool filters_initialized{};
73 Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; 51 Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None};
74 52 FFmpeg::DecodeApi decode_api;
75 const AVCodec* av_codec{nullptr};
76 AVCodecContext* av_codec_ctx{nullptr};
77 AVBufferRef* av_gpu_decoder{nullptr};
78
79 AVFilterContext* av_filter_src_ctx{nullptr};
80 AVFilterContext* av_filter_sink_ctx{nullptr};
81 AVFilterGraph* av_filter_graph{nullptr};
82 53
83 Host1x::Host1x& host1x; 54 Host1x::Host1x& host1x;
84 const Host1x::NvdecCommon::NvdecRegisters& state; 55 const Host1x::NvdecCommon::NvdecRegisters& state;
@@ -86,7 +57,7 @@ private:
86 std::unique_ptr<Decoder::VP8> vp8_decoder; 57 std::unique_ptr<Decoder::VP8> vp8_decoder;
87 std::unique_ptr<Decoder::VP9> vp9_decoder; 58 std::unique_ptr<Decoder::VP9> vp9_decoder;
88 59
89 std::queue<AVFramePtr> av_frames{}; 60 std::queue<std::unique_ptr<FFmpeg::Frame>> frames{};
90}; 61};
91 62
92} // namespace Tegra 63} // namespace Tegra
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp
index ece79b1e2..309a7f1d5 100644
--- a/src/video_core/host1x/codecs/h264.cpp
+++ b/src/video_core/host1x/codecs/h264.cpp
@@ -30,7 +30,7 @@ H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {}
30H264::~H264() = default; 30H264::~H264() = default;
31 31
32std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, 32std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
33 bool is_first_frame) { 33 size_t* out_configuration_size, bool is_first_frame) {
34 H264DecoderContext context; 34 H264DecoderContext context;
35 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, 35 host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context,
36 sizeof(H264DecoderContext)); 36 sizeof(H264DecoderContext));
@@ -39,6 +39,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
39 if (!is_first_frame && frame_number != 0) { 39 if (!is_first_frame && frame_number != 0) {
40 frame.resize_destructive(context.stream_len); 40 frame.resize_destructive(context.stream_len);
41 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); 41 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
42 *out_configuration_size = 0;
42 return frame; 43 return frame;
43 } 44 }
44 45
@@ -157,6 +158,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
157 frame.resize(encoded_header.size() + context.stream_len); 158 frame.resize(encoded_header.size() + context.stream_len);
158 std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); 159 std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
159 160
161 *out_configuration_size = encoded_header.size();
160 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, 162 host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset,
161 frame.data() + encoded_header.size(), context.stream_len); 163 frame.data() + encoded_header.size(), context.stream_len);
162 164
diff --git a/src/video_core/host1x/codecs/h264.h b/src/video_core/host1x/codecs/h264.h
index d6b556322..1deaf4632 100644
--- a/src/video_core/host1x/codecs/h264.h
+++ b/src/video_core/host1x/codecs/h264.h
@@ -67,6 +67,7 @@ public:
67 67
68 /// Compose the H264 frame for FFmpeg decoding 68 /// Compose the H264 frame for FFmpeg decoding
69 [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, 69 [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
70 size_t* out_configuration_size,
70 bool is_first_frame = false); 71 bool is_first_frame = false);
71 72
72private: 73private:
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
new file mode 100644
index 000000000..dcd07e6d2
--- /dev/null
+++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
@@ -0,0 +1,419 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "common/logging/log.h"
6#include "common/scope_exit.h"
7#include "common/settings.h"
8#include "video_core/host1x/ffmpeg/ffmpeg.h"
9
10extern "C" {
11#ifdef LIBVA_FOUND
12// for querying VAAPI driver information
13#include <libavutil/hwcontext_vaapi.h>
14#endif
15}
16
17namespace FFmpeg {
18
19namespace {
20
21constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12;
22constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P;
23constexpr std::array PreferredGpuDecoders = {
24 AV_HWDEVICE_TYPE_CUDA,
25#ifdef _WIN32
26 AV_HWDEVICE_TYPE_D3D11VA,
27 AV_HWDEVICE_TYPE_DXVA2,
28#elif defined(__unix__)
29 AV_HWDEVICE_TYPE_VAAPI,
30 AV_HWDEVICE_TYPE_VDPAU,
31#endif
32 // last resort for Linux Flatpak (w/ NVIDIA)
33 AV_HWDEVICE_TYPE_VULKAN,
34};
35
36AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) {
37 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
38 if (*p == codec_context->pix_fmt) {
39 return codec_context->pix_fmt;
40 }
41 }
42
43 LOG_INFO(HW_GPU, "Could not find compatible GPU AV format, falling back to CPU");
44 av_buffer_unref(&codec_context->hw_device_ctx);
45
46 codec_context->pix_fmt = PreferredCpuFormat;
47 return codec_context->pix_fmt;
48}
49
50std::string AVError(int errnum) {
51 char errbuf[AV_ERROR_MAX_STRING_SIZE] = {};
52 av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum);
53 return errbuf;
54}
55
56} // namespace
57
58Packet::Packet(std::span<const u8> data) {
59 m_packet = av_packet_alloc();
60 m_packet->data = const_cast<u8*>(data.data());
61 m_packet->size = static_cast<s32>(data.size());
62}
63
64Packet::~Packet() {
65 av_packet_free(&m_packet);
66}
67
68Frame::Frame() {
69 m_frame = av_frame_alloc();
70}
71
72Frame::~Frame() {
73 av_frame_free(&m_frame);
74}
75
76Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
77 const AVCodecID av_codec = [&] {
78 switch (codec) {
79 case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
80 return AV_CODEC_ID_H264;
81 case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
82 return AV_CODEC_ID_VP8;
83 case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
84 return AV_CODEC_ID_VP9;
85 default:
86 UNIMPLEMENTED_MSG("Unknown codec {}", codec);
87 return AV_CODEC_ID_NONE;
88 }
89 }();
90
91 m_codec = avcodec_find_decoder(av_codec);
92}
93
94bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
95 for (int i = 0;; i++) {
96 const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i);
97 if (!config) {
98 LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name,
99 av_hwdevice_get_type_name(type));
100 break;
101 }
102 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) != 0 &&
103 config->device_type == type) {
104 LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
105 *out_pix_fmt = config->pix_fmt;
106 return true;
107 }
108 }
109
110 return false;
111}
112
113std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() {
114 std::vector<AVHWDeviceType> types;
115 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
116
117 while (true) {
118 current_device_type = av_hwdevice_iterate_types(current_device_type);
119 if (current_device_type == AV_HWDEVICE_TYPE_NONE) {
120 return types;
121 }
122
123 types.push_back(current_device_type);
124 }
125}
126
127HardwareContext::~HardwareContext() {
128 av_buffer_unref(&m_gpu_decoder);
129}
130
131bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context,
132 const Decoder& decoder) {
133 const auto supported_types = GetSupportedDeviceTypes();
134 for (const auto type : PreferredGpuDecoders) {
135 AVPixelFormat hw_pix_fmt;
136
137 if (std::ranges::find(supported_types, type) == supported_types.end()) {
138 LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
139 continue;
140 }
141
142 if (!this->InitializeWithType(type)) {
143 continue;
144 }
145
146 if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
147 decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
148 return true;
149 }
150 }
151
152 return false;
153}
154
155bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
156 av_buffer_unref(&m_gpu_decoder);
157
158 if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0);
159 ret < 0) {
160 LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type),
161 AVError(ret));
162 return false;
163 }
164
165#ifdef LIBVA_FOUND
166 if (type == AV_HWDEVICE_TYPE_VAAPI) {
167 // We need to determine if this is an impersonated VAAPI driver.
168 auto* hwctx = reinterpret_cast<AVHWDeviceContext*>(m_gpu_decoder->data);
169 auto* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
170 const char* vendor_name = vaQueryVendorString(vactx->display);
171 if (strstr(vendor_name, "VDPAU backend")) {
172 // VDPAU impersonated VAAPI impls are super buggy, we need to skip them.
173 LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver");
174 return false;
175 } else {
176 // According to some user testing, certain VAAPI drivers (Intel?) could be buggy.
177 // Log the driver name just in case.
178 LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name);
179 }
180 }
181#endif
182
183 return true;
184}
185
186DecoderContext::DecoderContext(const Decoder& decoder) {
187 m_codec_context = avcodec_alloc_context3(decoder.GetCodec());
188 av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
189 m_codec_context->thread_count = 0;
190 m_codec_context->thread_type &= ~FF_THREAD_FRAME;
191}
192
193DecoderContext::~DecoderContext() {
194 av_buffer_unref(&m_codec_context->hw_device_ctx);
195 avcodec_free_context(&m_codec_context);
196}
197
198void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context,
199 AVPixelFormat hw_pix_fmt) {
200 m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef());
201 m_codec_context->get_format = GetGpuFormat;
202 m_codec_context->pix_fmt = hw_pix_fmt;
203}
204
205bool DecoderContext::OpenContext(const Decoder& decoder) {
206 if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
207 LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
208 return false;
209 }
210
211 if (!m_codec_context->hw_device_ctx) {
212 LOG_INFO(HW_GPU, "Using FFmpeg software decoding");
213 }
214
215 return true;
216}
217
218bool DecoderContext::SendPacket(const Packet& packet) {
219 if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) {
220 LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
221 return false;
222 }
223
224 return true;
225}
226
227std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) {
228 auto dst_frame = std::make_unique<Frame>();
229
230 const auto ReceiveImpl = [&](AVFrame* frame) {
231 if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
232 LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
233 return false;
234 }
235
236 *out_is_interlaced = frame->interlaced_frame != 0;
237 return true;
238 };
239
240 if (m_codec_context->hw_device_ctx) {
241 // If we have a hardware context, make a separate frame here to receive the
242 // hardware result before sending it to the output.
243 Frame intermediate_frame;
244
245 if (!ReceiveImpl(intermediate_frame.GetFrame())) {
246 return {};
247 }
248
249 dst_frame->SetFormat(PreferredGpuFormat);
250 if (const int ret =
251 av_hwframe_transfer_data(dst_frame->GetFrame(), intermediate_frame.GetFrame(), 0);
252 ret < 0) {
253 LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
254 return {};
255 }
256 } else {
257 // Otherwise, decode the frame as normal.
258 if (!ReceiveImpl(dst_frame->GetFrame())) {
259 return {};
260 }
261 }
262
263 return dst_frame;
264}
265
266DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) {
267 const AVFilter* buffer_src = avfilter_get_by_name("buffer");
268 const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
269 AVFilterInOut* inputs = avfilter_inout_alloc();
270 AVFilterInOut* outputs = avfilter_inout_alloc();
271 SCOPE_EXIT({
272 avfilter_inout_free(&inputs);
273 avfilter_inout_free(&outputs);
274 });
275
276 // Don't know how to get the accurate time_base but it doesn't matter for yadif filter
277 // so just use 1/1 to make buffer filter happy
278 std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame.GetWidth(),
279 frame.GetHeight(), static_cast<int>(frame.GetPixelFormat()));
280
281 m_filter_graph = avfilter_graph_alloc();
282 int ret = avfilter_graph_create_filter(&m_source_context, buffer_src, "in", args.c_str(),
283 nullptr, m_filter_graph);
284 if (ret < 0) {
285 LOG_ERROR(HW_GPU, "avfilter_graph_create_filter source error: {}", AVError(ret));
286 return;
287 }
288
289 ret = avfilter_graph_create_filter(&m_sink_context, buffer_sink, "out", nullptr, nullptr,
290 m_filter_graph);
291 if (ret < 0) {
292 LOG_ERROR(HW_GPU, "avfilter_graph_create_filter sink error: {}", AVError(ret));
293 return;
294 }
295
296 inputs->name = av_strdup("out");
297 inputs->filter_ctx = m_sink_context;
298 inputs->pad_idx = 0;
299 inputs->next = nullptr;
300
301 outputs->name = av_strdup("in");
302 outputs->filter_ctx = m_source_context;
303 outputs->pad_idx = 0;
304 outputs->next = nullptr;
305
306 const char* description = "yadif=1:-1:0";
307 ret = avfilter_graph_parse_ptr(m_filter_graph, description, &inputs, &outputs, nullptr);
308 if (ret < 0) {
309 LOG_ERROR(HW_GPU, "avfilter_graph_parse_ptr error: {}", AVError(ret));
310 return;
311 }
312
313 ret = avfilter_graph_config(m_filter_graph, nullptr);
314 if (ret < 0) {
315 LOG_ERROR(HW_GPU, "avfilter_graph_config error: {}", AVError(ret));
316 return;
317 }
318
319 m_initialized = true;
320}
321
322bool DeinterlaceFilter::AddSourceFrame(const Frame& frame) {
323 if (const int ret = av_buffersrc_add_frame_flags(m_source_context, frame.GetFrame(),
324 AV_BUFFERSRC_FLAG_KEEP_REF);
325 ret < 0) {
326 LOG_ERROR(HW_GPU, "av_buffersrc_add_frame_flags error: {}", AVError(ret));
327 return false;
328 }
329
330 return true;
331}
332
333std::unique_ptr<Frame> DeinterlaceFilter::DrainSinkFrame() {
334 auto dst_frame = std::make_unique<Frame>();
335 const int ret = av_buffersink_get_frame(m_sink_context, dst_frame->GetFrame());
336
337 if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) {
338 return {};
339 }
340
341 if (ret < 0) {
342 LOG_ERROR(HW_GPU, "av_buffersink_get_frame error: {}", AVError(ret));
343 return {};
344 }
345
346 return dst_frame;
347}
348
349DeinterlaceFilter::~DeinterlaceFilter() {
350 avfilter_graph_free(&m_filter_graph);
351}
352
353void DecodeApi::Reset() {
354 m_deinterlace_filter.reset();
355 m_hardware_context.reset();
356 m_decoder_context.reset();
357 m_decoder.reset();
358}
359
360bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
361 this->Reset();
362 m_decoder.emplace(codec);
363 m_decoder_context.emplace(*m_decoder);
364
365 // Enable GPU decoding if requested.
366 if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
367 m_hardware_context.emplace();
368 m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
369 }
370
371 // Open the decoder context.
372 if (!m_decoder_context->OpenContext(*m_decoder)) {
373 this->Reset();
374 return false;
375 }
376
377 return true;
378}
379
380bool DecodeApi::SendPacket(std::span<const u8> packet_data, size_t configuration_size) {
381 FFmpeg::Packet packet(packet_data);
382 return m_decoder_context->SendPacket(packet);
383}
384
385void DecodeApi::ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue) {
386 // Receive raw frame from decoder.
387 bool is_interlaced;
388 auto frame = m_decoder_context->ReceiveFrame(&is_interlaced);
389 if (!frame) {
390 return;
391 }
392
393 if (!is_interlaced) {
394 // If the frame is not interlaced, we can pend it now.
395 frame_queue.push(std::move(frame));
396 } else {
397 // Create the deinterlacer if needed.
398 if (!m_deinterlace_filter) {
399 m_deinterlace_filter.emplace(*frame);
400 }
401
402 // Add the frame we just received.
403 if (!m_deinterlace_filter->AddSourceFrame(*frame)) {
404 return;
405 }
406
407 // Pend output fields.
408 while (true) {
409 auto filter_frame = m_deinterlace_filter->DrainSinkFrame();
410 if (!filter_frame) {
411 break;
412 }
413
414 frame_queue.push(std::move(filter_frame));
415 }
416 }
417}
418
419} // namespace FFmpeg
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.h b/src/video_core/host1x/ffmpeg/ffmpeg.h
new file mode 100644
index 000000000..1de0bbd83
--- /dev/null
+++ b/src/video_core/host1x/ffmpeg/ffmpeg.h
@@ -0,0 +1,213 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <optional>
8#include <span>
9#include <vector>
10#include <queue>
11
12#include "common/common_funcs.h"
13#include "common/common_types.h"
14#include "video_core/host1x/nvdec_common.h"
15
16extern "C" {
17#if defined(__GNUC__) || defined(__clang__)
18#pragma GCC diagnostic push
19#pragma GCC diagnostic ignored "-Wconversion"
20#endif
21
22#include <libavcodec/avcodec.h>
23#include <libavfilter/avfilter.h>
24#include <libavfilter/buffersink.h>
25#include <libavfilter/buffersrc.h>
26#include <libavutil/avutil.h>
27#include <libavutil/opt.h>
28
29#if defined(__GNUC__) || defined(__clang__)
30#pragma GCC diagnostic pop
31#endif
32}
33
34namespace FFmpeg {
35
36class Packet;
37class Frame;
38class Decoder;
39class HardwareContext;
40class DecoderContext;
41class DeinterlaceFilter;
42
43// Wraps an AVPacket, a container for compressed bitstream data.
44class Packet {
45public:
46 YUZU_NON_COPYABLE(Packet);
47 YUZU_NON_MOVEABLE(Packet);
48
49 explicit Packet(std::span<const u8> data);
50 ~Packet();
51
52 AVPacket* GetPacket() const {
53 return m_packet;
54 }
55
56private:
57 AVPacket* m_packet{};
58};
59
60// Wraps an AVFrame, a container for audio and video stream data.
61class Frame {
62public:
63 YUZU_NON_COPYABLE(Frame);
64 YUZU_NON_MOVEABLE(Frame);
65
66 explicit Frame();
67 ~Frame();
68
69 int GetWidth() const {
70 return m_frame->width;
71 }
72
73 int GetHeight() const {
74 return m_frame->height;
75 }
76
77 AVPixelFormat GetPixelFormat() const {
78 return static_cast<AVPixelFormat>(m_frame->format);
79 }
80
81 int GetStride(int plane) const {
82 return m_frame->linesize[plane];
83 }
84
85 int* GetStrides() const {
86 return m_frame->linesize;
87 }
88
89 u8* GetData(int plane) const {
90 return m_frame->data[plane];
91 }
92
93 u8** GetPlanes() const {
94 return m_frame->data;
95 }
96
97 void SetFormat(int format) {
98 m_frame->format = format;
99 }
100
101 AVFrame* GetFrame() const {
102 return m_frame;
103 }
104
105private:
106 AVFrame* m_frame{};
107};
108
109// Wraps an AVCodec, a type containing information about a codec.
110class Decoder {
111public:
112 YUZU_NON_COPYABLE(Decoder);
113 YUZU_NON_MOVEABLE(Decoder);
114
115 explicit Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec);
116 ~Decoder() = default;
117
118 bool SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const;
119
120 const AVCodec* GetCodec() const {
121 return m_codec;
122 }
123
124private:
125 const AVCodec* m_codec{};
126};
127
128// Wraps AVBufferRef for an accelerated decoder.
129class HardwareContext {
130public:
131 YUZU_NON_COPYABLE(HardwareContext);
132 YUZU_NON_MOVEABLE(HardwareContext);
133
134 static std::vector<AVHWDeviceType> GetSupportedDeviceTypes();
135
136 explicit HardwareContext() = default;
137 ~HardwareContext();
138
139 bool InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder);
140
141 AVBufferRef* GetBufferRef() const {
142 return m_gpu_decoder;
143 }
144
145private:
146 bool InitializeWithType(AVHWDeviceType type);
147
148 AVBufferRef* m_gpu_decoder{};
149};
150
151// Wraps an AVCodecContext.
152class DecoderContext {
153public:
154 YUZU_NON_COPYABLE(DecoderContext);
155 YUZU_NON_MOVEABLE(DecoderContext);
156
157 explicit DecoderContext(const Decoder& decoder);
158 ~DecoderContext();
159
160 void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
161 bool OpenContext(const Decoder& decoder);
162 bool SendPacket(const Packet& packet);
163 std::unique_ptr<Frame> ReceiveFrame(bool* out_is_interlaced);
164
165 AVCodecContext* GetCodecContext() const {
166 return m_codec_context;
167 }
168
169private:
170 AVCodecContext* m_codec_context{};
171};
172
173// Wraps an AVFilterGraph.
174class DeinterlaceFilter {
175public:
176 YUZU_NON_COPYABLE(DeinterlaceFilter);
177 YUZU_NON_MOVEABLE(DeinterlaceFilter);
178
179 explicit DeinterlaceFilter(const Frame& frame);
180 ~DeinterlaceFilter();
181
182 bool AddSourceFrame(const Frame& frame);
183 std::unique_ptr<Frame> DrainSinkFrame();
184
185private:
186 AVFilterGraph* m_filter_graph{};
187 AVFilterContext* m_source_context{};
188 AVFilterContext* m_sink_context{};
189 bool m_initialized{};
190};
191
192class DecodeApi {
193public:
194 YUZU_NON_COPYABLE(DecodeApi);
195 YUZU_NON_MOVEABLE(DecodeApi);
196
197 DecodeApi() = default;
198 ~DecodeApi() = default;
199
200 bool Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec);
201 void Reset();
202
203 bool SendPacket(std::span<const u8> packet_data, size_t configuration_size);
204 void ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue);
205
206private:
207 std::optional<FFmpeg::Decoder> m_decoder;
208 std::optional<FFmpeg::DecoderContext> m_decoder_context;
209 std::optional<FFmpeg::HardwareContext> m_hardware_context;
210 std::optional<FFmpeg::DeinterlaceFilter> m_deinterlace_filter;
211};
212
213} // namespace FFmpeg
diff --git a/src/video_core/host1x/nvdec.cpp b/src/video_core/host1x/nvdec.cpp
index a4bd5b79f..b8f5866d3 100644
--- a/src/video_core/host1x/nvdec.cpp
+++ b/src/video_core/host1x/nvdec.cpp
@@ -28,7 +28,7 @@ void Nvdec::ProcessMethod(u32 method, u32 argument) {
28 } 28 }
29} 29}
30 30
31AVFramePtr Nvdec::GetFrame() { 31std::unique_ptr<FFmpeg::Frame> Nvdec::GetFrame() {
32 return codec->GetCurrentFrame(); 32 return codec->GetCurrentFrame();
33} 33}
34 34
diff --git a/src/video_core/host1x/nvdec.h b/src/video_core/host1x/nvdec.h
index 3949d5181..ddddb8d28 100644
--- a/src/video_core/host1x/nvdec.h
+++ b/src/video_core/host1x/nvdec.h
@@ -23,7 +23,7 @@ public:
23 void ProcessMethod(u32 method, u32 argument); 23 void ProcessMethod(u32 method, u32 argument);
24 24
25 /// Return most recently decoded frame 25 /// Return most recently decoded frame
26 [[nodiscard]] AVFramePtr GetFrame(); 26 [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetFrame();
27 27
28private: 28private:
29 /// Invoke codec to decode a frame 29 /// Invoke codec to decode a frame
diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp
index 10d7ef884..2a5eba415 100644
--- a/src/video_core/host1x/vic.cpp
+++ b/src/video_core/host1x/vic.cpp
@@ -82,27 +82,26 @@ void Vic::Execute() {
82 return; 82 return;
83 } 83 }
84 const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)}; 84 const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)};
85 const AVFramePtr frame_ptr = nvdec_processor->GetFrame(); 85 auto frame = nvdec_processor->GetFrame();
86 const auto* frame = frame_ptr.get();
87 if (!frame) { 86 if (!frame) {
88 return; 87 return;
89 } 88 }
90 const u64 surface_width = config.surface_width_minus1 + 1; 89 const u64 surface_width = config.surface_width_minus1 + 1;
91 const u64 surface_height = config.surface_height_minus1 + 1; 90 const u64 surface_height = config.surface_height_minus1 + 1;
92 if (static_cast<u64>(frame->width) != surface_width || 91 if (static_cast<u64>(frame->GetWidth()) != surface_width ||
93 static_cast<u64>(frame->height) != surface_height) { 92 static_cast<u64>(frame->GetHeight()) != surface_height) {
94 // TODO: Properly support multiple video streams with differing frame dimensions 93 // TODO: Properly support multiple video streams with differing frame dimensions
95 LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}", 94 LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}",
96 frame->width, frame->height, surface_width, surface_height); 95 frame->GetWidth(), frame->GetHeight(), surface_width, surface_height);
97 } 96 }
98 switch (config.pixel_format) { 97 switch (config.pixel_format) {
99 case VideoPixelFormat::RGBA8: 98 case VideoPixelFormat::RGBA8:
100 case VideoPixelFormat::BGRA8: 99 case VideoPixelFormat::BGRA8:
101 case VideoPixelFormat::RGBX8: 100 case VideoPixelFormat::RGBX8:
102 WriteRGBFrame(frame, config); 101 WriteRGBFrame(std::move(frame), config);
103 break; 102 break;
104 case VideoPixelFormat::YUV420: 103 case VideoPixelFormat::YUV420:
105 WriteYUVFrame(frame, config); 104 WriteYUVFrame(std::move(frame), config);
106 break; 105 break;
107 default: 106 default:
108 UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value()); 107 UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value());
@@ -110,10 +109,14 @@ void Vic::Execute() {
110 } 109 }
111} 110}
112 111
113void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) { 112void Vic::WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) {
114 LOG_TRACE(Service_NVDRV, "Writing RGB Frame"); 113 LOG_TRACE(Service_NVDRV, "Writing RGB Frame");
115 114
116 if (!scaler_ctx || frame->width != scaler_width || frame->height != scaler_height) { 115 const auto frame_width = frame->GetWidth();
116 const auto frame_height = frame->GetHeight();
117 const auto frame_format = frame->GetPixelFormat();
118
119 if (!scaler_ctx || frame_width != scaler_width || frame_height != scaler_height) {
117 const AVPixelFormat target_format = [pixel_format = config.pixel_format]() { 120 const AVPixelFormat target_format = [pixel_format = config.pixel_format]() {
118 switch (pixel_format) { 121 switch (pixel_format) {
119 case VideoPixelFormat::RGBA8: 122 case VideoPixelFormat::RGBA8:
@@ -129,27 +132,26 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
129 132
130 sws_freeContext(scaler_ctx); 133 sws_freeContext(scaler_ctx);
131 // Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format 134 // Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format
132 scaler_ctx = sws_getContext(frame->width, frame->height, 135 scaler_ctx = sws_getContext(frame_width, frame_height, frame_format, frame_width,
133 static_cast<AVPixelFormat>(frame->format), frame->width, 136 frame_height, target_format, 0, nullptr, nullptr, nullptr);
134 frame->height, target_format, 0, nullptr, nullptr, nullptr); 137 scaler_width = frame_width;
135 scaler_width = frame->width; 138 scaler_height = frame_height;
136 scaler_height = frame->height;
137 converted_frame_buffer.reset(); 139 converted_frame_buffer.reset();
138 } 140 }
139 if (!converted_frame_buffer) { 141 if (!converted_frame_buffer) {
140 const size_t frame_size = frame->width * frame->height * 4; 142 const size_t frame_size = frame_width * frame_height * 4;
141 converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free}; 143 converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free};
142 } 144 }
143 const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0}; 145 const std::array<int, 4> converted_stride{frame_width * 4, frame_height * 4, 0, 0};
144 u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; 146 u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
145 sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, &converted_frame_buf_addr, 147 sws_scale(scaler_ctx, frame->GetPlanes(), frame->GetStrides(), 0, frame_height,
146 converted_stride.data()); 148 &converted_frame_buf_addr, converted_stride.data());
147 149
148 // Use the minimum of surface/frame dimensions to avoid buffer overflow. 150 // Use the minimum of surface/frame dimensions to avoid buffer overflow.
149 const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1; 151 const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1;
150 const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1; 152 const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1;
151 const u32 width = std::min(surface_width, static_cast<u32>(frame->width)); 153 const u32 width = std::min(surface_width, static_cast<u32>(frame_width));
152 const u32 height = std::min(surface_height, static_cast<u32>(frame->height)); 154 const u32 height = std::min(surface_height, static_cast<u32>(frame_height));
153 const u32 blk_kind = static_cast<u32>(config.block_linear_kind); 155 const u32 blk_kind = static_cast<u32>(config.block_linear_kind);
154 if (blk_kind != 0) { 156 if (blk_kind != 0) {
155 // swizzle pitch linear to block linear 157 // swizzle pitch linear to block linear
@@ -169,23 +171,23 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
169 } 171 }
170} 172}
171 173
172void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) { 174void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) {
173 LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame"); 175 LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame");
174 176
175 const std::size_t surface_width = config.surface_width_minus1 + 1; 177 const std::size_t surface_width = config.surface_width_minus1 + 1;
176 const std::size_t surface_height = config.surface_height_minus1 + 1; 178 const std::size_t surface_height = config.surface_height_minus1 + 1;
177 const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL; 179 const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL;
178 // Use the minimum of surface/frame dimensions to avoid buffer overflow. 180 // Use the minimum of surface/frame dimensions to avoid buffer overflow.
179 const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width)); 181 const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->GetWidth()));
180 const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height)); 182 const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->GetHeight()));
181 183
182 const auto stride = static_cast<size_t>(frame->linesize[0]); 184 const auto stride = static_cast<size_t>(frame->GetStride(0));
183 185
184 luma_buffer.resize_destructive(aligned_width * surface_height); 186 luma_buffer.resize_destructive(aligned_width * surface_height);
185 chroma_buffer.resize_destructive(aligned_width * surface_height / 2); 187 chroma_buffer.resize_destructive(aligned_width * surface_height / 2);
186 188
187 // Populate luma buffer 189 // Populate luma buffer
188 const u8* luma_src = frame->data[0]; 190 const u8* luma_src = frame->GetData(0);
189 for (std::size_t y = 0; y < frame_height; ++y) { 191 for (std::size_t y = 0; y < frame_height; ++y) {
190 const std::size_t src = y * stride; 192 const std::size_t src = y * stride;
191 const std::size_t dst = y * aligned_width; 193 const std::size_t dst = y * aligned_width;
@@ -196,16 +198,16 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
196 198
197 // Chroma 199 // Chroma
198 const std::size_t half_height = frame_height / 2; 200 const std::size_t half_height = frame_height / 2;
199 const auto half_stride = static_cast<size_t>(frame->linesize[1]); 201 const auto half_stride = static_cast<size_t>(frame->GetStride(1));
200 202
201 switch (frame->format) { 203 switch (frame->GetPixelFormat()) {
202 case AV_PIX_FMT_YUV420P: { 204 case AV_PIX_FMT_YUV420P: {
203 // Frame from FFmpeg software 205 // Frame from FFmpeg software
204 // Populate chroma buffer from both channels with interleaving. 206 // Populate chroma buffer from both channels with interleaving.
205 const std::size_t half_width = frame_width / 2; 207 const std::size_t half_width = frame_width / 2;
206 u8* chroma_buffer_data = chroma_buffer.data(); 208 u8* chroma_buffer_data = chroma_buffer.data();
207 const u8* chroma_b_src = frame->data[1]; 209 const u8* chroma_b_src = frame->GetData(1);
208 const u8* chroma_r_src = frame->data[2]; 210 const u8* chroma_r_src = frame->GetData(2);
209 for (std::size_t y = 0; y < half_height; ++y) { 211 for (std::size_t y = 0; y < half_height; ++y) {
210 const std::size_t src = y * half_stride; 212 const std::size_t src = y * half_stride;
211 const std::size_t dst = y * aligned_width; 213 const std::size_t dst = y * aligned_width;
@@ -219,7 +221,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
219 case AV_PIX_FMT_NV12: { 221 case AV_PIX_FMT_NV12: {
220 // Frame from VA-API hardware 222 // Frame from VA-API hardware
221 // This is already interleaved so just copy 223 // This is already interleaved so just copy
222 const u8* chroma_src = frame->data[1]; 224 const u8* chroma_src = frame->GetData(1);
223 for (std::size_t y = 0; y < half_height; ++y) { 225 for (std::size_t y = 0; y < half_height; ++y) {
224 const std::size_t src = y * stride; 226 const std::size_t src = y * stride;
225 const std::size_t dst = y * aligned_width; 227 const std::size_t dst = y * aligned_width;
diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h
index 3d9753047..6c868f062 100644
--- a/src/video_core/host1x/vic.h
+++ b/src/video_core/host1x/vic.h
@@ -39,9 +39,9 @@ public:
39private: 39private:
40 void Execute(); 40 void Execute();
41 41
42 void WriteRGBFrame(const AVFrame* frame, const VicConfig& config); 42 void WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config);
43 43
44 void WriteYUVFrame(const AVFrame* frame, const VicConfig& config); 44 void WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config);
45 45
46 Host1x& host1x; 46 Host1x& host1x;
47 std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor; 47 std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor;
diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h
index 78b42b518..efa9adf7a 100644
--- a/src/video_core/query_cache/query_cache.h
+++ b/src/video_core/query_cache/query_cache.h
@@ -266,7 +266,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
266 return; 266 return;
267 } 267 }
268 if (False(query_base->flags & QueryFlagBits::IsFinalValueSynced)) [[unlikely]] { 268 if (False(query_base->flags & QueryFlagBits::IsFinalValueSynced)) [[unlikely]] {
269 UNREACHABLE(); 269 ASSERT(false);
270 return; 270 return;
271 } 271 }
272 query_base->value += streamer->GetAmmendValue(); 272 query_base->value += streamer->GetAmmendValue();
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 9d5209e97..e6c70fb34 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -145,8 +145,12 @@ StagingBufferMap BufferCacheRuntime::UploadStagingBuffer(size_t size) {
145 return staging_buffer_pool.RequestUploadBuffer(size); 145 return staging_buffer_pool.RequestUploadBuffer(size);
146} 146}
147 147
148StagingBufferMap BufferCacheRuntime::DownloadStagingBuffer(size_t size) { 148StagingBufferMap BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
149 return staging_buffer_pool.RequestDownloadBuffer(size); 149 return staging_buffer_pool.RequestDownloadBuffer(size, deferred);
150}
151
152void BufferCacheRuntime::FreeDeferredStagingBuffer(StagingBufferMap& buffer) {
153 staging_buffer_pool.FreeDeferredStagingBuffer(buffer);
150} 154}
151 155
152u64 BufferCacheRuntime::GetDeviceMemoryUsage() const { 156u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
@@ -177,13 +181,14 @@ void BufferCacheRuntime::CopyBuffer(GLuint dst_buffer, Buffer& src_buffer,
177} 181}
178 182
179void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, 183void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer,
180 std::span<const VideoCommon::BufferCopy> copies, bool barrier) { 184 std::span<const VideoCommon::BufferCopy> copies, bool barrier,
185 bool) {
181 CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier); 186 CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier);
182} 187}
183 188
184void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, 189void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
185 std::span<const VideoCommon::BufferCopy> copies) { 190 std::span<const VideoCommon::BufferCopy> copies, bool) {
186 CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies); 191 CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies, true);
187} 192}
188 193
189void BufferCacheRuntime::PreCopyBarrier() { 194void BufferCacheRuntime::PreCopyBarrier() {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 8613037eb..71cd45d35 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -30,6 +30,8 @@ public:
30 30
31 void MakeResident(GLenum access) noexcept; 31 void MakeResident(GLenum access) noexcept;
32 32
33 void MarkUsage(u64 offset, u64 size) {}
34
33 [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format); 35 [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
34 36
35 [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept { 37 [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept {
@@ -64,24 +66,33 @@ public:
64 66
65 [[nodiscard]] StagingBufferMap UploadStagingBuffer(size_t size); 67 [[nodiscard]] StagingBufferMap UploadStagingBuffer(size_t size);
66 68
67 [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size); 69 [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false);
70
71 void FreeDeferredStagingBuffer(StagingBufferMap& buffer);
72
73 bool CanReorderUpload(const Buffer&, std::span<const VideoCommon::BufferCopy>) {
74 return false;
75 }
68 76
69 void CopyBuffer(GLuint dst_buffer, GLuint src_buffer, 77 void CopyBuffer(GLuint dst_buffer, GLuint src_buffer,
70 std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); 78 std::span<const VideoCommon::BufferCopy> copies, bool barrier);
71 79
72 void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, 80 void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer,
73 std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); 81 std::span<const VideoCommon::BufferCopy> copies, bool barrier);
74 82
75 void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, 83 void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer,
76 std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); 84 std::span<const VideoCommon::BufferCopy> copies, bool barrier,
85 bool can_reorder_upload = false);
77 86
78 void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, 87 void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
79 std::span<const VideoCommon::BufferCopy> copies); 88 std::span<const VideoCommon::BufferCopy> copies, bool);
80 89
81 void PreCopyBarrier(); 90 void PreCopyBarrier();
82 void PostCopyBarrier(); 91 void PostCopyBarrier();
83 void Finish(); 92 void Finish();
84 93
94 void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {}
95
85 void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); 96 void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
86 97
87 void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); 98 void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
@@ -182,6 +193,10 @@ public:
182 return device.CanReportMemoryUsage(); 193 return device.CanReportMemoryUsage();
183 } 194 }
184 195
196 u32 GetStorageBufferAlignment() const {
197 return static_cast<u32>(device.GetShaderStorageBufferAlignment());
198 }
199
185private: 200private:
186 static constexpr std::array PABO_LUT{ 201 static constexpr std::array PABO_LUT{
187 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, 202 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
@@ -232,7 +247,7 @@ struct BufferCacheParams {
232 static constexpr bool NEEDS_BIND_STORAGE_INDEX = true; 247 static constexpr bool NEEDS_BIND_STORAGE_INDEX = true;
233 static constexpr bool USE_MEMORY_MAPS = true; 248 static constexpr bool USE_MEMORY_MAPS = true;
234 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true; 249 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true;
235 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = false; 250 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
236 251
237 // TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads 252 // TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
238 static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false; 253 static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 46d88c664..a6c93068f 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -264,33 +264,33 @@ std::string Device::GetVendorName() const {
264 if (vendor_name == "Intel") { 264 if (vendor_name == "Intel") {
265 // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris. 265 // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris.
266 // Simply return `INTEL` for those as well as the Windows driver. 266 // Simply return `INTEL` for those as well as the Windows driver.
267 return "INTEL"; 267 return "Intel";
268 } 268 }
269 if (vendor_name == "Intel Open Source Technology Center") { 269 if (vendor_name == "Intel Open Source Technology Center") {
270 return "I965"; 270 return "i965";
271 } 271 }
272 if (vendor_name == "Mesa Project") { 272 if (vendor_name == "Mesa Project") {
273 return "I915"; 273 return "i915";
274 } 274 }
275 if (vendor_name == "Mesa/X.org") { 275 if (vendor_name == "Mesa/X.org") {
276 // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return 276 // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return
277 // MESA instead of one of those driver names. 277 // MESA instead of one of those driver names.
278 return "MESA"; 278 return "Mesa";
279 } 279 }
280 if (vendor_name == "AMD") { 280 if (vendor_name == "AMD") {
281 return "RADEONSI"; 281 return "RadeonSI";
282 } 282 }
283 if (vendor_name == "nouveau") { 283 if (vendor_name == "nouveau") {
284 return "NOUVEAU"; 284 return "Nouveau";
285 } 285 }
286 if (vendor_name == "X.Org") { 286 if (vendor_name == "X.Org") {
287 return "R600"; 287 return "R600";
288 } 288 }
289 if (vendor_name == "Collabora Ltd") { 289 if (vendor_name == "Collabora Ltd") {
290 return "ZINK"; 290 return "Zink";
291 } 291 }
292 if (vendor_name == "Intel Corporation") { 292 if (vendor_name == "Intel Corporation") {
293 return "OPENSWR"; 293 return "OpenSWR";
294 } 294 }
295 if (vendor_name == "Microsoft Corporation") { 295 if (vendor_name == "Microsoft Corporation") {
296 return "D3D12"; 296 return "D3D12";
@@ -299,7 +299,7 @@ std::string Device::GetVendorName() const {
299 // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default 299 // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default
300 // strategy would have returned `NVIDIA` here for this driver, the same result as the 300 // strategy would have returned `NVIDIA` here for this driver, the same result as the
301 // proprietary driver. 301 // proprietary driver.
302 return "TEGRA"; 302 return "Tegra";
303 } 303 }
304 return vendor_name; 304 return vendor_name;
305} 305}
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 2888e0238..26f2d0ea7 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -232,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
232 .has_gl_bool_ref_bug = device.HasBoolRefBug(), 232 .has_gl_bool_ref_bug = device.HasBoolRefBug(),
233 .ignore_nan_fp_comparisons = true, 233 .ignore_nan_fp_comparisons = true,
234 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), 234 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
235 .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
235 }, 236 },
236 host_info{ 237 host_info{
237 .support_float64 = true, 238 .support_float64 = true,
@@ -240,6 +241,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
240 .needs_demote_reorder = device.IsAmd(), 241 .needs_demote_reorder = device.IsAmd(),
241 .support_snorm_render_buffer = false, 242 .support_snorm_render_buffer = false,
242 .support_viewport_index_layer = device.HasVertexViewportLayer(), 243 .support_viewport_index_layer = device.HasVertexViewportLayer(),
244 .min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()),
243 .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), 245 .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
244 .support_conditional_barrier = device.SupportsConditionalBarriers(), 246 .support_conditional_barrier = device.SupportsConditionalBarriers(),
245 } { 247 } {
diff --git a/src/video_core/renderer_opengl/gl_staging_buffer_pool.cpp b/src/video_core/renderer_opengl/gl_staging_buffer_pool.cpp
index bbb06e51f..cadad6507 100644
--- a/src/video_core/renderer_opengl/gl_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_opengl/gl_staging_buffer_pool.cpp
@@ -28,63 +28,69 @@ StagingBuffers::StagingBuffers(GLenum storage_flags_, GLenum map_flags_)
28 28
29StagingBuffers::~StagingBuffers() = default; 29StagingBuffers::~StagingBuffers() = default;
30 30
31StagingBufferMap StagingBuffers::RequestMap(size_t requested_size, bool insert_fence) { 31StagingBufferMap StagingBuffers::RequestMap(size_t requested_size, bool insert_fence,
32 bool deferred) {
32 MICROPROFILE_SCOPE(OpenGL_BufferRequest); 33 MICROPROFILE_SCOPE(OpenGL_BufferRequest);
33 34
34 const size_t index = RequestBuffer(requested_size); 35 const size_t index = RequestBuffer(requested_size);
35 OGLSync* const sync = insert_fence ? &syncs[index] : nullptr; 36 OGLSync* const sync = insert_fence ? &allocs[index].sync : nullptr;
36 sync_indices[index] = insert_fence ? ++current_sync_index : 0; 37 allocs[index].sync_index = insert_fence ? ++current_sync_index : 0;
38 allocs[index].deferred = deferred;
37 return StagingBufferMap{ 39 return StagingBufferMap{
38 .mapped_span = std::span(maps[index], requested_size), 40 .mapped_span = std::span(allocs[index].map, requested_size),
39 .sync = sync, 41 .sync = sync,
40 .buffer = buffers[index].handle, 42 .buffer = allocs[index].buffer.handle,
43 .index = index,
41 }; 44 };
42} 45}
43 46
47void StagingBuffers::FreeDeferredStagingBuffer(size_t index) {
48 ASSERT(allocs[index].deferred);
49 allocs[index].deferred = false;
50}
51
44size_t StagingBuffers::RequestBuffer(size_t requested_size) { 52size_t StagingBuffers::RequestBuffer(size_t requested_size) {
45 if (const std::optional<size_t> index = FindBuffer(requested_size); index) { 53 if (const std::optional<size_t> index = FindBuffer(requested_size); index) {
46 return *index; 54 return *index;
47 } 55 }
48 56 StagingBufferAlloc alloc;
49 OGLBuffer& buffer = buffers.emplace_back(); 57 alloc.buffer.Create();
50 buffer.Create();
51 const auto next_pow2_size = Common::NextPow2(requested_size); 58 const auto next_pow2_size = Common::NextPow2(requested_size);
52 glNamedBufferStorage(buffer.handle, next_pow2_size, nullptr, 59 glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr,
53 storage_flags | GL_MAP_PERSISTENT_BIT); 60 storage_flags | GL_MAP_PERSISTENT_BIT);
54 maps.push_back(static_cast<u8*>(glMapNamedBufferRange(buffer.handle, 0, next_pow2_size, 61 alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size,
55 map_flags | GL_MAP_PERSISTENT_BIT))); 62 map_flags | GL_MAP_PERSISTENT_BIT));
56 syncs.emplace_back(); 63 alloc.size = next_pow2_size;
57 sync_indices.emplace_back(); 64 allocs.emplace_back(std::move(alloc));
58 sizes.push_back(next_pow2_size); 65 return allocs.size() - 1;
59
60 ASSERT(syncs.size() == buffers.size() && buffers.size() == maps.size() &&
61 maps.size() == sizes.size());
62
63 return buffers.size() - 1;
64} 66}
65 67
66std::optional<size_t> StagingBuffers::FindBuffer(size_t requested_size) { 68std::optional<size_t> StagingBuffers::FindBuffer(size_t requested_size) {
67 size_t known_unsignaled_index = current_sync_index + 1; 69 size_t known_unsignaled_index = current_sync_index + 1;
68 size_t smallest_buffer = std::numeric_limits<size_t>::max(); 70 size_t smallest_buffer = std::numeric_limits<size_t>::max();
69 std::optional<size_t> found; 71 std::optional<size_t> found;
70 const size_t num_buffers = sizes.size(); 72 const size_t num_buffers = allocs.size();
71 for (size_t index = 0; index < num_buffers; ++index) { 73 for (size_t index = 0; index < num_buffers; ++index) {
72 const size_t buffer_size = sizes[index]; 74 StagingBufferAlloc& alloc = allocs[index];
75 const size_t buffer_size = alloc.size;
73 if (buffer_size < requested_size || buffer_size >= smallest_buffer) { 76 if (buffer_size < requested_size || buffer_size >= smallest_buffer) {
74 continue; 77 continue;
75 } 78 }
76 if (syncs[index].handle != 0) { 79 if (alloc.deferred) {
77 if (sync_indices[index] >= known_unsignaled_index) { 80 continue;
81 }
82 if (alloc.sync.handle != 0) {
83 if (alloc.sync_index >= known_unsignaled_index) {
78 // This fence is later than a fence that is known to not be signaled 84 // This fence is later than a fence that is known to not be signaled
79 continue; 85 continue;
80 } 86 }
81 if (!syncs[index].IsSignaled()) { 87 if (!alloc.sync.IsSignaled()) {
82 // Since this fence hasn't been signaled, it's safe to assume all later 88 // Since this fence hasn't been signaled, it's safe to assume all later
83 // fences haven't been signaled either 89 // fences haven't been signaled either
84 known_unsignaled_index = std::min(known_unsignaled_index, sync_indices[index]); 90 known_unsignaled_index = std::min(known_unsignaled_index, alloc.sync_index);
85 continue; 91 continue;
86 } 92 }
87 syncs[index].Release(); 93 alloc.sync.Release();
88 } 94 }
89 smallest_buffer = buffer_size; 95 smallest_buffer = buffer_size;
90 found = index; 96 found = index;
@@ -143,8 +149,12 @@ StagingBufferMap StagingBufferPool::RequestUploadBuffer(size_t size) {
143 return upload_buffers.RequestMap(size, true); 149 return upload_buffers.RequestMap(size, true);
144} 150}
145 151
146StagingBufferMap StagingBufferPool::RequestDownloadBuffer(size_t size) { 152StagingBufferMap StagingBufferPool::RequestDownloadBuffer(size_t size, bool deferred) {
147 return download_buffers.RequestMap(size, false); 153 return download_buffers.RequestMap(size, false, deferred);
154}
155
156void StagingBufferPool::FreeDeferredStagingBuffer(StagingBufferMap& buffer) {
157 download_buffers.FreeDeferredStagingBuffer(buffer.index);
148} 158}
149 159
150} // namespace OpenGL 160} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_staging_buffer_pool.h b/src/video_core/renderer_opengl/gl_staging_buffer_pool.h
index 60f72d3a0..07a56b4d2 100644
--- a/src/video_core/renderer_opengl/gl_staging_buffer_pool.h
+++ b/src/video_core/renderer_opengl/gl_staging_buffer_pool.h
@@ -26,23 +26,30 @@ struct StagingBufferMap {
26 size_t offset = 0; 26 size_t offset = 0;
27 OGLSync* sync; 27 OGLSync* sync;
28 GLuint buffer; 28 GLuint buffer;
29 size_t index;
29}; 30};
30 31
31struct StagingBuffers { 32struct StagingBuffers {
32 explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_); 33 explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_);
33 ~StagingBuffers(); 34 ~StagingBuffers();
34 35
35 StagingBufferMap RequestMap(size_t requested_size, bool insert_fence); 36 StagingBufferMap RequestMap(size_t requested_size, bool insert_fence, bool deferred = false);
37
38 void FreeDeferredStagingBuffer(size_t index);
36 39
37 size_t RequestBuffer(size_t requested_size); 40 size_t RequestBuffer(size_t requested_size);
38 41
39 std::optional<size_t> FindBuffer(size_t requested_size); 42 std::optional<size_t> FindBuffer(size_t requested_size);
40 43
41 std::vector<OGLSync> syncs; 44 struct StagingBufferAlloc {
42 std::vector<OGLBuffer> buffers; 45 OGLSync sync;
43 std::vector<u8*> maps; 46 OGLBuffer buffer;
44 std::vector<size_t> sizes; 47 u8* map;
45 std::vector<size_t> sync_indices; 48 size_t size;
49 size_t sync_index;
50 bool deferred;
51 };
52 std::vector<StagingBufferAlloc> allocs;
46 GLenum storage_flags; 53 GLenum storage_flags;
47 GLenum map_flags; 54 GLenum map_flags;
48 size_t current_sync_index = 0; 55 size_t current_sync_index = 0;
@@ -85,7 +92,8 @@ public:
85 ~StagingBufferPool() = default; 92 ~StagingBufferPool() = default;
86 93
87 StagingBufferMap RequestUploadBuffer(size_t size); 94 StagingBufferMap RequestUploadBuffer(size_t size);
88 StagingBufferMap RequestDownloadBuffer(size_t size); 95 StagingBufferMap RequestDownloadBuffer(size_t size, bool deferred = false);
96 void FreeDeferredStagingBuffer(StagingBufferMap& buffer);
89 97
90private: 98private:
91 StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT}; 99 StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 512eef575..66a5ca03e 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -557,8 +557,12 @@ StagingBufferMap TextureCacheRuntime::UploadStagingBuffer(size_t size) {
557 return staging_buffer_pool.RequestUploadBuffer(size); 557 return staging_buffer_pool.RequestUploadBuffer(size);
558} 558}
559 559
560StagingBufferMap TextureCacheRuntime::DownloadStagingBuffer(size_t size) { 560StagingBufferMap TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
561 return staging_buffer_pool.RequestDownloadBuffer(size); 561 return staging_buffer_pool.RequestDownloadBuffer(size, deferred);
562}
563
564void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferMap& buffer) {
565 staging_buffer_pool.FreeDeferredStagingBuffer(buffer);
562} 566}
563 567
564u64 TextureCacheRuntime::GetDeviceMemoryUsage() const { 568u64 TextureCacheRuntime::GetDeviceMemoryUsage() const {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index e71b87e99..34870c81f 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -74,7 +74,9 @@ public:
74 74
75 StagingBufferMap UploadStagingBuffer(size_t size); 75 StagingBufferMap UploadStagingBuffer(size_t size);
76 76
77 StagingBufferMap DownloadStagingBuffer(size_t size); 77 StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false);
78
79 void FreeDeferredStagingBuffer(StagingBufferMap& buffer);
78 80
79 u64 GetDeviceLocalMemory() const { 81 u64 GetDeviceLocalMemory() const {
80 return device_access_memory; 82 return device_access_memory;
@@ -359,7 +361,7 @@ struct TextureCacheParams {
359 static constexpr bool FRAMEBUFFER_BLITS = true; 361 static constexpr bool FRAMEBUFFER_BLITS = true;
360 static constexpr bool HAS_EMULATED_COPIES = true; 362 static constexpr bool HAS_EMULATED_COPIES = true;
361 static constexpr bool HAS_DEVICE_MEMORY_INFO = true; 363 static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
362 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = false; 364 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
363 365
364 using Runtime = OpenGL::TextureCacheRuntime; 366 using Runtime = OpenGL::TextureCacheRuntime;
365 using Image = OpenGL::Image; 367 using Image = OpenGL::Image;
@@ -367,7 +369,7 @@ struct TextureCacheParams {
367 using ImageView = OpenGL::ImageView; 369 using ImageView = OpenGL::ImageView;
368 using Sampler = OpenGL::Sampler; 370 using Sampler = OpenGL::Sampler;
369 using Framebuffer = OpenGL::Framebuffer; 371 using Framebuffer = OpenGL::Framebuffer;
370 using AsyncBuffer = u32; 372 using AsyncBuffer = OpenGL::StagingBufferMap;
371 using BufferType = GLuint; 373 using BufferType = GLuint;
372}; 374};
373 375
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 66483a900..5e461fbd0 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -1211,7 +1211,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1211 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); 1211 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1212 return; 1212 return;
1213 } 1213 }
1214 aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer)); 1214 aa_renderpass = CreateRenderPassImpl(VK_FORMAT_R16G16B16A16_SFLOAT);
1215 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); 1215 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1216 1216
1217 const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{ 1217 const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index d8148e89a..5958f52f7 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -79,13 +79,13 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
79} // Anonymous namespace 79} // Anonymous namespace
80 80
81Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) 81Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
82 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params) {} 82 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {}
83 83
84Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, 84Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
85 VAddr cpu_addr_, u64 size_bytes_) 85 VAddr cpu_addr_, u64 size_bytes_)
86 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_), 86 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_),
87 device{&runtime.device}, buffer{ 87 device{&runtime.device}, buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())},
88 CreateBuffer(*device, runtime.memory_allocator, SizeBytes())} { 88 tracker{SizeBytes()} {
89 if (runtime.device.HasDebuggingToolAttached()) { 89 if (runtime.device.HasDebuggingToolAttached()) {
90 buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str()); 90 buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
91 } 91 }
@@ -355,12 +355,35 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const {
355 return device.CanReportMemoryUsage(); 355 return device.CanReportMemoryUsage();
356} 356}
357 357
358u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
359 return static_cast<u32>(device.GetStorageBufferAlignment());
360}
361
362void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept {
363 for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
364 it->ResetUsageTracking();
365 }
366}
367
358void BufferCacheRuntime::Finish() { 368void BufferCacheRuntime::Finish() {
359 scheduler.Finish(); 369 scheduler.Finish();
360} 370}
361 371
372bool BufferCacheRuntime::CanReorderUpload(const Buffer& buffer,
373 std::span<const VideoCommon::BufferCopy> copies) {
374 if (Settings::values.disable_buffer_reorder) {
375 return false;
376 }
377 const bool can_use_upload_cmdbuf =
378 std::ranges::all_of(copies, [&](const VideoCommon::BufferCopy& copy) {
379 return !buffer.IsRegionUsed(copy.dst_offset, copy.size);
380 });
381 return can_use_upload_cmdbuf;
382}
383
362void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, 384void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
363 std::span<const VideoCommon::BufferCopy> copies, bool barrier) { 385 std::span<const VideoCommon::BufferCopy> copies, bool barrier,
386 bool can_reorder_upload) {
364 if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { 387 if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) {
365 return; 388 return;
366 } 389 }
@@ -376,9 +399,18 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
376 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, 399 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
377 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, 400 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
378 }; 401 };
402
379 // Measuring a popular game, this number never exceeds the specified size once data is warmed up 403 // Measuring a popular game, this number never exceeds the specified size once data is warmed up
380 boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size()); 404 boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size());
381 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); 405 std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy);
406 if (src_buffer == staging_pool.StreamBuf() && can_reorder_upload) {
407 scheduler.RecordWithUploadBuffer([src_buffer, dst_buffer, vk_copies](
408 vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) {
409 upload_cmdbuf.CopyBuffer(src_buffer, dst_buffer, vk_copies);
410 });
411 return;
412 }
413
382 scheduler.RequestOutsideRenderPassOperationContext(); 414 scheduler.RequestOutsideRenderPassOperationContext();
383 scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { 415 scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) {
384 if (barrier) { 416 if (barrier) {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 95446c732..0b3fbd6d0 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -5,6 +5,7 @@
5 5
6#include "video_core/buffer_cache/buffer_cache_base.h" 6#include "video_core/buffer_cache/buffer_cache_base.h"
7#include "video_core/buffer_cache/memory_tracker_base.h" 7#include "video_core/buffer_cache/memory_tracker_base.h"
8#include "video_core/buffer_cache/usage_tracker.h"
8#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
9#include "video_core/renderer_vulkan/vk_compute_pass.h" 10#include "video_core/renderer_vulkan/vk_compute_pass.h"
10#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 11#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -34,6 +35,18 @@ public:
34 return *buffer; 35 return *buffer;
35 } 36 }
36 37
38 [[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
39 return tracker.IsUsed(offset, size);
40 }
41
42 void MarkUsage(u64 offset, u64 size) noexcept {
43 tracker.Track(offset, size);
44 }
45
46 void ResetUsageTracking() noexcept {
47 tracker.Reset();
48 }
49
37 operator VkBuffer() const noexcept { 50 operator VkBuffer() const noexcept {
38 return *buffer; 51 return *buffer;
39 } 52 }
@@ -49,6 +62,7 @@ private:
49 const Device* device{}; 62 const Device* device{};
50 vk::Buffer buffer; 63 vk::Buffer buffer;
51 std::vector<BufferView> views; 64 std::vector<BufferView> views;
65 VideoCommon::UsageTracker tracker;
52}; 66};
53 67
54class QuadArrayIndexBuffer; 68class QuadArrayIndexBuffer;
@@ -67,6 +81,8 @@ public:
67 ComputePassDescriptorQueue& compute_pass_descriptor_queue, 81 ComputePassDescriptorQueue& compute_pass_descriptor_queue,
68 DescriptorPool& descriptor_pool); 82 DescriptorPool& descriptor_pool);
69 83
84 void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept;
85
70 void Finish(); 86 void Finish();
71 87
72 u64 GetDeviceLocalMemory() const; 88 u64 GetDeviceLocalMemory() const;
@@ -75,16 +91,21 @@ public:
75 91
76 bool CanReportMemoryUsage() const; 92 bool CanReportMemoryUsage() const;
77 93
94 u32 GetStorageBufferAlignment() const;
95
78 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); 96 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
79 97
80 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); 98 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);
81 99
100 bool CanReorderUpload(const Buffer& buffer, std::span<const VideoCommon::BufferCopy> copies);
101
82 void FreeDeferredStagingBuffer(StagingBufferRef& ref); 102 void FreeDeferredStagingBuffer(StagingBufferRef& ref);
83 103
84 void PreCopyBarrier(); 104 void PreCopyBarrier();
85 105
86 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, 106 void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
87 std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); 107 std::span<const VideoCommon::BufferCopy> copies, bool barrier,
108 bool can_reorder_upload = false);
88 109
89 void PostCopyBarrier(); 110 void PostCopyBarrier();
90 111
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index 6b288b994..ac8b6e838 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -100,12 +100,14 @@ void MasterSemaphore::Wait(u64 tick) {
100 Refresh(); 100 Refresh();
101} 101}
102 102
103VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 103VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
104 VkSemaphore wait_semaphore, u64 host_tick) { 104 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
105 u64 host_tick) {
105 if (semaphore) { 106 if (semaphore) {
106 return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick); 107 return SubmitQueueTimeline(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore,
108 host_tick);
107 } else { 109 } else {
108 return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick); 110 return SubmitQueueFence(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, host_tick);
109 } 111 }
110} 112}
111 113
@@ -115,6 +117,7 @@ static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
115}; 117};
116 118
117VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, 119VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
120 vk::CommandBuffer& upload_cmdbuf,
118 VkSemaphore signal_semaphore, 121 VkSemaphore signal_semaphore,
119 VkSemaphore wait_semaphore, u64 host_tick) { 122 VkSemaphore wait_semaphore, u64 host_tick) {
120 const VkSemaphore timeline_semaphore = *semaphore; 123 const VkSemaphore timeline_semaphore = *semaphore;
@@ -123,6 +126,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
123 const std::array signal_values{host_tick, u64(0)}; 126 const std::array signal_values{host_tick, u64(0)};
124 const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; 127 const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
125 128
129 const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf};
130
126 const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; 131 const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
127 const VkTimelineSemaphoreSubmitInfo timeline_si{ 132 const VkTimelineSemaphoreSubmitInfo timeline_si{
128 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, 133 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
@@ -138,8 +143,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
138 .waitSemaphoreCount = num_wait_semaphores, 143 .waitSemaphoreCount = num_wait_semaphores,
139 .pWaitSemaphores = &wait_semaphore, 144 .pWaitSemaphores = &wait_semaphore,
140 .pWaitDstStageMask = wait_stage_masks.data(), 145 .pWaitDstStageMask = wait_stage_masks.data(),
141 .commandBufferCount = 1, 146 .commandBufferCount = static_cast<u32>(cmdbuffers.size()),
142 .pCommandBuffers = cmdbuf.address(), 147 .pCommandBuffers = cmdbuffers.data(),
143 .signalSemaphoreCount = num_signal_semaphores, 148 .signalSemaphoreCount = num_signal_semaphores,
144 .pSignalSemaphores = signal_semaphores.data(), 149 .pSignalSemaphores = signal_semaphores.data(),
145 }; 150 };
@@ -147,19 +152,23 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
147 return device.GetGraphicsQueue().Submit(submit_info); 152 return device.GetGraphicsQueue().Submit(submit_info);
148} 153}
149 154
150VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 155VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf,
151 VkSemaphore wait_semaphore, u64 host_tick) { 156 vk::CommandBuffer& upload_cmdbuf,
157 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
158 u64 host_tick) {
152 const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; 159 const u32 num_signal_semaphores = signal_semaphore ? 1 : 0;
153 const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; 160 const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
154 161
162 const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf};
163
155 const VkSubmitInfo submit_info{ 164 const VkSubmitInfo submit_info{
156 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, 165 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
157 .pNext = nullptr, 166 .pNext = nullptr,
158 .waitSemaphoreCount = num_wait_semaphores, 167 .waitSemaphoreCount = num_wait_semaphores,
159 .pWaitSemaphores = &wait_semaphore, 168 .pWaitSemaphores = &wait_semaphore,
160 .pWaitDstStageMask = wait_stage_masks.data(), 169 .pWaitDstStageMask = wait_stage_masks.data(),
161 .commandBufferCount = 1, 170 .commandBufferCount = static_cast<u32>(cmdbuffers.size()),
162 .pCommandBuffers = cmdbuf.address(), 171 .pCommandBuffers = cmdbuffers.data(),
163 .signalSemaphoreCount = num_signal_semaphores, 172 .signalSemaphoreCount = num_signal_semaphores,
164 .pSignalSemaphores = &signal_semaphore, 173 .pSignalSemaphores = &signal_semaphore,
165 }; 174 };
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 3f599d7bd..7dfb93ffb 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -52,14 +52,16 @@ public:
52 void Wait(u64 tick); 52 void Wait(u64 tick);
53 53
54 /// Submits the device graphics queue, updating the tick as necessary 54 /// Submits the device graphics queue, updating the tick as necessary
55 VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 55 VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
56 VkSemaphore wait_semaphore, u64 host_tick); 56 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, u64 host_tick);
57 57
58private: 58private:
59 VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 59 VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
60 VkSemaphore wait_semaphore, u64 host_tick); 60 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
61 VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, 61 u64 host_tick);
62 VkSemaphore wait_semaphore, u64 host_tick); 62 VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
63 VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
64 u64 host_tick);
63 65
64 void WaitThread(std::stop_token token); 66 void WaitThread(std::stop_token token);
65 67
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 22bf8cc77..2a13b2a72 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -263,6 +263,22 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
263 info.y_negate = key.state.y_negate != 0; 263 info.y_negate = key.state.y_negate != 0;
264 return info; 264 return info;
265} 265}
266
267size_t GetTotalPipelineWorkers() {
268 const size_t max_core_threads =
269 std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL;
270#ifdef ANDROID
271 // Leave at least a few cores free in android
272 constexpr size_t free_cores = 3ULL;
273 if (max_core_threads <= free_cores) {
274 return 1ULL;
275 }
276 return max_core_threads - free_cores;
277#else
278 return max_core_threads;
279#endif
280}
281
266} // Anonymous namespace 282} // Anonymous namespace
267 283
268size_t ComputePipelineCacheKey::Hash() const noexcept { 284size_t ComputePipelineCacheKey::Hash() const noexcept {
@@ -294,11 +310,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
294 texture_cache{texture_cache_}, shader_notify{shader_notify_}, 310 texture_cache{texture_cache_}, shader_notify{shader_notify_},
295 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, 311 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
296 use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, 312 use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()},
297#ifdef ANDROID 313 workers(device.HasBrokenParallelShaderCompiling() ? 1ULL : GetTotalPipelineWorkers(),
298 workers(1, "VkPipelineBuilder"), 314 "VkPipelineBuilder"),
299#else
300 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"),
301#endif
302 serialization_thread(1, "VkPipelineSerialization") { 315 serialization_thread(1, "VkPipelineSerialization") {
303 const auto& float_control{device.FloatControlProperties()}; 316 const auto& float_control{device.FloatControlProperties()};
304 const VkDriverId driver_id{device.GetDriverID()}; 317 const VkDriverId driver_id{device.GetDriverID()};
@@ -338,6 +351,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
338 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), 351 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
339 .support_native_ndc = device.IsExtDepthClipControlSupported(), 352 .support_native_ndc = device.IsExtDepthClipControlSupported(),
340 .support_scaled_attributes = !device.MustEmulateScaledFormats(), 353 .support_scaled_attributes = !device.MustEmulateScaledFormats(),
354 .support_multi_viewport = device.SupportsMultiViewport(),
341 355
342 .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), 356 .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
343 357
@@ -359,6 +373,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
359 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, 373 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
360 .has_broken_robust = 374 .has_broken_robust =
361 device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, 375 device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
376 .min_ssbo_alignment = device.GetStorageBufferAlignment(),
362 }; 377 };
363 378
364 host_info = Shader::HostTranslateInfo{ 379 host_info = Shader::HostTranslateInfo{
@@ -369,6 +384,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
369 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, 384 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE,
370 .support_snorm_render_buffer = true, 385 .support_snorm_render_buffer = true,
371 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), 386 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
387 .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()),
372 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), 388 .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
373 .support_conditional_barrier = device.SupportsConditionalBarriers(), 389 .support_conditional_barrier = device.SupportsConditionalBarriers(),
374 }; 390 };
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index e0ab1eaac..07222e603 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -892,10 +892,6 @@ void RasterizerVulkan::UpdateDynamicStates() {
892 UpdateFrontFace(regs); 892 UpdateFrontFace(regs);
893 UpdateStencilOp(regs); 893 UpdateStencilOp(regs);
894 894
895 if (device.IsExtVertexInputDynamicStateSupported()) {
896 UpdateVertexInput(regs);
897 }
898
899 if (state_tracker.TouchStateEnable()) { 895 if (state_tracker.TouchStateEnable()) {
900 UpdateDepthBoundsTestEnable(regs); 896 UpdateDepthBoundsTestEnable(regs);
901 UpdateDepthTestEnable(regs); 897 UpdateDepthTestEnable(regs);
@@ -918,6 +914,9 @@ void RasterizerVulkan::UpdateDynamicStates() {
918 UpdateBlending(regs); 914 UpdateBlending(regs);
919 } 915 }
920 } 916 }
917 if (device.IsExtVertexInputDynamicStateSupported()) {
918 UpdateVertexInput(regs);
919 }
921} 920}
922 921
923void RasterizerVulkan::HandleTransformFeedback() { 922void RasterizerVulkan::HandleTransformFeedback() {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 3be7837f4..146923db4 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -22,11 +22,12 @@ namespace Vulkan {
22 22
23MICROPROFILE_DECLARE(Vulkan_WaitForWorker); 23MICROPROFILE_DECLARE(Vulkan_WaitForWorker);
24 24
25void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) { 25void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
26 vk::CommandBuffer upload_cmdbuf) {
26 auto command = first; 27 auto command = first;
27 while (command != nullptr) { 28 while (command != nullptr) {
28 auto next = command->GetNext(); 29 auto next = command->GetNext();
29 command->Execute(cmdbuf); 30 command->Execute(cmdbuf, upload_cmdbuf);
30 command->~Command(); 31 command->~Command();
31 command = next; 32 command = next;
32 } 33 }
@@ -180,7 +181,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) {
180 // Perform the work, tracking whether the chunk was a submission 181 // Perform the work, tracking whether the chunk was a submission
181 // before executing. 182 // before executing.
182 const bool has_submit = work->HasSubmit(); 183 const bool has_submit = work->HasSubmit();
183 work->ExecuteAll(current_cmdbuf); 184 work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf);
184 185
185 // If the chunk was a submission, reallocate the command buffer. 186 // If the chunk was a submission, reallocate the command buffer.
186 if (has_submit) { 187 if (has_submit) {
@@ -205,6 +206,13 @@ void Scheduler::AllocateWorkerCommandBuffer() {
205 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, 206 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
206 .pInheritanceInfo = nullptr, 207 .pInheritanceInfo = nullptr,
207 }); 208 });
209 current_upload_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
210 current_upload_cmdbuf.Begin({
211 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
212 .pNext = nullptr,
213 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
214 .pInheritanceInfo = nullptr,
215 });
208} 216}
209 217
210u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 218u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
@@ -212,7 +220,17 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
212 InvalidateState(); 220 InvalidateState();
213 221
214 const u64 signal_value = master_semaphore->NextTick(); 222 const u64 signal_value = master_semaphore->NextTick();
215 Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { 223 RecordWithUploadBuffer([signal_semaphore, wait_semaphore, signal_value,
224 this](vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) {
225 static constexpr VkMemoryBarrier WRITE_BARRIER{
226 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
227 .pNext = nullptr,
228 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
229 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
230 };
231 upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
232 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER);
233 upload_cmdbuf.End();
216 cmdbuf.End(); 234 cmdbuf.End();
217 235
218 if (on_submit) { 236 if (on_submit) {
@@ -221,7 +239,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
221 239
222 std::scoped_lock lock{submit_mutex}; 240 std::scoped_lock lock{submit_mutex};
223 switch (const VkResult result = master_semaphore->SubmitQueue( 241 switch (const VkResult result = master_semaphore->SubmitQueue(
224 cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { 242 cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
225 case VK_SUCCESS: 243 case VK_SUCCESS:
226 break; 244 break;
227 case VK_ERROR_DEVICE_LOST: 245 case VK_ERROR_DEVICE_LOST:
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index da03803aa..f8d8ca80a 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -80,7 +80,8 @@ public:
80 80
81 /// Send work to a separate thread. 81 /// Send work to a separate thread.
82 template <typename T> 82 template <typename T>
83 void Record(T&& command) { 83 requires std::is_invocable_v<T, vk::CommandBuffer, vk::CommandBuffer>
84 void RecordWithUploadBuffer(T&& command) {
84 if (chunk->Record(command)) { 85 if (chunk->Record(command)) {
85 return; 86 return;
86 } 87 }
@@ -88,6 +89,15 @@ public:
88 (void)chunk->Record(command); 89 (void)chunk->Record(command);
89 } 90 }
90 91
92 template <typename T>
93 requires std::is_invocable_v<T, vk::CommandBuffer>
94 void Record(T&& c) {
95 this->RecordWithUploadBuffer(
96 [command = std::move(c)](vk::CommandBuffer cmdbuf, vk::CommandBuffer) {
97 command(cmdbuf);
98 });
99 }
100
91 /// Returns the current command buffer tick. 101 /// Returns the current command buffer tick.
92 [[nodiscard]] u64 CurrentTick() const noexcept { 102 [[nodiscard]] u64 CurrentTick() const noexcept {
93 return master_semaphore->CurrentTick(); 103 return master_semaphore->CurrentTick();
@@ -119,7 +129,7 @@ private:
119 public: 129 public:
120 virtual ~Command() = default; 130 virtual ~Command() = default;
121 131
122 virtual void Execute(vk::CommandBuffer cmdbuf) const = 0; 132 virtual void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const = 0;
123 133
124 Command* GetNext() const { 134 Command* GetNext() const {
125 return next; 135 return next;
@@ -142,8 +152,8 @@ private:
142 TypedCommand(TypedCommand&&) = delete; 152 TypedCommand(TypedCommand&&) = delete;
143 TypedCommand& operator=(TypedCommand&&) = delete; 153 TypedCommand& operator=(TypedCommand&&) = delete;
144 154
145 void Execute(vk::CommandBuffer cmdbuf) const override { 155 void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const override {
146 command(cmdbuf); 156 command(cmdbuf, upload_cmdbuf);
147 } 157 }
148 158
149 private: 159 private:
@@ -152,7 +162,7 @@ private:
152 162
153 class CommandChunk final { 163 class CommandChunk final {
154 public: 164 public:
155 void ExecuteAll(vk::CommandBuffer cmdbuf); 165 void ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf);
156 166
157 template <typename T> 167 template <typename T>
158 bool Record(T& command) { 168 bool Record(T& command) {
@@ -228,6 +238,7 @@ private:
228 VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr; 238 VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr;
229 239
230 vk::CommandBuffer current_cmdbuf; 240 vk::CommandBuffer current_cmdbuf;
241 vk::CommandBuffer current_upload_cmdbuf;
231 242
232 std::unique_ptr<CommandChunk> chunk; 243 std::unique_ptr<CommandChunk> chunk;
233 std::function<void()> on_submit; 244 std::function<void()> on_submit;
diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/vk_smaa.cpp
index 5efd7d66e..70644ea82 100644
--- a/src/video_core/renderer_vulkan/vk_smaa.cpp
+++ b/src/video_core/renderer_vulkan/vk_smaa.cpp
@@ -672,7 +672,7 @@ void SMAA::UploadImages(Scheduler& scheduler) {
672 UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, 672 UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent,
673 VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); 673 VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes));
674 674
675 scheduler.Record([&](vk::CommandBuffer& cmdbuf) { 675 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
676 for (auto& images : m_dynamic_images) { 676 for (auto& images : m_dynamic_images) {
677 for (size_t i = 0; i < MaxDynamicImage; i++) { 677 for (size_t i = 0; i < MaxDynamicImage; i++) {
678 ClearColorImage(cmdbuf, *images.images[i]); 678 ClearColorImage(cmdbuf, *images.images[i]);
@@ -707,7 +707,7 @@ VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_
707 UpdateDescriptorSets(source_image_view, image_index); 707 UpdateDescriptorSets(source_image_view, image_index);
708 708
709 scheduler.RequestOutsideRenderPassOperationContext(); 709 scheduler.RequestOutsideRenderPassOperationContext();
710 scheduler.Record([=, this](vk::CommandBuffer& cmdbuf) { 710 scheduler.Record([=, this](vk::CommandBuffer cmdbuf) {
711 TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); 711 TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
712 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); 712 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
713 BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, 713 BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer,
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
index d3deb9072..f63a20327 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -36,6 +36,10 @@ public:
36 StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); 36 StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
37 void FreeDeferred(StagingBufferRef& ref); 37 void FreeDeferred(StagingBufferRef& ref);
38 38
39 [[nodiscard]] VkBuffer StreamBuf() const noexcept {
40 return *stream_buffer;
41 }
42
39 void TickFrame(); 43 void TickFrame();
40 44
41private: 45private:
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index de34f6d49..38b1619df 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1439,7 +1439,7 @@ void Image::UploadMemory(const StagingBufferRef& map, std::span<const BufferImag
1439 UploadMemory(map.buffer, map.offset, copies); 1439 UploadMemory(map.buffer, map.offset, copies);
1440} 1440}
1441 1441
1442void Image::DownloadMemory(VkBuffer buffer, VkDeviceSize offset, 1442void Image::DownloadMemory(VkBuffer buffer, size_t offset,
1443 std::span<const VideoCommon::BufferImageCopy> copies) { 1443 std::span<const VideoCommon::BufferImageCopy> copies) {
1444 std::array buffer_handles{ 1444 std::array buffer_handles{
1445 buffer, 1445 buffer,
@@ -1450,7 +1450,7 @@ void Image::DownloadMemory(VkBuffer buffer, VkDeviceSize offset,
1450 DownloadMemory(buffer_handles, buffer_offsets, copies); 1450 DownloadMemory(buffer_handles, buffer_offsets, copies);
1451} 1451}
1452 1452
1453void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceSize> offsets_span, 1453void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<size_t> offsets_span,
1454 std::span<const VideoCommon::BufferImageCopy> copies) { 1454 std::span<const VideoCommon::BufferImageCopy> copies) {
1455 const bool is_rescaled = True(flags & ImageFlagBits::Rescaled); 1455 const bool is_rescaled = True(flags & ImageFlagBits::Rescaled);
1456 if (is_rescaled) { 1456 if (is_rescaled) {
@@ -1530,7 +1530,7 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
1530 map.buffer, 1530 map.buffer,
1531 }; 1531 };
1532 std::array offsets{ 1532 std::array offsets{
1533 map.offset, 1533 static_cast<size_t>(map.offset),
1534 }; 1534 };
1535 DownloadMemory(buffers, offsets, copies); 1535 DownloadMemory(buffers, offsets, copies);
1536} 1536}
@@ -1785,8 +1785,22 @@ ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
1785 : VideoCommon::ImageViewBase{info, view_info, gpu_addr_}, 1785 : VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
1786 buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {} 1786 buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {}
1787 1787
1788ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams& params) 1788ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params)
1789 : VideoCommon::ImageViewBase{params} {} 1789 : VideoCommon::ImageViewBase{params}, device{&runtime.device} {
1790 if (device->HasNullDescriptor()) {
1791 return;
1792 }
1793
1794 // Handle fallback for devices without nullDescriptor
1795 ImageInfo info{};
1796 info.format = PixelFormat::A8B8G8R8_UNORM;
1797
1798 null_image = MakeImage(*device, runtime.memory_allocator, info, {});
1799 image_handle = *null_image;
1800 for (u32 i = 0; i < Shader::NUM_TEXTURE_TYPES; i++) {
1801 image_views[i] = MakeView(VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_ASPECT_COLOR_BIT);
1802 }
1803}
1790 1804
1791ImageView::~ImageView() = default; 1805ImageView::~ImageView() = default;
1792 1806
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 7a0807709..0dbde65d6 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -147,10 +147,10 @@ public:
147 void UploadMemory(const StagingBufferRef& map, 147 void UploadMemory(const StagingBufferRef& map,
148 std::span<const VideoCommon::BufferImageCopy> copies); 148 std::span<const VideoCommon::BufferImageCopy> copies);
149 149
150 void DownloadMemory(VkBuffer buffer, VkDeviceSize offset, 150 void DownloadMemory(VkBuffer buffer, size_t offset,
151 std::span<const VideoCommon::BufferImageCopy> copies); 151 std::span<const VideoCommon::BufferImageCopy> copies);
152 152
153 void DownloadMemory(std::span<VkBuffer> buffers, std::span<VkDeviceSize> offsets, 153 void DownloadMemory(std::span<VkBuffer> buffers, std::span<size_t> offsets,
154 std::span<const VideoCommon::BufferImageCopy> copies); 154 std::span<const VideoCommon::BufferImageCopy> copies);
155 155
156 void DownloadMemory(const StagingBufferRef& map, 156 void DownloadMemory(const StagingBufferRef& map,
@@ -267,6 +267,7 @@ private:
267 vk::ImageView depth_view; 267 vk::ImageView depth_view;
268 vk::ImageView stencil_view; 268 vk::ImageView stencil_view;
269 vk::ImageView color_view; 269 vk::ImageView color_view;
270 vk::Image null_image;
270 VkImage image_handle = VK_NULL_HANDLE; 271 VkImage image_handle = VK_NULL_HANDLE;
271 VkImageView render_target = VK_NULL_HANDLE; 272 VkImageView render_target = VK_NULL_HANDLE;
272 VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; 273 VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index 9df6a2903..3ffa2a661 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.h
@@ -138,6 +138,10 @@ public:
138 return Iterator(this, SlotId{SlotId::INVALID_INDEX}); 138 return Iterator(this, SlotId{SlotId::INVALID_INDEX});
139 } 139 }
140 140
141 [[nodiscard]] size_t size() const noexcept {
142 return values_capacity - free_list.size();
143 }
144
141private: 145private:
142 struct NonTrivialDummy { 146 struct NonTrivialDummy {
143 NonTrivialDummy() noexcept {} 147 NonTrivialDummy() noexcept {}
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index d575c57ca..dade38b18 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -995,7 +995,7 @@ void TextureCache<P>::DownloadImageIntoBuffer(typename TextureCache<P>::Image* i
995 buffer, 995 buffer,
996 download_map.buffer, 996 download_map.buffer,
997 }; 997 };
998 std::array<u64, 2> buffer_offsets{ 998 std::array<size_t, 2> buffer_offsets{
999 buffer_offset, 999 buffer_offset,
1000 download_map.offset, 1000 download_map.offset,
1001 }; 1001 };
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index e518756d2..1fda0042d 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -519,10 +519,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
519 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); 519 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
520 RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, 520 RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
521 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 521 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
522
523 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2");
524 RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
525 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
526 } 522 }
527 523
528 if (is_nvidia) { 524 if (is_nvidia) {
@@ -611,17 +607,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
611 } 607 }
612 } 608 }
613 if (extensions.vertex_input_dynamic_state && is_qualcomm) { 609 if (extensions.vertex_input_dynamic_state && is_qualcomm) {
614 const u32 version = (properties.properties.driverVersion << 3) >> 3; 610 // Qualcomm drivers do not properly support vertex_input_dynamic_state.
615 if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) && 611 LOG_WARNING(Render_Vulkan,
616 version < VK_MAKE_API_VERSION(0, 0, 680, 0)) { 612 "Qualcomm drivers have broken VK_EXT_vertex_input_dynamic_state");
617 // Qualcomm Adreno 7xx drivers do not properly support vertex_input_dynamic_state. 613 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
618 LOG_WARNING( 614 features.vertex_input_dynamic_state,
619 Render_Vulkan, 615 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
620 "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state");
621 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
622 features.vertex_input_dynamic_state,
623 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
624 }
625 } 616 }
626 617
627 sets_per_pool = 64; 618 sets_per_pool = 64;
@@ -635,6 +626,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
635 has_broken_cube_compatibility = true; 626 has_broken_cube_compatibility = true;
636 } 627 }
637 } 628 }
629 if (is_qualcomm) {
630 const u32 version = (properties.properties.driverVersion << 3) >> 3;
631 if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) {
632 has_broken_parallel_compiling = true;
633 }
634 }
638 if (extensions.sampler_filter_minmax && is_amd) { 635 if (extensions.sampler_filter_minmax && is_amd) {
639 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. 636 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
640 if (!features.shader_float16_int8.shaderFloat16) { 637 if (!features.shader_float16_int8.shaderFloat16) {
@@ -698,6 +695,22 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
698 std::min(properties.properties.limits.maxVertexInputBindings, 16U); 695 std::min(properties.properties.limits.maxVertexInputBindings, 16U);
699 } 696 }
700 697
698 if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) {
699 LOG_INFO(Render_Vulkan,
700 "Removing extendedDynamicState2 due to missing extendedDynamicState");
701 RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
702 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
703 }
704
705 if (!extensions.extended_dynamic_state2 && extensions.extended_dynamic_state3) {
706 LOG_INFO(Render_Vulkan,
707 "Removing extendedDynamicState3 due to missing extendedDynamicState2");
708 RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3,
709 VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
710 dynamic_state3_blending = false;
711 dynamic_state3_enables = false;
712 }
713
701 logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), 714 logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions),
702 first_next, dld); 715 first_next, dld);
703 716
@@ -841,11 +854,41 @@ std::string Device::GetDriverName() const {
841 case VK_DRIVER_ID_NVIDIA_PROPRIETARY: 854 case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
842 return "NVIDIA"; 855 return "NVIDIA";
843 case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: 856 case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
844 return "INTEL"; 857 return "Intel";
845 case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: 858 case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
846 return "ANV"; 859 return "ANV";
860 case VK_DRIVER_ID_IMAGINATION_PROPRIETARY:
861 return "PowerVR";
862 case VK_DRIVER_ID_QUALCOMM_PROPRIETARY:
863 return "Qualcomm";
864 case VK_DRIVER_ID_ARM_PROPRIETARY:
865 return "Mali";
866 case VK_DRIVER_ID_GOOGLE_SWIFTSHADER:
867 return "SwiftShader";
868 case VK_DRIVER_ID_BROADCOM_PROPRIETARY:
869 return "Broadcom";
847 case VK_DRIVER_ID_MESA_LLVMPIPE: 870 case VK_DRIVER_ID_MESA_LLVMPIPE:
848 return "LAVAPIPE"; 871 return "Lavapipe";
872 case VK_DRIVER_ID_MOLTENVK:
873 return "MoltenVK";
874 case VK_DRIVER_ID_VERISILICON_PROPRIETARY:
875 return "Vivante";
876 case VK_DRIVER_ID_MESA_TURNIP:
877 return "Turnip";
878 case VK_DRIVER_ID_MESA_V3DV:
879 return "V3DV";
880 case VK_DRIVER_ID_MESA_PANVK:
881 return "PanVK";
882 case VK_DRIVER_ID_MESA_VENUS:
883 return "Venus";
884 case VK_DRIVER_ID_MESA_DOZEN:
885 return "Dozen";
886 case VK_DRIVER_ID_MESA_NVK:
887 return "NVK";
888 case VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA:
889 return "PVR";
890 // case VK_DRIVER_ID_MESA_AGXV:
891 // return "Asahi";
849 default: 892 default:
850 return properties.driver.driverName; 893 return properties.driver.driverName;
851 } 894 }
@@ -863,7 +906,8 @@ bool Device::ShouldBoostClocks() const {
863 driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || 906 driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA ||
864 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; 907 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP;
865 908
866 const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; 909 const bool is_steam_deck = (vendor_id == 0x1002 && device_id == 0x163F) ||
910 (vendor_id == 0x1002 && device_id == 0x1435);
867 911
868 const bool is_debugging = this->HasDebuggingToolAttached(); 912 const bool is_debugging = this->HasDebuggingToolAttached();
869 913
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index b213ed7dd..4f3846345 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -102,6 +102,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
102 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \ 102 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \
103 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \ 103 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \
104 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ 104 EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
105 EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \
105 EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ 106 EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \
106 EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ 107 EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
107 EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ 108 EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
@@ -599,6 +600,11 @@ public:
599 return has_broken_cube_compatibility; 600 return has_broken_cube_compatibility;
600 } 601 }
601 602
603 /// Returns true if parallel shader compiling has issues with the current driver.
604 bool HasBrokenParallelShaderCompiling() const {
605 return has_broken_parallel_compiling;
606 }
607
602 /// Returns the vendor name reported from Vulkan. 608 /// Returns the vendor name reported from Vulkan.
603 std::string_view GetVendorName() const { 609 std::string_view GetVendorName() const {
604 return properties.driver.driverName; 610 return properties.driver.driverName;
@@ -663,6 +669,10 @@ public:
663 return supports_conditional_barriers; 669 return supports_conditional_barriers;
664 } 670 }
665 671
672 bool SupportsMultiViewport() const {
673 return features2.features.multiViewport;
674 }
675
666 [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id, 676 [[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id,
667 u32 driver_version) { 677 u32 driver_version) {
668 if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { 678 if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
@@ -794,6 +804,7 @@ private:
794 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. 804 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
795 bool has_broken_compute{}; ///< Compute shaders can cause crashes 805 bool has_broken_compute{}; ///< Compute shaders can cause crashes
796 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit 806 bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit
807 bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling.
797 bool has_renderdoc{}; ///< Has RenderDoc attached 808 bool has_renderdoc{}; ///< Has RenderDoc attached
798 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 809 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
799 bool supports_d24_depth{}; ///< Supports D24 depth buffers. 810 bool supports_d24_depth{}; ///< Supports D24 depth buffers.
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 0487cd3b6..a0c70797f 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -1101,6 +1101,10 @@ public:
1101 return &handle; 1101 return &handle;
1102 } 1102 }
1103 1103
1104 VkCommandBuffer operator*() const noexcept {
1105 return handle;
1106 }
1107
1104 void Begin(const VkCommandBufferBeginInfo& begin_info) const { 1108 void Begin(const VkCommandBufferBeginInfo& begin_info) const {
1105 Check(dld->vkBeginCommandBuffer(handle, &begin_info)); 1109 Check(dld->vkBeginCommandBuffer(handle, &begin_info));
1106 } 1110 }
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 33e1fb663..90278052a 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -38,8 +38,6 @@ add_executable(yuzu
38 compatdb.ui 38 compatdb.ui
39 compatibility_list.cpp 39 compatibility_list.cpp
40 compatibility_list.h 40 compatibility_list.h
41 configuration/config.cpp
42 configuration/config.h
43 configuration/configuration_shared.cpp 41 configuration/configuration_shared.cpp
44 configuration/configuration_shared.h 42 configuration/configuration_shared.h
45 configuration/configure.ui 43 configuration/configure.ui
@@ -147,6 +145,8 @@ add_executable(yuzu
147 configuration/shared_translation.h 145 configuration/shared_translation.h
148 configuration/shared_widget.cpp 146 configuration/shared_widget.cpp
149 configuration/shared_widget.h 147 configuration/shared_widget.h
148 configuration/qt_config.cpp
149 configuration/qt_config.h
150 debugger/console.cpp 150 debugger/console.cpp
151 debugger/console.h 151 debugger/console.h
152 debugger/controller.cpp 152 debugger/controller.cpp
@@ -252,6 +252,7 @@ file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
252if (ENABLE_QT_TRANSLATION) 252if (ENABLE_QT_TRANSLATION)
253 set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") 253 set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
254 option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) 254 option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
255 option(WORKAROUND_BROKEN_LUPDATE "Run lupdate directly through CMake if Qt's convenience wrappers don't work" OFF)
255 256
256 # Update source TS file if enabled 257 # Update source TS file if enabled
257 if (GENERATE_QT_TRANSLATION) 258 if (GENERATE_QT_TRANSLATION)
@@ -259,19 +260,51 @@ if (ENABLE_QT_TRANSLATION)
259 # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals 260 # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals
260 # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm 261 # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm
261 set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") 262 set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
262 qt_create_translation(QM_FILES 263 if (WORKAROUND_BROKEN_LUPDATE)
263 ${SRCS} 264 add_custom_command(OUTPUT ${YUZU_QT_LANGUAGES}/en.ts
264 ${UIS} 265 COMMAND lupdate
265 ${YUZU_QT_LANGUAGES}/en.ts 266 -source-language en_US
266 OPTIONS 267 -target-language en_US
267 -source-language en_US 268 ${SRCS}
268 -target-language en_US 269 ${UIS}
269 ) 270 -ts ${YUZU_QT_LANGUAGES}/en.ts
271 DEPENDS
272 ${SRCS}
273 ${UIS}
274 WORKING_DIRECTORY
275 ${CMAKE_CURRENT_SOURCE_DIR}
276 )
277 else()
278 qt_create_translation(QM_FILES
279 ${SRCS}
280 ${UIS}
281 ${YUZU_QT_LANGUAGES}/en.ts
282 OPTIONS
283 -source-language en_US
284 -target-language en_US
285 )
286 endif()
270 287
271 # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts 288 # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts
272 set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts) 289 set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts)
273 set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals") 290 set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals")
274 qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US) 291 if (WORKAROUND_BROKEN_LUPDATE)
292 add_custom_command(OUTPUT ${GENERATED_PLURALS_FILE}
293 COMMAND lupdate
294 -source-language en_US
295 -target-language en_US
296 ${SRCS}
297 ${UIS}
298 -ts ${GENERATED_PLURALS_FILE}
299 DEPENDS
300 ${SRCS}
301 ${UIS}
302 WORKING_DIRECTORY
303 ${CMAKE_CURRENT_SOURCE_DIR}
304 )
305 else()
306 qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US)
307 endif()
275 308
276 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE}) 309 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE})
277 endif() 310 endif()
@@ -344,7 +377,7 @@ endif()
344 377
345create_target_directory_groups(yuzu) 378create_target_directory_groups(yuzu)
346 379
347target_link_libraries(yuzu PRIVATE common core input_common network video_core) 380target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core)
348target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets) 381target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets)
349target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 382target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
350 383
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 2afa72140..ed5750155 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -30,7 +30,6 @@
30#include <QSize> 30#include <QSize>
31#include <QStringLiteral> 31#include <QStringLiteral>
32#include <QSurfaceFormat> 32#include <QSurfaceFormat>
33#include <QTimer>
34#include <QWindow> 33#include <QWindow>
35#include <QtCore/qobjectdefs.h> 34#include <QtCore/qobjectdefs.h>
36 35
@@ -66,6 +65,8 @@ class QObject;
66class QPaintEngine; 65class QPaintEngine;
67class QSurface; 66class QSurface;
68 67
68constexpr int default_mouse_constrain_timeout = 10;
69
69EmuThread::EmuThread(Core::System& system) : m_system{system} {} 70EmuThread::EmuThread(Core::System& system) : m_system{system} {}
70 71
71EmuThread::~EmuThread() = default; 72EmuThread::~EmuThread() = default;
@@ -304,6 +305,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
304 Qt::QueuedConnection); 305 Qt::QueuedConnection);
305 connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); 306 connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
306 connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged); 307 connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged);
308
309 mouse_constrain_timer.setInterval(default_mouse_constrain_timeout);
310 connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse);
307} 311}
308 312
309void GRenderWindow::ExecuteProgram(std::size_t program_index) { 313void GRenderWindow::ExecuteProgram(std::size_t program_index) {
@@ -393,6 +397,22 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
393 QWidget::closeEvent(event); 397 QWidget::closeEvent(event);
394} 398}
395 399
400void GRenderWindow::leaveEvent(QEvent* event) {
401 if (Settings::values.mouse_panning) {
402 const QRect& rect = QWidget::geometry();
403 QPoint position = QCursor::pos();
404
405 qint32 x = qBound(rect.left(), position.x(), rect.right());
406 qint32 y = qBound(rect.top(), position.y(), rect.bottom());
407 // Only start the timer if the mouse has left the window bound.
408 // The leave event is also triggered when the window looses focus.
409 if (x != position.x() || y != position.y()) {
410 mouse_constrain_timer.start();
411 }
412 event->accept();
413 }
414}
415
396int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { 416int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
397 static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = { 417 static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = {
398 std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A}, 418 std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A},
@@ -658,10 +678,19 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
658 input_subsystem->GetMouse()->TouchMove(touch_x, touch_y); 678 input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
659 input_subsystem->GetMouse()->Move(pos.x(), pos.y(), center_x, center_y); 679 input_subsystem->GetMouse()->Move(pos.x(), pos.y(), center_x, center_y);
660 680
681 // Center mouse for mouse panning
661 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { 682 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
662 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); 683 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
663 } 684 }
664 685
686 // Constrain mouse for mouse emulation with mouse panning
687 if (Settings::values.mouse_panning && Settings::values.mouse_enabled) {
688 const auto [clamped_mouse_x, clamped_mouse_y] = ClipToTouchScreen(x, y);
689 QCursor::setPos(mapToGlobal(
690 QPoint{static_cast<int>(clamped_mouse_x), static_cast<int>(clamped_mouse_y)}));
691 }
692
693 mouse_constrain_timer.stop();
665 emit MouseActivity(); 694 emit MouseActivity();
666} 695}
667 696
@@ -675,6 +704,31 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
675 input_subsystem->GetMouse()->ReleaseButton(button); 704 input_subsystem->GetMouse()->ReleaseButton(button);
676} 705}
677 706
707void GRenderWindow::ConstrainMouse() {
708 if (emu_thread == nullptr || !Settings::values.mouse_panning) {
709 mouse_constrain_timer.stop();
710 return;
711 }
712 if (!this->isActiveWindow()) {
713 mouse_constrain_timer.stop();
714 return;
715 }
716
717 if (Settings::values.mouse_enabled) {
718 const auto pos = mapFromGlobal(QCursor::pos());
719 const int new_pos_x = std::clamp(pos.x(), 0, width());
720 const int new_pos_y = std::clamp(pos.y(), 0, height());
721
722 QCursor::setPos(mapToGlobal(QPoint{new_pos_x, new_pos_y}));
723 return;
724 }
725
726 const int center_x = width() / 2;
727 const int center_y = height() / 2;
728
729 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
730}
731
678void GRenderWindow::wheelEvent(QWheelEvent* event) { 732void GRenderWindow::wheelEvent(QWheelEvent* event) {
679 const int x = event->angleDelta().x(); 733 const int x = event->angleDelta().x();
680 const int y = event->angleDelta().y(); 734 const int y = event->angleDelta().y();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 87b23df12..60edd464c 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -17,6 +17,7 @@
17#include <QString> 17#include <QString>
18#include <QStringList> 18#include <QStringList>
19#include <QThread> 19#include <QThread>
20#include <QTimer>
20#include <QWidget> 21#include <QWidget>
21#include <qglobal.h> 22#include <qglobal.h>
22#include <qnamespace.h> 23#include <qnamespace.h>
@@ -38,7 +39,6 @@ class QMouseEvent;
38class QObject; 39class QObject;
39class QResizeEvent; 40class QResizeEvent;
40class QShowEvent; 41class QShowEvent;
41class QTimer;
42class QTouchEvent; 42class QTouchEvent;
43class QWheelEvent; 43class QWheelEvent;
44 44
@@ -166,6 +166,7 @@ public:
166 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; 166 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
167 167
168 void closeEvent(QCloseEvent* event) override; 168 void closeEvent(QCloseEvent* event) override;
169 void leaveEvent(QEvent* event) override;
169 170
170 void resizeEvent(QResizeEvent* event) override; 171 void resizeEvent(QResizeEvent* event) override;
171 172
@@ -229,6 +230,7 @@ private:
229 void TouchBeginEvent(const QTouchEvent* event); 230 void TouchBeginEvent(const QTouchEvent* event);
230 void TouchUpdateEvent(const QTouchEvent* event); 231 void TouchUpdateEvent(const QTouchEvent* event);
231 void TouchEndEvent(); 232 void TouchEndEvent();
233 void ConstrainMouse();
232 234
233 void RequestCameraCapture(); 235 void RequestCameraCapture();
234 void OnCameraCapture(int requestId, const QImage& img); 236 void OnCameraCapture(int requestId, const QImage& img);
@@ -268,6 +270,8 @@ private:
268 std::unique_ptr<QTimer> camera_timer; 270 std::unique_ptr<QTimer> camera_timer;
269#endif 271#endif
270 272
273 QTimer mouse_constrain_timer;
274
271 Core::System& system; 275 Core::System& system;
272 276
273protected: 277protected:
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
deleted file mode 100644
index c0ae6468b..000000000
--- a/src/yuzu/configuration/config.cpp
+++ /dev/null
@@ -1,1309 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <array>
6#include <QKeySequence>
7#include <QSettings>
8#include "common/fs/fs.h"
9#include "common/fs/path_util.h"
10#include "common/settings.h"
11#include "common/settings_common.h"
12#include "common/settings_enums.h"
13#include "core/core.h"
14#include "core/hle/service/acc/profile_manager.h"
15#include "core/hle/service/hid/controllers/npad.h"
16#include "input_common/main.h"
17#include "network/network.h"
18#include "yuzu/configuration/config.h"
19
20namespace FS = Common::FS;
21
22Config::Config(const std::string& config_name, ConfigType config_type)
23 : type(config_type), global{config_type == ConfigType::GlobalConfig} {
24 Initialize(config_name);
25}
26
27Config::~Config() {
28 if (global) {
29 Save();
30 }
31}
32
33const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
34 Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F,
35 Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T,
36 Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right,
37 Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0,
38 Qt::Key_Q, Qt::Key_E,
39};
40
41const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
42 Qt::Key_7,
43 Qt::Key_8,
44};
45
46const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
47 {
48 Qt::Key_W,
49 Qt::Key_S,
50 Qt::Key_A,
51 Qt::Key_D,
52 },
53 {
54 Qt::Key_I,
55 Qt::Key_K,
56 Qt::Key_J,
57 Qt::Key_L,
58 },
59}};
60
61const std::array<int, 2> Config::default_stick_mod = {
62 Qt::Key_Shift,
63 0,
64};
65
66const std::array<int, 2> Config::default_ringcon_analogs{{
67 Qt::Key_A,
68 Qt::Key_D,
69}};
70
71const std::map<Settings::AntiAliasing, QString> Config::anti_aliasing_texts_map = {
72 {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
73 {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
74 {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
75};
76
77const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_map = {
78 {Settings::ScalingFilter::NearestNeighbor,
79 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
80 {Settings::ScalingFilter::Bilinear,
81 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
82 {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
83 {Settings::ScalingFilter::Gaussian,
84 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
85 {Settings::ScalingFilter::ScaleForce,
86 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
87 {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
88};
89
90const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = {
91 {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
92 {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
93};
94
95const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = {
96 {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
97 {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
98 {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
99};
100
101const std::map<Settings::RendererBackend, QString> Config::renderer_backend_texts_map = {
102 {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
103 {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
104 {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
105};
106
107const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = {
108 {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
109 {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
110 {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
111};
112
113// This shouldn't have anything except static initializers (no functions). So
114// QKeySequence(...).toString() is NOT ALLOWED HERE.
115// This must be in alphabetical order according to action name as it must have the same order as
116// UISetting::values.shortcuts, which is alphabetically ordered.
117// clang-format off
118const std::array<UISettings::Shortcut, 23> Config::default_hotkeys{{
119 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut, false}},
120 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
121 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
122 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
123 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}},
124 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}},
125 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}},
126 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}},
127 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}},
128 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}},
129 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
130 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
131 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
132 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}},
133 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}},
134 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
135 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
136 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
137 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}},
138 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}},
139 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
140 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut, false}},
141 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}},
142}};
143// clang-format on
144
145void Config::Initialize(const std::string& config_name) {
146 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
147 const auto config_file = fmt::format("{}.ini", config_name);
148
149 switch (type) {
150 case ConfigType::GlobalConfig:
151 qt_config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
152 void(FS::CreateParentDir(qt_config_loc));
153 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
154 QSettings::IniFormat);
155 Reload();
156 break;
157 case ConfigType::PerGameConfig:
158 qt_config_loc =
159 FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
160 void(FS::CreateParentDir(qt_config_loc));
161 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
162 QSettings::IniFormat);
163 Reload();
164 break;
165 case ConfigType::InputProfile:
166 qt_config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
167 void(FS::CreateParentDir(qt_config_loc));
168 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
169 QSettings::IniFormat);
170 break;
171 }
172}
173
174bool Config::IsCustomConfig() {
175 return type == ConfigType::PerGameConfig;
176}
177
178void Config::ReadPlayerValue(std::size_t player_index) {
179 const QString player_prefix = [this, player_index] {
180 if (type == ConfigType::InputProfile) {
181 return QString{};
182 } else {
183 return QStringLiteral("player_%1_").arg(player_index);
184 }
185 }();
186
187 auto& player = Settings::values.players.GetValue()[player_index];
188 if (IsCustomConfig()) {
189 const auto profile_name =
190 qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{})
191 .toString()
192 .toStdString();
193 if (profile_name.empty()) {
194 // Use the global input config
195 player = Settings::values.players.GetValue(true)[player_index];
196 return;
197 }
198 player.profile_name = profile_name;
199 }
200
201 if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) {
202 const auto controller = static_cast<Settings::ControllerType>(
203 qt_config
204 ->value(QStringLiteral("%1type").arg(player_prefix),
205 static_cast<u8>(Settings::ControllerType::ProController))
206 .toUInt());
207
208 if (controller == Settings::ControllerType::LeftJoycon ||
209 controller == Settings::ControllerType::RightJoycon) {
210 player.controller_type = controller;
211 }
212 } else {
213 player.connected =
214 ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0)
215 .toBool();
216
217 player.controller_type = static_cast<Settings::ControllerType>(
218 qt_config
219 ->value(QStringLiteral("%1type").arg(player_prefix),
220 static_cast<u8>(Settings::ControllerType::ProController))
221 .toUInt());
222
223 player.vibration_enabled =
224 qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true)
225 .toBool();
226
227 player.vibration_strength =
228 qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100)
229 .toInt();
230
231 player.body_color_left = qt_config
232 ->value(QStringLiteral("%1body_color_left").arg(player_prefix),
233 Settings::JOYCON_BODY_NEON_BLUE)
234 .toUInt();
235 player.body_color_right =
236 qt_config
237 ->value(QStringLiteral("%1body_color_right").arg(player_prefix),
238 Settings::JOYCON_BODY_NEON_RED)
239 .toUInt();
240 player.button_color_left =
241 qt_config
242 ->value(QStringLiteral("%1button_color_left").arg(player_prefix),
243 Settings::JOYCON_BUTTONS_NEON_BLUE)
244 .toUInt();
245 player.button_color_right =
246 qt_config
247 ->value(QStringLiteral("%1button_color_right").arg(player_prefix),
248 Settings::JOYCON_BUTTONS_NEON_RED)
249 .toUInt();
250 }
251
252 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
253 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
254 auto& player_buttons = player.buttons[i];
255
256 player_buttons = qt_config
257 ->value(QStringLiteral("%1").arg(player_prefix) +
258 QString::fromUtf8(Settings::NativeButton::mapping[i]),
259 QString::fromStdString(default_param))
260 .toString()
261 .toStdString();
262 if (player_buttons.empty()) {
263 player_buttons = default_param;
264 }
265 }
266
267 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
268 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
269 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
270 default_analogs[i][3], default_stick_mod[i], 0.5f);
271 auto& player_analogs = player.analogs[i];
272
273 player_analogs = qt_config
274 ->value(QStringLiteral("%1").arg(player_prefix) +
275 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
276 QString::fromStdString(default_param))
277 .toString()
278 .toStdString();
279 if (player_analogs.empty()) {
280 player_analogs = default_param;
281 }
282 }
283
284 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
285 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
286 auto& player_motions = player.motions[i];
287
288 player_motions = qt_config
289 ->value(QStringLiteral("%1").arg(player_prefix) +
290 QString::fromUtf8(Settings::NativeMotion::mapping[i]),
291 QString::fromStdString(default_param))
292 .toString()
293 .toStdString();
294 if (player_motions.empty()) {
295 player_motions = default_param;
296 }
297 }
298}
299
300void Config::ReadDebugValues() {
301 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
302 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
303 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
304
305 debug_pad_buttons = qt_config
306 ->value(QStringLiteral("debug_pad_") +
307 QString::fromUtf8(Settings::NativeButton::mapping[i]),
308 QString::fromStdString(default_param))
309 .toString()
310 .toStdString();
311 if (debug_pad_buttons.empty()) {
312 debug_pad_buttons = default_param;
313 }
314 }
315
316 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
317 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
318 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
319 default_analogs[i][3], default_stick_mod[i], 0.5f);
320 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
321
322 debug_pad_analogs = qt_config
323 ->value(QStringLiteral("debug_pad_") +
324 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
325 QString::fromStdString(default_param))
326 .toString()
327 .toStdString();
328 if (debug_pad_analogs.empty()) {
329 debug_pad_analogs = default_param;
330 }
331 }
332}
333
334void Config::ReadTouchscreenValues() {
335 Settings::values.touchscreen.enabled =
336 ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool();
337
338 Settings::values.touchscreen.rotation_angle =
339 ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt();
340 Settings::values.touchscreen.diameter_x =
341 ReadSetting(QStringLiteral("touchscreen_diameter_x"), 15).toUInt();
342 Settings::values.touchscreen.diameter_y =
343 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
344}
345
346void Config::ReadHidbusValues() {
347 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
348 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
349 auto& ringcon_analogs = Settings::values.ringcon_analogs;
350
351 ringcon_analogs =
352 qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param))
353 .toString()
354 .toStdString();
355 if (ringcon_analogs.empty()) {
356 ringcon_analogs = default_param;
357 }
358}
359
360void Config::ReadAudioValues() {
361 qt_config->beginGroup(QStringLiteral("Audio"));
362
363 ReadCategory(Settings::Category::Audio);
364 ReadCategory(Settings::Category::UiAudio);
365
366 qt_config->endGroup();
367}
368
369void Config::ReadControlValues() {
370 qt_config->beginGroup(QStringLiteral("Controls"));
371
372 ReadCategory(Settings::Category::Controls);
373
374 Settings::values.players.SetGlobal(!IsCustomConfig());
375 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
376 ReadPlayerValue(p);
377 }
378
379 // Disable docked mode if handheld is selected
380 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
381 if (controller_type == Settings::ControllerType::Handheld) {
382 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
383 Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
384 }
385
386 if (IsCustomConfig()) {
387 qt_config->endGroup();
388 return;
389 }
390 ReadDebugValues();
391 ReadTouchscreenValues();
392 ReadMotionTouchValues();
393 ReadHidbusValues();
394
395 qt_config->endGroup();
396}
397
398void Config::ReadMotionTouchValues() {
399 int num_touch_from_button_maps =
400 qt_config->beginReadArray(QStringLiteral("touch_from_button_maps"));
401
402 if (num_touch_from_button_maps > 0) {
403 const auto append_touch_from_button_map = [this] {
404 Settings::TouchFromButtonMap map;
405 map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default"))
406 .toString()
407 .toStdString();
408 const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries"));
409 map.buttons.reserve(num_touch_maps);
410 for (int i = 0; i < num_touch_maps; i++) {
411 qt_config->setArrayIndex(i);
412 std::string touch_mapping =
413 ReadSetting(QStringLiteral("bind")).toString().toStdString();
414 map.buttons.emplace_back(std::move(touch_mapping));
415 }
416 qt_config->endArray(); // entries
417 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
418 };
419
420 for (int i = 0; i < num_touch_from_button_maps; ++i) {
421 qt_config->setArrayIndex(i);
422 append_touch_from_button_map();
423 }
424 } else {
425 Settings::values.touch_from_button_maps.emplace_back(
426 Settings::TouchFromButtonMap{"default", {}});
427 num_touch_from_button_maps = 1;
428 }
429 qt_config->endArray();
430
431 Settings::values.touch_from_button_map_index = std::clamp(
432 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
433}
434
435void Config::ReadCoreValues() {
436 qt_config->beginGroup(QStringLiteral("Core"));
437
438 ReadCategory(Settings::Category::Core);
439
440 qt_config->endGroup();
441}
442
443void Config::ReadDataStorageValues() {
444 qt_config->beginGroup(QStringLiteral("Data Storage"));
445
446 FS::SetYuzuPath(
447 FS::YuzuPath::NANDDir,
448 qt_config
449 ->value(QStringLiteral("nand_directory"),
450 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)))
451 .toString()
452 .toStdString());
453 FS::SetYuzuPath(
454 FS::YuzuPath::SDMCDir,
455 qt_config
456 ->value(QStringLiteral("sdmc_directory"),
457 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)))
458 .toString()
459 .toStdString());
460 FS::SetYuzuPath(
461 FS::YuzuPath::LoadDir,
462 qt_config
463 ->value(QStringLiteral("load_directory"),
464 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)))
465 .toString()
466 .toStdString());
467 FS::SetYuzuPath(
468 FS::YuzuPath::DumpDir,
469 qt_config
470 ->value(QStringLiteral("dump_directory"),
471 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)))
472 .toString()
473 .toStdString());
474 FS::SetYuzuPath(FS::YuzuPath::TASDir,
475 qt_config
476 ->value(QStringLiteral("tas_directory"),
477 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)))
478 .toString()
479 .toStdString());
480
481 ReadCategory(Settings::Category::DataStorage);
482
483 qt_config->endGroup();
484}
485
486void Config::ReadDebuggingValues() {
487 qt_config->beginGroup(QStringLiteral("Debugging"));
488
489 // Intentionally not using the QT default setting as this is intended to be changed in the ini
490 Settings::values.record_frame_times =
491 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
492
493 ReadCategory(Settings::Category::Debugging);
494 ReadCategory(Settings::Category::DebuggingGraphics);
495
496 qt_config->endGroup();
497}
498
499void Config::ReadServiceValues() {
500 qt_config->beginGroup(QStringLiteral("Services"));
501
502 ReadCategory(Settings::Category::Services);
503
504 qt_config->endGroup();
505}
506
507void Config::ReadDisabledAddOnValues() {
508 const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns"));
509
510 for (int i = 0; i < size; ++i) {
511 qt_config->setArrayIndex(i);
512 const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong();
513 std::vector<std::string> out;
514 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
515 for (int j = 0; j < d_size; ++j) {
516 qt_config->setArrayIndex(j);
517 out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString());
518 }
519 qt_config->endArray();
520 Settings::values.disabled_addons.insert_or_assign(title_id, out);
521 }
522
523 qt_config->endArray();
524}
525
526void Config::ReadMiscellaneousValues() {
527 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
528
529 ReadCategory(Settings::Category::Miscellaneous);
530
531 qt_config->endGroup();
532}
533
534void Config::ReadPathValues() {
535 qt_config->beginGroup(QStringLiteral("Paths"));
536
537 UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString();
538 UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString();
539 UISettings::values.game_dir_deprecated =
540 ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString();
541 UISettings::values.game_dir_deprecated_deepscan =
542 ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool();
543 const int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs"));
544 for (int i = 0; i < gamedirs_size; ++i) {
545 qt_config->setArrayIndex(i);
546 UISettings::GameDir game_dir;
547 game_dir.path = ReadSetting(QStringLiteral("path")).toString();
548 game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool();
549 game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool();
550 UISettings::values.game_dirs.append(game_dir);
551 }
552 qt_config->endArray();
553 // create NAND and SD card directories if empty, these are not removable through the UI,
554 // also carries over old game list settings if present
555 if (UISettings::values.game_dirs.isEmpty()) {
556 UISettings::GameDir game_dir;
557 game_dir.path = QStringLiteral("SDMC");
558 game_dir.expanded = true;
559 UISettings::values.game_dirs.append(game_dir);
560 game_dir.path = QStringLiteral("UserNAND");
561 UISettings::values.game_dirs.append(game_dir);
562 game_dir.path = QStringLiteral("SysNAND");
563 UISettings::values.game_dirs.append(game_dir);
564 if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) {
565 game_dir.path = UISettings::values.game_dir_deprecated;
566 game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan;
567 UISettings::values.game_dirs.append(game_dir);
568 }
569 }
570 UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
571 UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString();
572
573 qt_config->endGroup();
574}
575
576void Config::ReadCpuValues() {
577 qt_config->beginGroup(QStringLiteral("Cpu"));
578
579 ReadCategory(Settings::Category::Cpu);
580 ReadCategory(Settings::Category::CpuDebug);
581 ReadCategory(Settings::Category::CpuUnsafe);
582
583 qt_config->endGroup();
584}
585
586void Config::ReadRendererValues() {
587 qt_config->beginGroup(QStringLiteral("Renderer"));
588
589 ReadCategory(Settings::Category::Renderer);
590 ReadCategory(Settings::Category::RendererAdvanced);
591 ReadCategory(Settings::Category::RendererDebug);
592
593 qt_config->endGroup();
594}
595
596void Config::ReadScreenshotValues() {
597 qt_config->beginGroup(QStringLiteral("Screenshots"));
598
599 ReadCategory(Settings::Category::Screenshots);
600 FS::SetYuzuPath(
601 FS::YuzuPath::ScreenshotsDir,
602 qt_config
603 ->value(QStringLiteral("screenshot_path"),
604 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)))
605 .toString()
606 .toStdString());
607
608 qt_config->endGroup();
609}
610
611void Config::ReadShortcutValues() {
612 qt_config->beginGroup(QStringLiteral("Shortcuts"));
613
614 for (const auto& [name, group, shortcut] : default_hotkeys) {
615 qt_config->beginGroup(group);
616 qt_config->beginGroup(name);
617 // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
618 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
619 // a file dialog in windowed mode
620 UISettings::values.shortcuts.push_back(
621 {name,
622 group,
623 {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
624 ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq)
625 .toString(),
626 shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}});
627 qt_config->endGroup();
628 qt_config->endGroup();
629 }
630
631 qt_config->endGroup();
632}
633
634void Config::ReadSystemValues() {
635 qt_config->beginGroup(QStringLiteral("System"));
636
637 ReadCategory(Settings::Category::System);
638 ReadCategory(Settings::Category::SystemAudio);
639
640 qt_config->endGroup();
641}
642
643void Config::ReadUIValues() {
644 qt_config->beginGroup(QStringLiteral("UI"));
645
646 UISettings::values.theme =
647 ReadSetting(
648 QStringLiteral("theme"),
649 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second))
650 .toString();
651
652 ReadUIGamelistValues();
653 ReadUILayoutValues();
654 ReadPathValues();
655 ReadScreenshotValues();
656 ReadShortcutValues();
657 ReadMultiplayerValues();
658
659 ReadCategory(Settings::Category::Ui);
660 ReadCategory(Settings::Category::UiGeneral);
661
662 qt_config->endGroup();
663}
664
665void Config::ReadUIGamelistValues() {
666 qt_config->beginGroup(QStringLiteral("UIGameList"));
667
668 ReadCategory(Settings::Category::UiGameList);
669
670 const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
671 for (int i = 0; i < favorites_size; i++) {
672 qt_config->setArrayIndex(i);
673 UISettings::values.favorited_ids.append(
674 ReadSetting(QStringLiteral("program_id")).toULongLong());
675 }
676 qt_config->endArray();
677
678 qt_config->endGroup();
679}
680
681void Config::ReadUILayoutValues() {
682 qt_config->beginGroup(QStringLiteral("UILayout"));
683
684 UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray();
685 UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray();
686 UISettings::values.renderwindow_geometry =
687 ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray();
688 UISettings::values.gamelist_header_state =
689 ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray();
690 UISettings::values.microprofile_geometry =
691 ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray();
692
693 ReadCategory(Settings::Category::UiLayout);
694
695 qt_config->endGroup();
696}
697
698void Config::ReadWebServiceValues() {
699 qt_config->beginGroup(QStringLiteral("WebService"));
700
701 ReadCategory(Settings::Category::WebService);
702
703 qt_config->endGroup();
704}
705
706void Config::ReadMultiplayerValues() {
707 qt_config->beginGroup(QStringLiteral("Multiplayer"));
708
709 ReadCategory(Settings::Category::Multiplayer);
710
711 // Read ban list back
712 int size = qt_config->beginReadArray(QStringLiteral("username_ban_list"));
713 UISettings::values.multiplayer_ban_list.first.resize(size);
714 for (int i = 0; i < size; ++i) {
715 qt_config->setArrayIndex(i);
716 UISettings::values.multiplayer_ban_list.first[i] =
717 ReadSetting(QStringLiteral("username")).toString().toStdString();
718 }
719 qt_config->endArray();
720 size = qt_config->beginReadArray(QStringLiteral("ip_ban_list"));
721 UISettings::values.multiplayer_ban_list.second.resize(size);
722 for (int i = 0; i < size; ++i) {
723 qt_config->setArrayIndex(i);
724 UISettings::values.multiplayer_ban_list.second[i] =
725 ReadSetting(QStringLiteral("ip")).toString().toStdString();
726 }
727 qt_config->endArray();
728
729 qt_config->endGroup();
730}
731
732void Config::ReadNetworkValues() {
733 qt_config->beginGroup(QString::fromStdString("Services"));
734
735 ReadCategory(Settings::Category::Network);
736
737 qt_config->endGroup();
738}
739
740void Config::ReadValues() {
741 if (global) {
742 ReadDataStorageValues();
743 ReadDebuggingValues();
744 ReadDisabledAddOnValues();
745 ReadNetworkValues();
746 ReadServiceValues();
747 ReadUIValues();
748 ReadWebServiceValues();
749 ReadMiscellaneousValues();
750 }
751 ReadControlValues();
752 ReadCoreValues();
753 ReadCpuValues();
754 ReadRendererValues();
755 ReadAudioValues();
756 ReadSystemValues();
757}
758
759void Config::SavePlayerValue(std::size_t player_index) {
760 const QString player_prefix = [this, player_index] {
761 if (type == ConfigType::InputProfile) {
762 return QString{};
763 } else {
764 return QStringLiteral("player_%1_").arg(player_index);
765 }
766 }();
767
768 const auto& player = Settings::values.players.GetValue()[player_index];
769 if (IsCustomConfig()) {
770 if (player.profile_name.empty()) {
771 // No custom profile selected
772 return;
773 }
774 WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix),
775 QString::fromStdString(player.profile_name), QString{});
776 }
777
778 WriteSetting(QStringLiteral("%1type").arg(player_prefix),
779 static_cast<u8>(player.controller_type),
780 static_cast<u8>(Settings::ControllerType::ProController));
781
782 if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) {
783 WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected,
784 player_index == 0);
785 WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix),
786 player.vibration_enabled, true);
787 WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix),
788 player.vibration_strength, 100);
789 WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left,
790 Settings::JOYCON_BODY_NEON_BLUE);
791 WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix),
792 player.body_color_right, Settings::JOYCON_BODY_NEON_RED);
793 WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix),
794 player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE);
795 WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix),
796 player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED);
797 }
798
799 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
800 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
801 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
802 QString::fromStdString(Settings::NativeButton::mapping[i]),
803 QString::fromStdString(player.buttons[i]),
804 QString::fromStdString(default_param));
805 }
806 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
807 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
808 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
809 default_analogs[i][3], default_stick_mod[i], 0.5f);
810 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
811 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
812 QString::fromStdString(player.analogs[i]),
813 QString::fromStdString(default_param));
814 }
815 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
816 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
817 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
818 QString::fromStdString(Settings::NativeMotion::mapping[i]),
819 QString::fromStdString(player.motions[i]),
820 QString::fromStdString(default_param));
821 }
822}
823
824void Config::SaveDebugValues() {
825 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
826 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
827 WriteSetting(QStringLiteral("debug_pad_") +
828 QString::fromStdString(Settings::NativeButton::mapping[i]),
829 QString::fromStdString(Settings::values.debug_pad_buttons[i]),
830 QString::fromStdString(default_param));
831 }
832 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
833 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
834 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
835 default_analogs[i][3], default_stick_mod[i], 0.5f);
836 WriteSetting(QStringLiteral("debug_pad_") +
837 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
838 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
839 QString::fromStdString(default_param));
840 }
841}
842
843void Config::SaveTouchscreenValues() {
844 const auto& touchscreen = Settings::values.touchscreen;
845
846 WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true);
847
848 WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0);
849 WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15);
850 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
851}
852
853void Config::SaveMotionTouchValues() {
854 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
855 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
856 qt_config->setArrayIndex(static_cast<int>(p));
857 WriteSetting(QStringLiteral("name"),
858 QString::fromStdString(Settings::values.touch_from_button_maps[p].name),
859 QStringLiteral("default"));
860 qt_config->beginWriteArray(QStringLiteral("entries"));
861 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
862 ++q) {
863 qt_config->setArrayIndex(static_cast<int>(q));
864 WriteSetting(
865 QStringLiteral("bind"),
866 QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q]));
867 }
868 qt_config->endArray();
869 }
870 qt_config->endArray();
871}
872
873void Config::SaveHidbusValues() {
874 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
875 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
876 WriteSetting(QStringLiteral("ring_controller"),
877 QString::fromStdString(Settings::values.ringcon_analogs),
878 QString::fromStdString(default_param));
879}
880
881void Config::SaveValues() {
882 if (global) {
883 SaveDataStorageValues();
884 SaveDebuggingValues();
885 SaveDisabledAddOnValues();
886 SaveNetworkValues();
887 SaveUIValues();
888 SaveWebServiceValues();
889 SaveMiscellaneousValues();
890 }
891 SaveControlValues();
892 SaveCoreValues();
893 SaveCpuValues();
894 SaveRendererValues();
895 SaveAudioValues();
896 SaveSystemValues();
897
898 qt_config->sync();
899}
900
901void Config::SaveAudioValues() {
902 qt_config->beginGroup(QStringLiteral("Audio"));
903
904 WriteCategory(Settings::Category::Audio);
905 WriteCategory(Settings::Category::UiAudio);
906
907 qt_config->endGroup();
908}
909
910void Config::SaveControlValues() {
911 qt_config->beginGroup(QStringLiteral("Controls"));
912
913 WriteCategory(Settings::Category::Controls);
914
915 Settings::values.players.SetGlobal(!IsCustomConfig());
916 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
917 SavePlayerValue(p);
918 }
919 if (IsCustomConfig()) {
920 qt_config->endGroup();
921 return;
922 }
923 SaveDebugValues();
924 SaveTouchscreenValues();
925 SaveMotionTouchValues();
926 SaveHidbusValues();
927
928 qt_config->endGroup();
929}
930
931void Config::SaveCoreValues() {
932 qt_config->beginGroup(QStringLiteral("Core"));
933
934 WriteCategory(Settings::Category::Core);
935
936 qt_config->endGroup();
937}
938
939void Config::SaveDataStorageValues() {
940 qt_config->beginGroup(QStringLiteral("Data Storage"));
941
942 WriteSetting(QStringLiteral("nand_directory"),
943 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)),
944 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
945 WriteSetting(QStringLiteral("sdmc_directory"),
946 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)),
947 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
948 WriteSetting(QStringLiteral("load_directory"),
949 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)),
950 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
951 WriteSetting(QStringLiteral("dump_directory"),
952 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)),
953 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
954 WriteSetting(QStringLiteral("tas_directory"),
955 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)),
956 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
957
958 WriteCategory(Settings::Category::DataStorage);
959
960 qt_config->endGroup();
961}
962
963void Config::SaveDebuggingValues() {
964 qt_config->beginGroup(QStringLiteral("Debugging"));
965
966 // Intentionally not using the QT default setting as this is intended to be changed in the ini
967 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
968
969 WriteCategory(Settings::Category::Debugging);
970 WriteCategory(Settings::Category::DebuggingGraphics);
971
972 qt_config->endGroup();
973}
974
975void Config::SaveNetworkValues() {
976 qt_config->beginGroup(QStringLiteral("Services"));
977
978 WriteCategory(Settings::Category::Network);
979
980 qt_config->endGroup();
981}
982
983void Config::SaveDisabledAddOnValues() {
984 qt_config->beginWriteArray(QStringLiteral("DisabledAddOns"));
985
986 int i = 0;
987 for (const auto& elem : Settings::values.disabled_addons) {
988 qt_config->setArrayIndex(i);
989 WriteSetting(QStringLiteral("title_id"), QVariant::fromValue<u64>(elem.first), 0);
990 qt_config->beginWriteArray(QStringLiteral("disabled"));
991 for (std::size_t j = 0; j < elem.second.size(); ++j) {
992 qt_config->setArrayIndex(static_cast<int>(j));
993 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{});
994 }
995 qt_config->endArray();
996 ++i;
997 }
998
999 qt_config->endArray();
1000}
1001
1002void Config::SaveMiscellaneousValues() {
1003 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
1004
1005 WriteCategory(Settings::Category::Miscellaneous);
1006
1007 qt_config->endGroup();
1008}
1009
1010void Config::SavePathValues() {
1011 qt_config->beginGroup(QStringLiteral("Paths"));
1012
1013 WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path);
1014 WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path);
1015 qt_config->beginWriteArray(QStringLiteral("gamedirs"));
1016 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
1017 qt_config->setArrayIndex(i);
1018 const auto& game_dir = UISettings::values.game_dirs[i];
1019 WriteSetting(QStringLiteral("path"), game_dir.path);
1020 WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false);
1021 WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true);
1022 }
1023 qt_config->endArray();
1024 WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
1025 WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{});
1026
1027 qt_config->endGroup();
1028}
1029
1030void Config::SaveCpuValues() {
1031 qt_config->beginGroup(QStringLiteral("Cpu"));
1032
1033 WriteCategory(Settings::Category::Cpu);
1034 WriteCategory(Settings::Category::CpuDebug);
1035 WriteCategory(Settings::Category::CpuUnsafe);
1036
1037 qt_config->endGroup();
1038}
1039
1040void Config::SaveRendererValues() {
1041 qt_config->beginGroup(QStringLiteral("Renderer"));
1042
1043 WriteCategory(Settings::Category::Renderer);
1044 WriteCategory(Settings::Category::RendererAdvanced);
1045 WriteCategory(Settings::Category::RendererDebug);
1046
1047 qt_config->endGroup();
1048}
1049
1050void Config::SaveScreenshotValues() {
1051 qt_config->beginGroup(QStringLiteral("Screenshots"));
1052
1053 WriteSetting(QStringLiteral("screenshot_path"),
1054 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)));
1055 WriteCategory(Settings::Category::Screenshots);
1056
1057 qt_config->endGroup();
1058}
1059
1060void Config::SaveShortcutValues() {
1061 qt_config->beginGroup(QStringLiteral("Shortcuts"));
1062
1063 // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
1064 // However, their ordering must also be the same.
1065 for (std::size_t i = 0; i < default_hotkeys.size(); i++) {
1066 const auto& [name, group, shortcut] = UISettings::values.shortcuts[i];
1067 const auto& default_hotkey = default_hotkeys[i].shortcut;
1068
1069 qt_config->beginGroup(group);
1070 qt_config->beginGroup(name);
1071 WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq);
1072 WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq,
1073 default_hotkey.controller_keyseq);
1074 WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
1075 WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat);
1076 qt_config->endGroup();
1077 qt_config->endGroup();
1078 }
1079
1080 qt_config->endGroup();
1081}
1082
1083void Config::SaveSystemValues() {
1084 qt_config->beginGroup(QStringLiteral("System"));
1085
1086 WriteCategory(Settings::Category::System);
1087 WriteCategory(Settings::Category::SystemAudio);
1088
1089 qt_config->endGroup();
1090}
1091
1092void Config::SaveUIValues() {
1093 qt_config->beginGroup(QStringLiteral("UI"));
1094
1095 WriteCategory(Settings::Category::Ui);
1096 WriteCategory(Settings::Category::UiGeneral);
1097
1098 WriteSetting(QStringLiteral("theme"), UISettings::values.theme,
1099 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second));
1100
1101 SaveUIGamelistValues();
1102 SaveUILayoutValues();
1103 SavePathValues();
1104 SaveScreenshotValues();
1105 SaveShortcutValues();
1106 SaveMultiplayerValues();
1107
1108 qt_config->endGroup();
1109}
1110
1111void Config::SaveUIGamelistValues() {
1112 qt_config->beginGroup(QStringLiteral("UIGameList"));
1113
1114 WriteCategory(Settings::Category::UiGameList);
1115
1116 qt_config->beginWriteArray(QStringLiteral("favorites"));
1117 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
1118 qt_config->setArrayIndex(i);
1119 WriteSetting(QStringLiteral("program_id"),
1120 QVariant::fromValue(UISettings::values.favorited_ids[i]));
1121 }
1122 qt_config->endArray();
1123
1124 qt_config->endGroup();
1125}
1126
1127void Config::SaveUILayoutValues() {
1128 qt_config->beginGroup(QStringLiteral("UILayout"));
1129
1130 WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry);
1131 WriteSetting(QStringLiteral("state"), UISettings::values.state);
1132 WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry);
1133 WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state);
1134 WriteSetting(QStringLiteral("microProfileDialogGeometry"),
1135 UISettings::values.microprofile_geometry);
1136
1137 WriteCategory(Settings::Category::UiLayout);
1138
1139 qt_config->endGroup();
1140}
1141
1142void Config::SaveWebServiceValues() {
1143 qt_config->beginGroup(QStringLiteral("WebService"));
1144
1145 WriteCategory(Settings::Category::WebService);
1146
1147 qt_config->endGroup();
1148}
1149
1150void Config::SaveMultiplayerValues() {
1151 qt_config->beginGroup(QStringLiteral("Multiplayer"));
1152
1153 WriteCategory(Settings::Category::Multiplayer);
1154
1155 // Write ban list
1156 qt_config->beginWriteArray(QStringLiteral("username_ban_list"));
1157 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
1158 qt_config->setArrayIndex(static_cast<int>(i));
1159 WriteSetting(QStringLiteral("username"),
1160 QString::fromStdString(UISettings::values.multiplayer_ban_list.first[i]));
1161 }
1162 qt_config->endArray();
1163 qt_config->beginWriteArray(QStringLiteral("ip_ban_list"));
1164 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
1165 qt_config->setArrayIndex(static_cast<int>(i));
1166 WriteSetting(QStringLiteral("ip"),
1167 QString::fromStdString(UISettings::values.multiplayer_ban_list.second[i]));
1168 }
1169 qt_config->endArray();
1170
1171 qt_config->endGroup();
1172}
1173
1174QVariant Config::ReadSetting(const QString& name) const {
1175 return qt_config->value(name);
1176}
1177
1178QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const {
1179 QVariant result;
1180 if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
1181 result = default_value;
1182 } else {
1183 result = qt_config->value(name, default_value);
1184 }
1185 return result;
1186}
1187
1188void Config::WriteSetting(const QString& name, const QVariant& value) {
1189 qt_config->setValue(name, value);
1190}
1191
1192void Config::WriteSetting(const QString& name, const QVariant& value,
1193 const QVariant& default_value) {
1194 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
1195 qt_config->setValue(name, value);
1196}
1197
1198void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
1199 bool use_global) {
1200 if (!global) {
1201 qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
1202 }
1203 if (global || !use_global) {
1204 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
1205 qt_config->setValue(name, value);
1206 }
1207}
1208
1209void Config::Reload() {
1210 ReadValues();
1211 // To apply default value changes
1212 SaveValues();
1213}
1214
1215void Config::Save() {
1216 SaveValues();
1217}
1218
1219void Config::ReadControlPlayerValue(std::size_t player_index) {
1220 qt_config->beginGroup(QStringLiteral("Controls"));
1221 ReadPlayerValue(player_index);
1222 qt_config->endGroup();
1223}
1224
1225void Config::SaveControlPlayerValue(std::size_t player_index) {
1226 qt_config->beginGroup(QStringLiteral("Controls"));
1227 SavePlayerValue(player_index);
1228 qt_config->endGroup();
1229}
1230
1231void Config::ClearControlPlayerValues() {
1232 qt_config->beginGroup(QStringLiteral("Controls"));
1233 // If key is an empty string, all keys in the current group() are removed.
1234 qt_config->remove(QString{});
1235 qt_config->endGroup();
1236}
1237
1238const std::string& Config::GetConfigFilePath() const {
1239 return qt_config_loc;
1240}
1241
1242static auto FindRelevantList(Settings::Category category) {
1243 auto& map = Settings::values.linkage.by_category;
1244 if (map.contains(category)) {
1245 return Settings::values.linkage.by_category[category];
1246 }
1247 return UISettings::values.linkage.by_category[category];
1248}
1249
1250void Config::ReadCategory(Settings::Category category) {
1251 const auto& settings = FindRelevantList(category);
1252 std::for_each(settings.begin(), settings.end(),
1253 [&](const auto& setting) { ReadSettingGeneric(setting); });
1254}
1255
1256void Config::WriteCategory(Settings::Category category) {
1257 const auto& settings = FindRelevantList(category);
1258 std::for_each(settings.begin(), settings.end(),
1259 [&](const auto& setting) { WriteSettingGeneric(setting); });
1260}
1261
1262void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) {
1263 if (!setting->Save() || (!setting->Switchable() && !global)) {
1264 return;
1265 }
1266 const QString name = QString::fromStdString(setting->GetLabel());
1267 const auto default_value =
1268 QVariant::fromValue<QString>(QString::fromStdString(setting->DefaultToString()));
1269
1270 bool use_global = true;
1271 if (setting->Switchable() && !global) {
1272 use_global = qt_config->value(name + QStringLiteral("/use_global"), true).value<bool>();
1273 setting->SetGlobal(use_global);
1274 }
1275
1276 if (global || !use_global) {
1277 const bool is_default =
1278 qt_config->value(name + QStringLiteral("/default"), true).value<bool>();
1279 if (!is_default) {
1280 setting->LoadString(
1281 qt_config->value(name, default_value).value<QString>().toStdString());
1282 } else {
1283 // Empty string resets the Setting to default
1284 setting->LoadString("");
1285 }
1286 }
1287}
1288
1289void Config::WriteSettingGeneric(Settings::BasicSetting* const setting) const {
1290 if (!setting->Save()) {
1291 return;
1292 }
1293 const QVariant value = QVariant::fromValue(QString::fromStdString(setting->ToString()));
1294 const QVariant default_value =
1295 QVariant::fromValue(QString::fromStdString(setting->DefaultToString()));
1296 const QString label = QString::fromStdString(setting->GetLabel());
1297 if (setting->Switchable()) {
1298 if (!global) {
1299 qt_config->setValue(label + QStringLiteral("/use_global"), setting->UsingGlobal());
1300 }
1301 if (global || !setting->UsingGlobal()) {
1302 qt_config->setValue(label + QStringLiteral("/default"), value == default_value);
1303 qt_config->setValue(label, value);
1304 }
1305 } else if (global) {
1306 qt_config->setValue(label + QStringLiteral("/default"), value == default_value);
1307 qt_config->setValue(label, value);
1308 }
1309}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
deleted file mode 100644
index 1589ba057..000000000
--- a/src/yuzu/configuration/config.h
+++ /dev/null
@@ -1,179 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <string>
9#include <QMetaType>
10#include <QVariant>
11#include "common/settings.h"
12#include "common/settings_enums.h"
13#include "yuzu/uisettings.h"
14
15class QSettings;
16
17namespace Core {
18class System;
19}
20
21class Config {
22public:
23 enum class ConfigType {
24 GlobalConfig,
25 PerGameConfig,
26 InputProfile,
27 };
28
29 explicit Config(const std::string& config_name = "qt-config",
30 ConfigType config_type = ConfigType::GlobalConfig);
31 ~Config();
32
33 void Reload();
34 void Save();
35
36 void ReadControlPlayerValue(std::size_t player_index);
37 void SaveControlPlayerValue(std::size_t player_index);
38 void ClearControlPlayerValues();
39
40 const std::string& GetConfigFilePath() const;
41
42 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
43 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
44 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
45 static const std::array<int, 2> default_stick_mod;
46 static const std::array<int, 2> default_ringcon_analogs;
47 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
48 default_mouse_buttons;
49 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
50 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
51 static const std::array<UISettings::Shortcut, 23> default_hotkeys;
52
53 static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map;
54 static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map;
55 static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map;
56 static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map;
57 static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
58 static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
59
60 static constexpr UISettings::Theme default_theme{
61#ifdef _WIN32
62 UISettings::Theme::DarkColorful
63#else
64 UISettings::Theme::DefaultColorful
65#endif
66 };
67
68private:
69 void Initialize(const std::string& config_name);
70 bool IsCustomConfig();
71
72 void ReadValues();
73 void ReadPlayerValue(std::size_t player_index);
74 void ReadDebugValues();
75 void ReadKeyboardValues();
76 void ReadMouseValues();
77 void ReadTouchscreenValues();
78 void ReadMotionTouchValues();
79 void ReadHidbusValues();
80 void ReadIrCameraValues();
81
82 // Read functions bases off the respective config section names.
83 void ReadAudioValues();
84 void ReadControlValues();
85 void ReadCoreValues();
86 void ReadDataStorageValues();
87 void ReadDebuggingValues();
88 void ReadServiceValues();
89 void ReadDisabledAddOnValues();
90 void ReadMiscellaneousValues();
91 void ReadPathValues();
92 void ReadCpuValues();
93 void ReadRendererValues();
94 void ReadScreenshotValues();
95 void ReadShortcutValues();
96 void ReadSystemValues();
97 void ReadUIValues();
98 void ReadUIGamelistValues();
99 void ReadUILayoutValues();
100 void ReadWebServiceValues();
101 void ReadMultiplayerValues();
102 void ReadNetworkValues();
103
104 void SaveValues();
105 void SavePlayerValue(std::size_t player_index);
106 void SaveDebugValues();
107 void SaveMouseValues();
108 void SaveTouchscreenValues();
109 void SaveMotionTouchValues();
110 void SaveHidbusValues();
111 void SaveIrCameraValues();
112
113 // Save functions based off the respective config section names.
114 void SaveAudioValues();
115 void SaveControlValues();
116 void SaveCoreValues();
117 void SaveDataStorageValues();
118 void SaveDebuggingValues();
119 void SaveNetworkValues();
120 void SaveDisabledAddOnValues();
121 void SaveMiscellaneousValues();
122 void SavePathValues();
123 void SaveCpuValues();
124 void SaveRendererValues();
125 void SaveScreenshotValues();
126 void SaveShortcutValues();
127 void SaveSystemValues();
128 void SaveUIValues();
129 void SaveUIGamelistValues();
130 void SaveUILayoutValues();
131 void SaveWebServiceValues();
132 void SaveMultiplayerValues();
133
134 /**
135 * Reads a setting from the qt_config.
136 *
137 * @param name The setting's identifier
138 * @param default_value The value to use when the setting is not already present in the config
139 */
140 QVariant ReadSetting(const QString& name) const;
141 QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
142
143 /**
144 * Writes a setting to the qt_config.
145 *
146 * @param name The setting's idetentifier
147 * @param value Value of the setting
148 * @param default_value Default of the setting if not present in qt_config
149 * @param use_global Specifies if the custom or global config should be in use, for custom
150 * configs
151 */
152 void WriteSetting(const QString& name, const QVariant& value);
153 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
154 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
155 bool use_global);
156
157 void ReadCategory(Settings::Category category);
158 void WriteCategory(Settings::Category category);
159 void ReadSettingGeneric(Settings::BasicSetting* const setting);
160 void WriteSettingGeneric(Settings::BasicSetting* const setting) const;
161
162 const ConfigType type;
163 std::unique_ptr<QSettings> qt_config;
164 std::string qt_config_loc;
165 const bool global;
166};
167
168// These metatype declarations cannot be in common/settings.h because core is devoid of QT
169Q_DECLARE_METATYPE(Settings::CpuAccuracy);
170Q_DECLARE_METATYPE(Settings::GpuAccuracy);
171Q_DECLARE_METATYPE(Settings::FullscreenMode);
172Q_DECLARE_METATYPE(Settings::NvdecEmulation);
173Q_DECLARE_METATYPE(Settings::ResolutionSetup);
174Q_DECLARE_METATYPE(Settings::ScalingFilter);
175Q_DECLARE_METATYPE(Settings::AntiAliasing);
176Q_DECLARE_METATYPE(Settings::RendererBackend);
177Q_DECLARE_METATYPE(Settings::ShaderBackend);
178Q_DECLARE_METATYPE(Settings::AstcRecompression);
179Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
diff --git a/src/yuzu/configuration/configure_camera.cpp b/src/yuzu/configuration/configure_camera.cpp
index d95e96696..3368f53f3 100644
--- a/src/yuzu/configuration/configure_camera.cpp
+++ b/src/yuzu/configuration/configure_camera.cpp
@@ -10,10 +10,10 @@
10#include <QStandardItemModel> 10#include <QStandardItemModel>
11#include <QTimer> 11#include <QTimer>
12 12
13#include "common/settings.h"
13#include "input_common/drivers/camera.h" 14#include "input_common/drivers/camera.h"
14#include "input_common/main.h" 15#include "input_common/main.h"
15#include "ui_configure_camera.h" 16#include "ui_configure_camera.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_camera.h" 17#include "yuzu/configuration/configure_camera.h"
18 18
19ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) 19ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_)
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index a51359903..7e16cf17d 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -27,6 +27,13 @@ ConfigureCpu::ConfigureCpu(const Core::System& system_,
27 27
28 connect(accuracy_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this, 28 connect(accuracy_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
29 &ConfigureCpu::UpdateGroup); 29 &ConfigureCpu::UpdateGroup);
30
31 connect(backend_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
32 &ConfigureCpu::UpdateGroup);
33
34#ifdef HAS_NCE
35 ui->backend_group->setVisible(true);
36#endif
30} 37}
31 38
32ConfigureCpu::~ConfigureCpu() = default; 39ConfigureCpu::~ConfigureCpu() = default;
@@ -34,6 +41,7 @@ ConfigureCpu::~ConfigureCpu() = default;
34void ConfigureCpu::SetConfiguration() {} 41void ConfigureCpu::SetConfiguration() {}
35void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) { 42void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
36 auto* accuracy_layout = ui->widget_accuracy->layout(); 43 auto* accuracy_layout = ui->widget_accuracy->layout();
44 auto* backend_layout = ui->widget_backend->layout();
37 auto* unsafe_layout = ui->unsafe_widget->layout(); 45 auto* unsafe_layout = ui->unsafe_widget->layout();
38 std::map<u32, QWidget*> unsafe_hold{}; 46 std::map<u32, QWidget*> unsafe_hold{};
39 47
@@ -62,6 +70,9 @@ void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
62 // Keep track of cpu_accuracy combobox to display/hide the unsafe settings 70 // Keep track of cpu_accuracy combobox to display/hide the unsafe settings
63 accuracy_layout->addWidget(widget); 71 accuracy_layout->addWidget(widget);
64 accuracy_combobox = widget->combobox; 72 accuracy_combobox = widget->combobox;
73 } else if (setting->Id() == Settings::values.cpu_backend.Id()) {
74 backend_layout->addWidget(widget);
75 backend_combobox = widget->combobox;
65 } else { 76 } else {
66 // Presently, all other settings here are unsafe checkboxes 77 // Presently, all other settings here are unsafe checkboxes
67 unsafe_hold.insert({setting->Id(), widget}); 78 unsafe_hold.insert({setting->Id(), widget});
@@ -73,6 +84,7 @@ void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
73 } 84 }
74 85
75 UpdateGroup(accuracy_combobox->currentIndex()); 86 UpdateGroup(accuracy_combobox->currentIndex());
87 UpdateGroup(backend_combobox->currentIndex());
76} 88}
77 89
78void ConfigureCpu::UpdateGroup(int index) { 90void ConfigureCpu::UpdateGroup(int index) {
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index 61a6de7aa..a102b4c1f 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -49,4 +49,5 @@ private:
49 std::vector<std::function<void(bool)>> apply_funcs{}; 49 std::vector<std::function<void(bool)>> apply_funcs{};
50 50
51 QComboBox* accuracy_combobox; 51 QComboBox* accuracy_combobox;
52 QComboBox* backend_combobox;
52}; 53};
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index f734e842e..13fd43605 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -60,6 +60,36 @@
60 </widget> 60 </widget>
61 </item> 61 </item>
62 <item> 62 <item>
63 <widget class="QGroupBox" name="backend_group">
64 <property name="title">
65 <string>CPU Backend</string>
66 </property>
67 <layout class="QVBoxLayout">
68 <item>
69 <widget class="QWidget" name="widget_backend" native="true">
70 <layout class="QVBoxLayout" name="verticalLayout1">
71 <property name="leftMargin">
72 <number>0</number>
73 </property>
74 <property name="topMargin">
75 <number>0</number>
76 </property>
77 <property name="rightMargin">
78 <number>0</number>
79 </property>
80 <property name="bottomMargin">
81 <number>0</number>
82 </property>
83 </layout>
84 </widget>
85 </item>
86 </layout>
87 <property name="visible">
88 <bool>false</bool>
89 </property>
90 </widget>
91 </item>
92 <item>
63 <widget class="QGroupBox" name="unsafe_group"> 93 <widget class="QGroupBox" name="unsafe_group">
64 <property name="title"> 94 <property name="title">
65 <string>Unsafe CPU Optimization Settings</string> 95 <string>Unsafe CPU Optimization Settings</string>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index ef421c754..1010038b7 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
51 ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); 51 ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
52 ui->enable_renderdoc_hotkey->setEnabled(runtime_lock); 52 ui->enable_renderdoc_hotkey->setEnabled(runtime_lock);
53 ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue()); 53 ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue());
54 ui->disable_buffer_reorder->setEnabled(runtime_lock);
55 ui->disable_buffer_reorder->setChecked(Settings::values.disable_buffer_reorder.GetValue());
54 ui->enable_graphics_debugging->setEnabled(runtime_lock); 56 ui->enable_graphics_debugging->setEnabled(runtime_lock);
55 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); 57 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
56 ui->enable_shader_feedback->setEnabled(runtime_lock); 58 ui->enable_shader_feedback->setEnabled(runtime_lock);
@@ -96,6 +98,7 @@ void ConfigureDebug::ApplyConfiguration() {
96 Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked(); 98 Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked();
97 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 99 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
98 Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked(); 100 Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked();
101 Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked();
99 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); 102 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
100 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); 103 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
101 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); 104 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 76fe98924..22b51f39c 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -271,19 +271,6 @@
271 </widget> 271 </widget>
272 </item> 272 </item>
273 <item row="8" column="0"> 273 <item row="8" column="0">
274 <widget class="QCheckBox" name="disable_macro_hle">
275 <property name="enabled">
276 <bool>true</bool>
277 </property>
278 <property name="toolTip">
279 <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
280 </property>
281 <property name="text">
282 <string>Disable Macro HLE</string>
283 </property>
284 </widget>
285 </item>
286 <item row="7" column="0">
287 <widget class="QCheckBox" name="dump_macros"> 274 <widget class="QCheckBox" name="dump_macros">
288 <property name="enabled"> 275 <property name="enabled">
289 <bool>true</bool> 276 <bool>true</bool>
@@ -306,17 +293,27 @@
306 </property> 293 </property>
307 </widget> 294 </widget>
308 </item> 295 </item>
309 <item row="2" column="0"> 296 <item row="6" column="0">
310 <widget class="QCheckBox" name="enable_shader_feedback"> 297 <widget class="QCheckBox" name="dump_shaders">
298 <property name="enabled">
299 <bool>true</bool>
300 </property>
311 <property name="toolTip"> 301 <property name="toolTip">
312 <string>When checked, yuzu will log statistics about the compiled pipeline cache</string> 302 <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
313 </property> 303 </property>
314 <property name="text"> 304 <property name="text">
315 <string>Enable Shader Feedback</string> 305 <string>Dump Game Shaders</string>
316 </property> 306 </property>
317 </widget> 307 </widget>
318 </item> 308 </item>
319 <item row="6" column="0"> 309 <item row="1" column="0">
310 <widget class="QCheckBox" name="enable_renderdoc_hotkey">
311 <property name="text">
312 <string>Enable Renderdoc Hotkey</string>
313 </property>
314 </widget>
315 </item>
316 <item row="7" column="0">
320 <widget class="QCheckBox" name="disable_macro_jit"> 317 <widget class="QCheckBox" name="disable_macro_jit">
321 <property name="enabled"> 318 <property name="enabled">
322 <bool>true</bool> 319 <bool>true</bool>
@@ -330,20 +327,17 @@
330 </widget> 327 </widget>
331 </item> 328 </item>
332 <item row="9" column="0"> 329 <item row="9" column="0">
333 <spacer name="verticalSpacer_5"> 330 <widget class="QCheckBox" name="disable_macro_hle">
334 <property name="orientation"> 331 <property name="enabled">
335 <enum>Qt::Vertical</enum> 332 <bool>true</bool>
336 </property> 333 </property>
337 <property name="sizeType"> 334 <property name="toolTip">
338 <enum>QSizePolicy::Preferred</enum> 335 <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
339 </property> 336 </property>
340 <property name="sizeHint" stdset="0"> 337 <property name="text">
341 <size> 338 <string>Disable Macro HLE</string>
342 <width>20</width>
343 <height>0</height>
344 </size>
345 </property> 339 </property>
346 </spacer> 340 </widget>
347 </item> 341 </item>
348 <item row="0" column="0"> 342 <item row="0" column="0">
349 <widget class="QCheckBox" name="enable_graphics_debugging"> 343 <widget class="QCheckBox" name="enable_graphics_debugging">
@@ -358,23 +352,39 @@
358 </property> 352 </property>
359 </widget> 353 </widget>
360 </item> 354 </item>
361 <item row="5" column="0"> 355 <item row="10" column="0">
362 <widget class="QCheckBox" name="dump_shaders"> 356 <spacer name="verticalSpacer_5">
363 <property name="enabled"> 357 <property name="orientation">
364 <bool>true</bool> 358 <enum>Qt::Vertical</enum>
359 </property>
360 <property name="sizeType">
361 <enum>QSizePolicy::Preferred</enum>
365 </property> 362 </property>
363 <property name="sizeHint" stdset="0">
364 <size>
365 <width>20</width>
366 <height>0</height>
367 </size>
368 </property>
369 </spacer>
370 </item>
371 <item row="2" column="0">
372 <widget class="QCheckBox" name="enable_shader_feedback">
366 <property name="toolTip"> 373 <property name="toolTip">
367 <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string> 374 <string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
368 </property> 375 </property>
369 <property name="text"> 376 <property name="text">
370 <string>Dump Game Shaders</string> 377 <string>Enable Shader Feedback</string>
371 </property> 378 </property>
372 </widget> 379 </widget>
373 </item> 380 </item>
374 <item row="1" column="0"> 381 <item row="5" column="0">
375 <widget class="QCheckBox" name="enable_renderdoc_hotkey"> 382 <widget class="QCheckBox" name="disable_buffer_reorder">
383 <property name="toolTip">
384 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, disables reording of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
385 </property>
376 <property name="text"> 386 <property name="text">
377 <string>Enable Renderdoc Hotkey</string> 387 <string>Disable Buffer Reorder</string>
378 </property> 388 </property>
379 </widget> 389 </widget>
380 </item> 390 </item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 0ad95cc02..aab54a1cc 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -8,7 +8,6 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "ui_configure.h" 9#include "ui_configure.h"
10#include "vk_device_info.h" 10#include "vk_device_info.h"
11#include "yuzu/configuration/config.h"
12#include "yuzu/configuration/configure_audio.h" 11#include "yuzu/configuration/configure_audio.h"
13#include "yuzu/configuration/configure_cpu.h" 12#include "yuzu/configuration/configure_cpu.h"
14#include "yuzu/configuration/configure_debug_tab.h" 13#include "yuzu/configuration/configure_debug_tab.h"
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index c727fadd1..701b895e7 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -36,12 +36,29 @@ ConfigureGeneral::~ConfigureGeneral() = default;
36void ConfigureGeneral::SetConfiguration() {} 36void ConfigureGeneral::SetConfiguration() {}
37 37
38void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { 38void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) {
39 QLayout& layout = *ui->general_widget->layout(); 39 QLayout& general_layout = *ui->general_widget->layout();
40 QLayout& linux_layout = *ui->linux_widget->layout();
40 41
41 std::map<u32, QWidget*> hold{}; 42 std::map<u32, QWidget*> general_hold{};
43 std::map<u32, QWidget*> linux_hold{};
42 44
43 for (const auto setting : 45 std::vector<Settings::BasicSetting*> settings;
44 UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) { 46
47 auto push = [&settings](auto& list) {
48 for (auto setting : list) {
49 settings.push_back(setting);
50 }
51 };
52
53 push(UISettings::values.linkage.by_category[Settings::Category::UiGeneral]);
54 push(Settings::values.linkage.by_category[Settings::Category::Linux]);
55
56 // Only show Linux group on Unix
57#ifndef __unix__
58 ui->LinuxGroupBox->setVisible(false);
59#endif
60
61 for (const auto setting : settings) {
45 auto* widget = builder.BuildWidget(setting, apply_funcs); 62 auto* widget = builder.BuildWidget(setting, apply_funcs);
46 63
47 if (widget == nullptr) { 64 if (widget == nullptr) {
@@ -52,11 +69,23 @@ void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) {
52 continue; 69 continue;
53 } 70 }
54 71
55 hold.emplace(setting->Id(), widget); 72 switch (setting->GetCategory()) {
73 case Settings::Category::UiGeneral:
74 general_hold.emplace(setting->Id(), widget);
75 break;
76 case Settings::Category::Linux:
77 linux_hold.emplace(setting->Id(), widget);
78 break;
79 default:
80 widget->deleteLater();
81 }
56 } 82 }
57 83
58 for (const auto& [id, widget] : hold) { 84 for (const auto& [id, widget] : general_hold) {
59 layout.addWidget(widget); 85 general_layout.addWidget(widget);
86 }
87 for (const auto& [id, widget] : linux_hold) {
88 linux_layout.addWidget(widget);
60 } 89 }
61} 90}
62 91
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index a10e7d3a5..ef20891a3 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -47,6 +47,33 @@
47 </widget> 47 </widget>
48 </item> 48 </item>
49 <item> 49 <item>
50 <widget class="QGroupBox" name="LinuxGroupBox">
51 <property name="title">
52 <string>Linux</string>
53 </property>
54 <layout class="QVBoxLayout" name="LinuxVerticalLayout_1">
55 <item>
56 <widget class="QWidget" name="linux_widget" native="true">
57 <layout class="QVBoxLayout" name="LinuxVerticalLayout_2">
58 <property name="leftMargin">
59 <number>0</number>
60 </property>
61 <property name="topMargin">
62 <number>0</number>
63 </property>
64 <property name="rightMargin">
65 <number>0</number>
66 </property>
67 <property name="bottomMargin">
68 <number>0</number>
69 </property>
70 </layout>
71 </widget>
72 </item>
73 </layout>
74 </widget>
75 </item>
76 <item>
50 <spacer name="verticalSpacer"> 77 <spacer name="verticalSpacer">
51 <property name="orientation"> 78 <property name="orientation">
52 <enum>Qt::Vertical</enum> 79 <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 68e21cd84..76fc33e49 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -9,10 +9,11 @@
9#include "core/hid/emulated_controller.h" 9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h" 10#include "core/hid/hid_core.h"
11 11
12#include "frontend_common/config.h"
12#include "ui_configure_hotkeys.h" 13#include "ui_configure_hotkeys.h"
13#include "yuzu/configuration/config.h"
14#include "yuzu/configuration/configure_hotkeys.h" 14#include "yuzu/configuration/configure_hotkeys.h"
15#include "yuzu/hotkeys.h" 15#include "yuzu/hotkeys.h"
16#include "yuzu/uisettings.h"
16#include "yuzu/util/sequence_dialog/sequence_dialog.h" 17#include "yuzu/util/sequence_dialog/sequence_dialog.h"
17 18
18constexpr int name_column = 0; 19constexpr int name_column = 0;
@@ -62,18 +63,21 @@ ConfigureHotkeys::~ConfigureHotkeys() = default;
62 63
63void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { 64void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
64 for (const auto& group : registry.hotkey_groups) { 65 for (const auto& group : registry.hotkey_groups) {
66 QString parent_item_data = QString::fromStdString(group.first);
65 auto* parent_item = 67 auto* parent_item =
66 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first))); 68 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(parent_item_data)));
67 parent_item->setEditable(false); 69 parent_item->setEditable(false);
68 parent_item->setData(group.first); 70 parent_item->setData(parent_item_data);
69 for (const auto& hotkey : group.second) { 71 for (const auto& hotkey : group.second) {
70 auto* action = 72 QString hotkey_action_data = QString::fromStdString(hotkey.first);
71 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first))); 73 auto* action = new QStandardItem(
74 QCoreApplication::translate("Hotkeys", qPrintable(hotkey_action_data)));
72 auto* keyseq = 75 auto* keyseq =
73 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); 76 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
74 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); 77 auto* controller_keyseq =
78 new QStandardItem(QString::fromStdString(hotkey.second.controller_keyseq));
75 action->setEditable(false); 79 action->setEditable(false);
76 action->setData(hotkey.first); 80 action->setData(hotkey_action_data);
77 keyseq->setEditable(false); 81 keyseq->setEditable(false);
78 controller_keyseq->setEditable(false); 82 controller_keyseq->setEditable(false);
79 parent_item->appendRow({action, keyseq, controller_keyseq}); 83 parent_item->appendRow({action, keyseq, controller_keyseq});
@@ -301,13 +305,13 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
301 const QStandardItem* controller_keyseq = 305 const QStandardItem* controller_keyseq =
302 parent->child(key_column_id, controller_column); 306 parent->child(key_column_id, controller_column);
303 for (auto& [group, sub_actions] : registry.hotkey_groups) { 307 for (auto& [group, sub_actions] : registry.hotkey_groups) {
304 if (group != parent->data()) 308 if (group != parent->data().toString().toStdString())
305 continue; 309 continue;
306 for (auto& [action_name, hotkey] : sub_actions) { 310 for (auto& [action_name, hotkey] : sub_actions) {
307 if (action_name != action->data()) 311 if (action_name != action->data().toString().toStdString())
308 continue; 312 continue;
309 hotkey.keyseq = QKeySequence(keyseq->text()); 313 hotkey.keyseq = QKeySequence(keyseq->text());
310 hotkey.controller_keyseq = controller_keyseq->text(); 314 hotkey.controller_keyseq = controller_keyseq->text().toStdString();
311 } 315 }
312 } 316 }
313 } 317 }
@@ -319,7 +323,7 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
319void ConfigureHotkeys::RestoreDefaults() { 323void ConfigureHotkeys::RestoreDefaults() {
320 for (int r = 0; r < model->rowCount(); ++r) { 324 for (int r = 0; r < model->rowCount(); ++r) {
321 const QStandardItem* parent = model->item(r, 0); 325 const QStandardItem* parent = model->item(r, 0);
322 const int hotkey_size = static_cast<int>(Config::default_hotkeys.size()); 326 const int hotkey_size = static_cast<int>(UISettings::default_hotkeys.size());
323 327
324 if (hotkey_size != parent->rowCount()) { 328 if (hotkey_size != parent->rowCount()) {
325 QMessageBox::warning(this, tr("Invalid hotkey settings"), 329 QMessageBox::warning(this, tr("Invalid hotkey settings"),
@@ -330,10 +334,11 @@ void ConfigureHotkeys::RestoreDefaults() {
330 for (int r2 = 0; r2 < parent->rowCount(); ++r2) { 334 for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
331 model->item(r, 0) 335 model->item(r, 0)
332 ->child(r2, hotkey_column) 336 ->child(r2, hotkey_column)
333 ->setText(Config::default_hotkeys[r2].shortcut.keyseq); 337 ->setText(QString::fromStdString(UISettings::default_hotkeys[r2].shortcut.keyseq));
334 model->item(r, 0) 338 model->item(r, 0)
335 ->child(r2, controller_column) 339 ->child(r2, controller_column)
336 ->setText(Config::default_hotkeys[r2].shortcut.controller_keyseq); 340 ->setText(QString::fromStdString(
341 UISettings::default_hotkeys[r2].shortcut.controller_keyseq));
337 } 342 }
338 } 343 }
339} 344}
@@ -379,7 +384,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
379 384
380void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { 385void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
381 const QString& default_key_sequence = 386 const QString& default_key_sequence =
382 Config::default_hotkeys[index.row()].shortcut.controller_keyseq; 387 QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.controller_keyseq);
383 const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence); 388 const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence);
384 389
385 if (key_sequence_used && default_key_sequence != model->data(index).toString()) { 390 if (key_sequence_used && default_key_sequence != model->data(index).toString()) {
@@ -393,7 +398,8 @@ void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
393 398
394void ConfigureHotkeys::RestoreHotkey(QModelIndex index) { 399void ConfigureHotkeys::RestoreHotkey(QModelIndex index) {
395 const QKeySequence& default_key_sequence = QKeySequence::fromString( 400 const QKeySequence& default_key_sequence = QKeySequence::fromString(
396 Config::default_hotkeys[index.row()].shortcut.keyseq, QKeySequence::NativeText); 401 QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.keyseq),
402 QKeySequence::NativeText);
397 const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); 403 const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence);
398 404
399 if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) { 405 if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) {
diff --git a/src/yuzu/configuration/configure_input_per_game.cpp b/src/yuzu/configuration/configure_input_per_game.cpp
index 78e65d468..8d9f65a05 100644
--- a/src/yuzu/configuration/configure_input_per_game.cpp
+++ b/src/yuzu/configuration/configure_input_per_game.cpp
@@ -5,12 +5,12 @@
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hid/emulated_controller.h" 6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
8#include "frontend_common/config.h"
8#include "ui_configure_input_per_game.h" 9#include "ui_configure_input_per_game.h"
9#include "yuzu/configuration/config.h"
10#include "yuzu/configuration/configure_input_per_game.h" 10#include "yuzu/configuration/configure_input_per_game.h"
11#include "yuzu/configuration/input_profiles.h" 11#include "yuzu/configuration/input_profiles.h"
12 12
13ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, Config* config_, 13ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, QtConfig* config_,
14 QWidget* parent) 14 QWidget* parent)
15 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()), 15 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()),
16 profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} { 16 profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} {
@@ -110,6 +110,6 @@ void ConfigureInputPerGame::SaveConfiguration() {
110 // Clear all controls from the config in case the user reverted back to globals 110 // Clear all controls from the config in case the user reverted back to globals
111 config->ClearControlPlayerValues(); 111 config->ClearControlPlayerValues();
112 for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) { 112 for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) {
113 config->SaveControlPlayerValue(index); 113 config->SaveQtControlPlayerValues(index);
114 } 114 }
115} 115}
diff --git a/src/yuzu/configuration/configure_input_per_game.h b/src/yuzu/configuration/configure_input_per_game.h
index 660faf574..4420e856c 100644
--- a/src/yuzu/configuration/configure_input_per_game.h
+++ b/src/yuzu/configuration/configure_input_per_game.h
@@ -9,6 +9,7 @@
9 9
10#include "ui_configure_input_per_game.h" 10#include "ui_configure_input_per_game.h"
11#include "yuzu/configuration/input_profiles.h" 11#include "yuzu/configuration/input_profiles.h"
12#include "yuzu/configuration/qt_config.h"
12 13
13class QComboBox; 14class QComboBox;
14 15
@@ -22,7 +23,7 @@ class ConfigureInputPerGame : public QWidget {
22 Q_OBJECT 23 Q_OBJECT
23 24
24public: 25public:
25 explicit ConfigureInputPerGame(Core::System& system_, Config* config_, 26 explicit ConfigureInputPerGame(Core::System& system_, QtConfig* config_,
26 QWidget* parent = nullptr); 27 QWidget* parent = nullptr);
27 28
28 /// Load and Save configurations to settings file. 29 /// Load and Save configurations to settings file.
@@ -41,5 +42,5 @@ private:
41 std::array<QComboBox*, 8> profile_comboboxes; 42 std::array<QComboBox*, 8> profile_comboboxes;
42 43
43 Core::System& system; 44 Core::System& system;
44 Config* config; 45 QtConfig* config;
45}; 46};
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 9259e2a5d..0f7b3714e 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -12,15 +12,16 @@
12#include <QTimer> 12#include <QTimer>
13#include "common/assert.h" 13#include "common/assert.h"
14#include "common/param_package.h" 14#include "common/param_package.h"
15#include "configuration/qt_config.h"
15#include "core/hid/emulated_controller.h" 16#include "core/hid/emulated_controller.h"
16#include "core/hid/hid_core.h" 17#include "core/hid/hid_core.h"
17#include "core/hid/hid_types.h" 18#include "core/hid/hid_types.h"
19#include "frontend_common/config.h"
18#include "input_common/drivers/keyboard.h" 20#include "input_common/drivers/keyboard.h"
19#include "input_common/drivers/mouse.h" 21#include "input_common/drivers/mouse.h"
20#include "input_common/main.h" 22#include "input_common/main.h"
21#include "ui_configure_input_player.h" 23#include "ui_configure_input_player.h"
22#include "yuzu/bootmanager.h" 24#include "yuzu/bootmanager.h"
23#include "yuzu/configuration/config.h"
24#include "yuzu/configuration/configure_input_player.h" 25#include "yuzu/configuration/configure_input_player.h"
25#include "yuzu/configuration/configure_input_player_widget.h" 26#include "yuzu/configuration/configure_input_player_widget.h"
26#include "yuzu/configuration/configure_mouse_panning.h" 27#include "yuzu/configuration/configure_mouse_panning.h"
@@ -1397,25 +1398,25 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
1397 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { 1398 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
1398 emulated_controller->SetButtonParam( 1399 emulated_controller->SetButtonParam(
1399 button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( 1400 button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1400 Config::default_buttons[button_id])}); 1401 QtConfig::default_buttons[button_id])});
1401 } 1402 }
1402 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 1403 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
1403 Common::ParamPackage analog_param{}; 1404 Common::ParamPackage analog_param{};
1404 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 1405 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
1405 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 1406 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
1406 Config::default_analogs[analog_id][sub_button_id])}; 1407 QtConfig::default_analogs[analog_id][sub_button_id])};
1407 SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); 1408 SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]);
1408 } 1409 }
1409 1410
1410 analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( 1411 analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
1411 Config::default_stick_mod[analog_id])); 1412 QtConfig::default_stick_mod[analog_id]));
1412 emulated_controller->SetStickParam(analog_id, analog_param); 1413 emulated_controller->SetStickParam(analog_id, analog_param);
1413 } 1414 }
1414 1415
1415 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { 1416 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
1416 emulated_controller->SetMotionParam( 1417 emulated_controller->SetMotionParam(
1417 motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( 1418 motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1418 Config::default_motions[motion_id])}); 1419 QtConfig::default_motions[motion_id])});
1419 } 1420 }
1420 1421
1421 // If mouse is selected we want to override with mappings from the driver 1422 // If mouse is selected we want to override with mappings from the driver
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index b91d6ad4a..b274a3321 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -25,8 +25,8 @@
25#include "core/file_sys/patch_manager.h" 25#include "core/file_sys/patch_manager.h"
26#include "core/file_sys/xts_archive.h" 26#include "core/file_sys/xts_archive.h"
27#include "core/loader/loader.h" 27#include "core/loader/loader.h"
28#include "frontend_common/config.h"
28#include "ui_configure_per_game.h" 29#include "ui_configure_per_game.h"
29#include "yuzu/configuration/config.h"
30#include "yuzu/configuration/configuration_shared.h" 30#include "yuzu/configuration/configuration_shared.h"
31#include "yuzu/configuration/configure_audio.h" 31#include "yuzu/configuration/configure_audio.h"
32#include "yuzu/configuration/configure_cpu.h" 32#include "yuzu/configuration/configure_cpu.h"
@@ -50,8 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
50 const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); 50 const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
51 const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) 51 const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
52 : fmt::format("{:016X}", title_id); 52 : fmt::format("{:016X}", title_id);
53 game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); 53 game_config = std::make_unique<QtConfig>(config_file_name, Config::ConfigType::PerGameConfig);
54
55 addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); 54 addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
56 audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this); 55 audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this);
57 cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this); 56 cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this);
@@ -108,7 +107,7 @@ void ConfigurePerGame::ApplyConfiguration() {
108 system.ApplySettings(); 107 system.ApplySettings();
109 Settings::LogSettings(); 108 Settings::LogSettings();
110 109
111 game_config->Save(); 110 game_config->SaveAllValues();
112} 111}
113 112
114void ConfigurePerGame::changeEvent(QEvent* event) { 113void ConfigurePerGame::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index cc2513001..c8ee46c04 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -12,9 +12,10 @@
12 12
13#include "configuration/shared_widget.h" 13#include "configuration/shared_widget.h"
14#include "core/file_sys/vfs_types.h" 14#include "core/file_sys/vfs_types.h"
15#include "frontend_common/config.h"
15#include "vk_device_info.h" 16#include "vk_device_info.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configuration_shared.h" 17#include "yuzu/configuration/configuration_shared.h"
18#include "yuzu/configuration/qt_config.h"
18#include "yuzu/configuration/shared_translation.h" 19#include "yuzu/configuration/shared_translation.h"
19 20
20namespace Core { 21namespace Core {
@@ -72,7 +73,7 @@ private:
72 73
73 QGraphicsScene* scene; 74 QGraphicsScene* scene;
74 75
75 std::unique_ptr<Config> game_config; 76 std::unique_ptr<QtConfig> game_config;
76 77
77 Core::System& system; 78 Core::System& system;
78 std::unique_ptr<ConfigurationShared::Builder> builder; 79 std::unique_ptr<ConfigurationShared::Builder> builder;
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 674a75a62..140a7fe5d 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -19,7 +19,6 @@
19#include "core/file_sys/xts_archive.h" 19#include "core/file_sys/xts_archive.h"
20#include "core/loader/loader.h" 20#include "core/loader/loader.h"
21#include "ui_configure_per_game_addons.h" 21#include "ui_configure_per_game_addons.h"
22#include "yuzu/configuration/config.h"
23#include "yuzu/configuration/configure_input.h" 22#include "yuzu/configuration/configure_input.h"
24#include "yuzu/configuration/configure_per_game_addons.h" 23#include "yuzu/configuration/configure_per_game_addons.h"
25#include "yuzu/uisettings.h" 24#include "yuzu/uisettings.h"
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index f83705544..9572ff43c 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -8,6 +8,7 @@
8#include <QTimer> 8#include <QTimer>
9#include <fmt/format.h> 9#include <fmt/format.h>
10 10
11#include "configuration/qt_config.h"
11#include "core/hid/emulated_controller.h" 12#include "core/hid/emulated_controller.h"
12#include "core/hid/hid_core.h" 13#include "core/hid/hid_core.h"
13#include "input_common/drivers/keyboard.h" 14#include "input_common/drivers/keyboard.h"
@@ -15,7 +16,6 @@
15#include "input_common/main.h" 16#include "input_common/main.h"
16#include "ui_configure_ringcon.h" 17#include "ui_configure_ringcon.h"
17#include "yuzu/bootmanager.h" 18#include "yuzu/bootmanager.h"
18#include "yuzu/configuration/config.h"
19#include "yuzu/configuration/configure_ringcon.h" 19#include "yuzu/configuration/configure_ringcon.h"
20 20
21const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM> 21const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM>
@@ -270,7 +270,7 @@ void ConfigureRingController::LoadConfiguration() {
270 270
271void ConfigureRingController::RestoreDefaults() { 271void ConfigureRingController::RestoreDefaults() {
272 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( 272 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
273 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); 273 0, 0, QtConfig::default_ringcon_analogs[0], QtConfig::default_ringcon_analogs[1], 0, 0.05f);
274 emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string)); 274 emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string));
275 UpdateUI(); 275 UpdateUI();
276} 276}
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 0c8e5c8b4..7cbf43775 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -16,7 +16,6 @@
16#include "core/core.h" 16#include "core/core.h"
17#include "core/hle/service/time/time_manager.h" 17#include "core/hle/service/time/time_manager.h"
18#include "ui_configure_system.h" 18#include "ui_configure_system.h"
19#include "yuzu/configuration/config.h"
20#include "yuzu/configuration/configuration_shared.h" 19#include "yuzu/configuration/configuration_shared.h"
21#include "yuzu/configuration/configure_system.h" 20#include "yuzu/configuration/configure_system.h"
22#include "yuzu/configuration/shared_widget.h" 21#include "yuzu/configuration/shared_widget.h"
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 2a735836e..04b771129 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -57,7 +57,7 @@
57 </widget> 57 </widget>
58 </item> 58 </item>
59 <item> 59 <item>
60 <widget class="QGroupBox" name="groupBox"> 60 <widget class="QGroupBox" name="coreGroup">
61 <property name="title"> 61 <property name="title">
62 <string>Core</string> 62 <string>Core</string>
63 </property> 63 </property>
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
index 5a03e48df..94df6d9d3 100644
--- a/src/yuzu/configuration/configure_touchscreen_advanced.cpp
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
@@ -2,8 +2,8 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <memory> 4#include <memory>
5#include "common/settings.h"
5#include "ui_configure_touchscreen_advanced.h" 6#include "ui_configure_touchscreen_advanced.h"
6#include "yuzu/configuration/config.h"
7#include "yuzu/configuration/configure_touchscreen_advanced.h" 7#include "yuzu/configuration/configure_touchscreen_advanced.h"
8 8
9ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent) 9ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 82f3b6e78..dd43f0a0e 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -164,7 +164,7 @@ ConfigureUi::~ConfigureUi() = default;
164 164
165void ConfigureUi::ApplyConfiguration() { 165void ConfigureUi::ApplyConfiguration() {
166 UISettings::values.theme = 166 UISettings::values.theme =
167 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 167 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString().toStdString();
168 UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); 168 UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
169 UISettings::values.show_compat = ui->show_compat->isChecked(); 169 UISettings::values.show_compat = ui->show_compat->isChecked();
170 UISettings::values.show_size = ui->show_size->isChecked(); 170 UISettings::values.show_size = ui->show_size->isChecked();
@@ -191,9 +191,10 @@ void ConfigureUi::RequestGameListUpdate() {
191} 191}
192 192
193void ConfigureUi::SetConfiguration() { 193void ConfigureUi::SetConfiguration() {
194 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 194 ui->theme_combobox->setCurrentIndex(
195 ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme)));
195 ui->language_combobox->setCurrentIndex( 196 ui->language_combobox->setCurrentIndex(
196 ui->language_combobox->findData(UISettings::values.language)); 197 ui->language_combobox->findData(QString::fromStdString(UISettings::values.language)));
197 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); 198 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
198 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); 199 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
199 ui->show_size->setChecked(UISettings::values.show_size.GetValue()); 200 ui->show_size->setChecked(UISettings::values.show_size.GetValue());
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 41ef4250a..716efbccd 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -5,7 +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 "yuzu/configuration/config.h" 8#include "frontend_common/config.h"
9#include "yuzu/configuration/input_profiles.h" 9#include "yuzu/configuration/input_profiles.h"
10 10
11namespace FS = Common::FS; 11namespace FS = Common::FS;
@@ -44,7 +44,7 @@ InputProfiles::InputProfiles() {
44 if (IsINI(filename) && IsProfileNameValid(name_without_ext)) { 44 if (IsINI(filename) && IsProfileNameValid(name_without_ext)) {
45 map_profiles.insert_or_assign( 45 map_profiles.insert_or_assign(
46 name_without_ext, 46 name_without_ext,
47 std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile)); 47 std::make_unique<QtConfig>(name_without_ext, Config::ConfigType::InputProfile));
48 } 48 }
49 49
50 return true; 50 return true;
@@ -85,7 +85,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p
85 } 85 }
86 86
87 map_profiles.insert_or_assign( 87 map_profiles.insert_or_assign(
88 profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile)); 88 profile_name, std::make_unique<QtConfig>(profile_name, Config::ConfigType::InputProfile));
89 89
90 return SaveProfile(profile_name, player_index); 90 return SaveProfile(profile_name, player_index);
91} 91}
@@ -113,7 +113,7 @@ bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t pla
113 return false; 113 return false;
114 } 114 }
115 115
116 map_profiles[profile_name]->ReadControlPlayerValue(player_index); 116 map_profiles[profile_name]->ReadQtControlPlayerValues(player_index);
117 return true; 117 return true;
118} 118}
119 119
@@ -122,7 +122,7 @@ bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t pla
122 return false; 122 return false;
123 } 123 }
124 124
125 map_profiles[profile_name]->SaveControlPlayerValue(player_index); 125 map_profiles[profile_name]->SaveQtControlPlayerValues(player_index);
126 return true; 126 return true;
127} 127}
128 128
diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h
index 2bf3e4250..023ec74a6 100644
--- a/src/yuzu/configuration/input_profiles.h
+++ b/src/yuzu/configuration/input_profiles.h
@@ -6,6 +6,8 @@
6#include <string> 6#include <string>
7#include <unordered_map> 7#include <unordered_map>
8 8
9#include "configuration/qt_config.h"
10
9namespace Core { 11namespace Core {
10class System; 12class System;
11} 13}
@@ -30,5 +32,5 @@ public:
30private: 32private:
31 bool ProfileExistsInMap(const std::string& profile_name) const; 33 bool ProfileExistsInMap(const std::string& profile_name) const;
32 34
33 std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles; 35 std::unordered_map<std::string, std::unique_ptr<QtConfig>> map_profiles;
34}; 36};
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
new file mode 100644
index 000000000..5a8e69aa9
--- /dev/null
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -0,0 +1,549 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "input_common/main.h"
5#include "qt_config.h"
6#include "uisettings.h"
7
8const std::array<int, Settings::NativeButton::NumButtons> QtConfig::default_buttons = {
9 Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F,
10 Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T,
11 Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right,
12 Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0,
13 Qt::Key_Q, Qt::Key_E,
14};
15
16const std::array<int, Settings::NativeMotion::NumMotions> QtConfig::default_motions = {
17 Qt::Key_7,
18 Qt::Key_8,
19};
20
21const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{
22 {
23 Qt::Key_W,
24 Qt::Key_S,
25 Qt::Key_A,
26 Qt::Key_D,
27 },
28 {
29 Qt::Key_I,
30 Qt::Key_K,
31 Qt::Key_J,
32 Qt::Key_L,
33 },
34}};
35
36const std::array<int, 2> QtConfig::default_stick_mod = {
37 Qt::Key_Shift,
38 0,
39};
40
41const std::array<int, 2> QtConfig::default_ringcon_analogs{{
42 Qt::Key_A,
43 Qt::Key_D,
44}};
45
46QtConfig::QtConfig(const std::string& config_name, const ConfigType config_type)
47 : Config(config_type) {
48 Initialize(config_name);
49 if (config_type != ConfigType::InputProfile) {
50 ReadQtValues();
51 SaveQtValues();
52 }
53}
54
55QtConfig::~QtConfig() {
56 if (global) {
57 QtConfig::SaveAllValues();
58 }
59}
60
61void QtConfig::ReloadAllValues() {
62 Reload();
63 ReadQtValues();
64 SaveQtValues();
65}
66
67void QtConfig::SaveAllValues() {
68 Save();
69 SaveQtValues();
70}
71
72void QtConfig::ReadQtValues() {
73 if (global) {
74 ReadUIValues();
75 }
76 ReadQtControlValues();
77}
78
79void QtConfig::ReadQtPlayerValues(const std::size_t player_index) {
80 std::string player_prefix;
81 if (type != ConfigType::InputProfile) {
82 player_prefix.append("player_").append(ToString(player_index)).append("_");
83 }
84
85 auto& player = Settings::values.players.GetValue()[player_index];
86 if (IsCustomConfig()) {
87 const auto profile_name =
88 ReadStringSetting(std::string(player_prefix).append("profile_name"));
89 if (profile_name.empty()) {
90 // Use the global input config
91 player = Settings::values.players.GetValue(true)[player_index];
92 return;
93 }
94 }
95
96 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
97 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
98 auto& player_buttons = player.buttons[i];
99
100 player_buttons = ReadStringSetting(
101 std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
102 if (player_buttons.empty()) {
103 player_buttons = default_param;
104 }
105 }
106
107 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
108 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
109 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
110 default_analogs[i][3], default_stick_mod[i], 0.5f);
111 auto& player_analogs = player.analogs[i];
112
113 player_analogs = ReadStringSetting(
114 std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
115 if (player_analogs.empty()) {
116 player_analogs = default_param;
117 }
118 }
119
120 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
121 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
122 auto& player_motions = player.motions[i];
123
124 player_motions = ReadStringSetting(
125 std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
126 if (player_motions.empty()) {
127 player_motions = default_param;
128 }
129 }
130}
131
132void QtConfig::ReadHidbusValues() {
133 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
134 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
135 auto& ringcon_analogs = Settings::values.ringcon_analogs;
136
137 ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param);
138 if (ringcon_analogs.empty()) {
139 ringcon_analogs = default_param;
140 }
141}
142
143void QtConfig::ReadDebugControlValues() {
144 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
145 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
146 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
147
148 debug_pad_buttons = ReadStringSetting(
149 std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param);
150 if (debug_pad_buttons.empty()) {
151 debug_pad_buttons = default_param;
152 }
153 }
154
155 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
156 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
157 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
158 default_analogs[i][3], default_stick_mod[i], 0.5f);
159 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
160
161 debug_pad_analogs = ReadStringSetting(
162 std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param);
163 if (debug_pad_analogs.empty()) {
164 debug_pad_analogs = default_param;
165 }
166 }
167}
168
169void QtConfig::ReadQtControlValues() {
170 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
171
172 Settings::values.players.SetGlobal(!IsCustomConfig());
173 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
174 ReadQtPlayerValues(p);
175 }
176 if (IsCustomConfig()) {
177 EndGroup();
178 return;
179 }
180 ReadDebugControlValues();
181 ReadHidbusValues();
182
183 EndGroup();
184}
185
186void QtConfig::ReadPathValues() {
187 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
188
189 UISettings::values.roms_path = ReadStringSetting(std::string("romsPath"));
190 UISettings::values.symbols_path = ReadStringSetting(std::string("symbolsPath"));
191 UISettings::values.game_dir_deprecated =
192 ReadStringSetting(std::string("gameListRootDir"), std::string("."));
193 UISettings::values.game_dir_deprecated_deepscan =
194 ReadBooleanSetting(std::string("gameListDeepScan"), std::make_optional(false));
195
196 const int gamedirs_size = BeginArray(std::string("gamedirs"));
197 for (int i = 0; i < gamedirs_size; ++i) {
198 SetArrayIndex(i);
199 UISettings::GameDir game_dir;
200 game_dir.path = ReadStringSetting(std::string("path"));
201 game_dir.deep_scan =
202 ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false));
203 game_dir.expanded = ReadBooleanSetting(std::string("expanded"), std::make_optional(true));
204 UISettings::values.game_dirs.append(game_dir);
205 }
206 EndArray();
207
208 // Create NAND and SD card directories if empty, these are not removable through the UI,
209 // also carries over old game list settings if present
210 if (UISettings::values.game_dirs.empty()) {
211 UISettings::GameDir game_dir;
212 game_dir.path = std::string("SDMC");
213 game_dir.expanded = true;
214 UISettings::values.game_dirs.append(game_dir);
215 game_dir.path = std::string("UserNAND");
216 UISettings::values.game_dirs.append(game_dir);
217 game_dir.path = std::string("SysNAND");
218 UISettings::values.game_dirs.append(game_dir);
219 if (UISettings::values.game_dir_deprecated != std::string(".")) {
220 game_dir.path = UISettings::values.game_dir_deprecated;
221 game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan;
222 UISettings::values.game_dirs.append(game_dir);
223 }
224 }
225 UISettings::values.recent_files =
226 QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
227 .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
228 UISettings::values.language = ReadStringSetting(std::string("language"), std::string(""));
229
230 EndGroup();
231}
232
233void QtConfig::ReadShortcutValues() {
234 BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts));
235
236 for (const auto& [name, group, shortcut] : UISettings::default_hotkeys) {
237 BeginGroup(group);
238 BeginGroup(name);
239
240 // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
241 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
242 // a file dialog in windowed mode
243 UISettings::values.shortcuts.push_back(
244 {name,
245 group,
246 {ReadStringSetting(std::string("KeySeq"), shortcut.keyseq),
247 ReadStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq),
248 shortcut.context,
249 ReadBooleanSetting(std::string("Repeat"), std::optional(shortcut.repeat))}});
250
251 EndGroup(); // name
252 EndGroup(); // group
253 }
254
255 EndGroup();
256}
257
258void QtConfig::ReadUIValues() {
259 BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
260
261 UISettings::values.theme = ReadStringSetting(
262 std::string("theme"),
263 std::string(UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second));
264
265 ReadUIGamelistValues();
266 ReadUILayoutValues();
267 ReadPathValues();
268 ReadScreenshotValues();
269 ReadShortcutValues();
270 ReadMultiplayerValues();
271
272 ReadCategory(Settings::Category::Ui);
273 ReadCategory(Settings::Category::UiGeneral);
274
275 EndGroup();
276}
277
278void QtConfig::ReadUIGamelistValues() {
279 BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
280
281 ReadCategory(Settings::Category::UiGameList);
282
283 const int favorites_size = BeginArray("favorites");
284 for (int i = 0; i < favorites_size; i++) {
285 SetArrayIndex(i);
286 UISettings::values.favorited_ids.append(
287 ReadUnsignedIntegerSetting(std::string("program_id")));
288 }
289 EndArray();
290
291 EndGroup();
292}
293
294void QtConfig::ReadUILayoutValues() {
295 BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
296
297 ReadCategory(Settings::Category::UiLayout);
298
299 EndGroup();
300}
301
302void QtConfig::ReadMultiplayerValues() {
303 BeginGroup(Settings::TranslateCategory(Settings::Category::Multiplayer));
304
305 ReadCategory(Settings::Category::Multiplayer);
306
307 // Read ban list back
308 int size = BeginArray(std::string("username_ban_list"));
309 UISettings::values.multiplayer_ban_list.first.resize(size);
310 for (int i = 0; i < size; ++i) {
311 SetArrayIndex(i);
312 UISettings::values.multiplayer_ban_list.first[i] =
313 ReadStringSetting(std::string("username"), std::string(""));
314 }
315 EndArray();
316
317 size = BeginArray(std::string("ip_ban_list"));
318 UISettings::values.multiplayer_ban_list.second.resize(size);
319 for (int i = 0; i < size; ++i) {
320 UISettings::values.multiplayer_ban_list.second[i] =
321 ReadStringSetting("username", std::string(""));
322 }
323 EndArray();
324
325 EndGroup();
326}
327
328void QtConfig::SaveQtValues() {
329 if (global) {
330 SaveUIValues();
331 }
332 SaveQtControlValues();
333
334 WriteToIni();
335}
336
337void QtConfig::SaveQtPlayerValues(const std::size_t player_index) {
338 std::string player_prefix;
339 if (type != ConfigType::InputProfile) {
340 player_prefix = std::string("player_").append(ToString(player_index)).append("_");
341 }
342
343 const auto& player = Settings::values.players.GetValue()[player_index];
344 if (IsCustomConfig() && player.profile_name.empty()) {
345 // No custom profile selected
346 return;
347 }
348
349 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
350 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
351 WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
352 player.buttons[i], std::make_optional(default_param));
353 }
354 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
355 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
356 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
357 default_analogs[i][3], default_stick_mod[i], 0.5f);
358 WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
359 player.analogs[i], std::make_optional(default_param));
360 }
361 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
362 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
363 WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
364 player.motions[i], std::make_optional(default_param));
365 }
366}
367
368void QtConfig::SaveDebugControlValues() {
369 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
370 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
371 WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
372 Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
373 }
374 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
375 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
376 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
377 default_analogs[i][3], default_stick_mod[i], 0.5f);
378 WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
379 Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
380 }
381}
382
383void QtConfig::SaveHidbusValues() {
384 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
385 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
386 WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
387 std::make_optional(default_param));
388}
389
390void QtConfig::SaveQtControlValues() {
391 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
392
393 Settings::values.players.SetGlobal(!IsCustomConfig());
394 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
395 SaveQtPlayerValues(p);
396 }
397 if (IsCustomConfig()) {
398 EndGroup();
399 return;
400 }
401 SaveDebugControlValues();
402 SaveHidbusValues();
403
404 EndGroup();
405}
406
407void QtConfig::SavePathValues() {
408 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
409
410 WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
411 WriteSetting(std::string("symbolsPath"), UISettings::values.symbols_path);
412 BeginArray(std::string("gamedirs"));
413 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
414 SetArrayIndex(i);
415 const auto& game_dir = UISettings::values.game_dirs[i];
416 WriteSetting(std::string("path"), game_dir.path);
417 WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false));
418 WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
419 }
420 EndArray();
421
422 WriteSetting(std::string("recentFiles"),
423 UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
424 WriteSetting(std::string("language"), UISettings::values.language);
425
426 EndGroup();
427}
428
429void QtConfig::SaveShortcutValues() {
430 BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts));
431
432 // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
433 // However, their ordering must also be the same.
434 for (std::size_t i = 0; i < UISettings::default_hotkeys.size(); i++) {
435 const auto& [name, group, shortcut] = UISettings::values.shortcuts[i];
436 const auto& default_hotkey = UISettings::default_hotkeys[i].shortcut;
437
438 BeginGroup(group);
439 BeginGroup(name);
440
441 WriteSetting(std::string("KeySeq"), shortcut.keyseq,
442 std::make_optional(default_hotkey.keyseq));
443 WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
444 std::make_optional(default_hotkey.controller_keyseq));
445 WriteSetting(std::string("Context"), shortcut.context,
446 std::make_optional(default_hotkey.context));
447 WriteSetting(std::string("Repeat"), shortcut.repeat,
448 std::make_optional(default_hotkey.repeat));
449
450 EndGroup(); // name
451 EndGroup(); // group
452 }
453
454 EndGroup();
455}
456
457void QtConfig::SaveUIValues() {
458 BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
459
460 WriteCategory(Settings::Category::Ui);
461 WriteCategory(Settings::Category::UiGeneral);
462
463 WriteSetting(std::string("theme"), UISettings::values.theme,
464 std::make_optional(std::string(
465 UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
466
467 SaveUIGamelistValues();
468 SaveUILayoutValues();
469 SavePathValues();
470 SaveScreenshotValues();
471 SaveShortcutValues();
472 SaveMultiplayerValues();
473
474 EndGroup();
475}
476
477void QtConfig::SaveUIGamelistValues() {
478 BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
479
480 WriteCategory(Settings::Category::UiGameList);
481
482 BeginArray(std::string("favorites"));
483 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
484 SetArrayIndex(i);
485 WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
486 }
487 EndArray(); // favorites
488
489 EndGroup();
490}
491
492void QtConfig::SaveUILayoutValues() {
493 BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout));
494
495 WriteCategory(Settings::Category::UiLayout);
496
497 EndGroup();
498}
499
500void QtConfig::SaveMultiplayerValues() {
501 BeginGroup(std::string("Multiplayer"));
502
503 WriteCategory(Settings::Category::Multiplayer);
504
505 // Write ban list
506 BeginArray(std::string("username_ban_list"));
507 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
508 SetArrayIndex(static_cast<int>(i));
509 WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]);
510 }
511 EndArray(); // username_ban_list
512
513 BeginArray(std::string("ip_ban_list"));
514 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
515 SetArrayIndex(static_cast<int>(i));
516 WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
517 }
518 EndArray(); // ip_ban_list
519
520 EndGroup();
521}
522
523std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) {
524 auto& map = Settings::values.linkage.by_category;
525 if (map.contains(category)) {
526 return Settings::values.linkage.by_category[category];
527 }
528 return UISettings::values.linkage.by_category[category];
529}
530
531void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) {
532 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
533
534 ReadPlayerValues(player_index);
535 ReadQtPlayerValues(player_index);
536
537 EndGroup();
538}
539
540void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) {
541 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
542
543 SavePlayerValues(player_index);
544 SaveQtPlayerValues(player_index);
545
546 EndGroup();
547
548 WriteToIni();
549}
diff --git a/src/yuzu/configuration/qt_config.h b/src/yuzu/configuration/qt_config.h
new file mode 100644
index 000000000..dc2dceb4d
--- /dev/null
+++ b/src/yuzu/configuration/qt_config.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <QMetaType>
7
8#include "frontend_common/config.h"
9
10class QtConfig final : public Config {
11public:
12 explicit QtConfig(const std::string& config_name = "qt-config",
13 ConfigType config_type = ConfigType::GlobalConfig);
14 ~QtConfig() override;
15
16 void ReloadAllValues() override;
17 void SaveAllValues() override;
18
19 void ReadQtControlPlayerValues(std::size_t player_index);
20 void SaveQtControlPlayerValues(std::size_t player_index);
21
22protected:
23 void ReadQtValues();
24 void ReadQtPlayerValues(std::size_t player_index);
25 void ReadQtControlValues();
26 void ReadHidbusValues() override;
27 void ReadDebugControlValues() override;
28 void ReadPathValues() override;
29 void ReadShortcutValues() override;
30 void ReadUIValues() override;
31 void ReadUIGamelistValues() override;
32 void ReadUILayoutValues() override;
33 void ReadMultiplayerValues() override;
34
35 void SaveQtValues();
36 void SaveQtPlayerValues(std::size_t player_index);
37 void SaveQtControlValues();
38 void SaveHidbusValues() override;
39 void SaveDebugControlValues() override;
40 void SavePathValues() override;
41 void SaveShortcutValues() override;
42 void SaveUIValues() override;
43 void SaveUIGamelistValues() override;
44 void SaveUILayoutValues() override;
45 void SaveMultiplayerValues() override;
46
47 std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
48
49public:
50 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
51 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
52 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
53 static const std::array<int, 2> default_stick_mod;
54 static const std::array<int, 2> default_ringcon_analogs;
55};
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index 1434b1a56..7e908924c 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -1,17 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 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/time_zone.h"
5#include "yuzu/configuration/shared_translation.h" 4#include "yuzu/configuration/shared_translation.h"
6 5
7#include <map> 6#include <map>
8#include <memory> 7#include <memory>
9#include <tuple> 8#include <tuple>
10#include <utility> 9#include <utility>
10#include <QCoreApplication>
11#include <QWidget> 11#include <QWidget>
12#include "common/settings.h" 12#include "common/settings.h"
13#include "common/settings_enums.h" 13#include "common/settings_enums.h"
14#include "common/settings_setting.h" 14#include "common/settings_setting.h"
15#include "common/time_zone.h"
15#include "yuzu/uisettings.h" 16#include "yuzu/uisettings.h"
16 17
17namespace ConfigurationShared { 18namespace ConfigurationShared {
@@ -21,123 +22,136 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
21 const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); }; 22 const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); };
22 23
23#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \ 24#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \
24 translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{tr((NAME)), tr((TOOLTIP))}}) 25 translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}})
25 26
26 // A setting can be ignored by giving it a blank name 27 // A setting can be ignored by giving it a blank name
27 28
28 // Audio 29 // Audio
29 INSERT(Settings, sink_id, "Output Engine:", ""); 30 INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral());
30 INSERT(Settings, audio_output_device_id, "Output Device:", ""); 31 INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral());
31 INSERT(Settings, audio_input_device_id, "Input Device:", ""); 32 INSERT(Settings, audio_input_device_id, tr("Input Device:"), QStringLiteral());
32 INSERT(Settings, audio_muted, "Mute audio", ""); 33 INSERT(Settings, audio_muted, tr("Mute audio"), QStringLiteral());
33 INSERT(Settings, volume, "Volume:", ""); 34 INSERT(Settings, volume, tr("Volume:"), QStringLiteral());
34 INSERT(Settings, dump_audio_commands, "", ""); 35 INSERT(Settings, dump_audio_commands, QStringLiteral(), QStringLiteral());
35 INSERT(UISettings, mute_when_in_background, "Mute audio when in background", ""); 36 INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"),
37 QStringLiteral());
36 38
37 // Core 39 // Core
38 INSERT(Settings, use_multi_core, "Multicore CPU Emulation", ""); 40 INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral());
39 INSERT(Settings, memory_layout_mode, "Memory Layout", ""); 41 INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral());
40 INSERT(Settings, use_speed_limit, "", ""); 42 INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral());
41 INSERT(Settings, speed_limit, "Limit Speed Percent", ""); 43 INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral());
42 44
43 // Cpu 45 // Cpu
44 INSERT(Settings, cpu_accuracy, "Accuracy:", ""); 46 INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral());
47 INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral());
45 48
46 // Cpu Debug 49 // Cpu Debug
47 50
48 // Cpu Unsafe 51 // Cpu Unsafe
49 INSERT(Settings, cpuopt_unsafe_unfuse_fma,
50 "Unfuse FMA (improve performance on CPUs without FMA)",
51 "This option improves speed by reducing accuracy of fused-multiply-add instructions on "
52 "CPUs without native FMA support.");
53 INSERT(Settings, cpuopt_unsafe_reduce_fp_error, "Faster FRSQRTE and FRECPE",
54 "This option improves the speed of some approximate floating-point functions by using "
55 "less accurate native approximations.");
56 INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, "Faster ASIMD instructions (32 bits only)",
57 "This option improves the speed of 32 bits ASIMD floating-point functions by running "
58 "with incorrect rounding modes.");
59 INSERT(Settings, cpuopt_unsafe_inaccurate_nan, "Inaccurate NaN handling",
60 "This option improves speed by removing NaN checking. Please note this also reduces "
61 "accuracy of certain floating-point instructions.");
62 INSERT( 52 INSERT(
63 Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks", 53 Settings, cpuopt_unsafe_unfuse_fma,
64 "This option improves speed by eliminating a safety check before every memory read/write " 54 tr("Unfuse FMA (improve performance on CPUs without FMA)"),
65 "in guest. Disabling it may allow a game to read/write the emulator's memory."); 55 tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on "
66 INSERT(Settings, cpuopt_unsafe_ignore_global_monitor, "Ignore global monitor", 56 "CPUs without native FMA support."));
67 "This option improves speed by relying only on the semantics of cmpxchg to ensure " 57 INSERT(
58 Settings, cpuopt_unsafe_reduce_fp_error, tr("Faster FRSQRTE and FRECPE"),
59 tr("This option improves the speed of some approximate floating-point functions by using "
60 "less accurate native approximations."));
61 INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr,
62 tr("Faster ASIMD instructions (32 bits only)"),
63 tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
64 "with incorrect rounding modes."));
65 INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"),
66 tr("This option improves speed by removing NaN checking. Please note this also reduces "
67 "accuracy of certain floating-point instructions."));
68 INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"),
69 tr("This option improves speed by eliminating a safety check before every memory "
70 "read/write "
71 "in guest. Disabling it may allow a game to read/write the emulator's memory."));
72 INSERT(
73 Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"),
74 tr("This option improves speed by relying only on the semantics of cmpxchg to ensure "
68 "safety of exclusive access instructions. Please note this may result in deadlocks and " 75 "safety of exclusive access instructions. Please note this may result in deadlocks and "
69 "other race conditions."); 76 "other race conditions."));
70 77
71 // Renderer 78 // Renderer
72 INSERT(Settings, renderer_backend, "API:", ""); 79 INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral());
73 INSERT(Settings, vulkan_device, "Device:", ""); 80 INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral());
74 INSERT(Settings, shader_backend, "Shader Backend:", ""); 81 INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral());
75 INSERT(Settings, resolution_setup, "Resolution:", ""); 82 INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral());
76 INSERT(Settings, scaling_filter, "Window Adapting Filter:", ""); 83 INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral());
77 INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", ""); 84 INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral());
78 INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", ""); 85 INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral());
79 INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", ""); 86 INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral());
80 INSERT(Settings, aspect_ratio, "Aspect Ratio:", ""); 87 INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral());
81 INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", ""); 88 INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral());
82 INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", ""); 89 INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
83 INSERT(Settings, nvdec_emulation, "NVDEC emulation:", ""); 90 QStringLiteral());
84 INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", ""); 91 INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral());
85 INSERT(Settings, astc_recompression, "ASTC Recompression Method:", ""); 92 INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral());
86 INSERT(Settings, vsync_mode, "VSync Mode:", 93 INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral());
87 "FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " 94 INSERT(
95 Settings, vsync_mode, tr("VSync Mode:"),
96 tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
88 "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from " 97 "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from "
89 "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop " 98 "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop "
90 "frames.\nImmediate (no synchronization) just presents whatever is available and can " 99 "frames.\nImmediate (no synchronization) just presents whatever is available and can "
91 "exhibit tearing."); 100 "exhibit tearing."));
92 INSERT(Settings, bg_red, "", ""); 101 INSERT(Settings, bg_red, QStringLiteral(), QStringLiteral());
93 INSERT(Settings, bg_green, "", ""); 102 INSERT(Settings, bg_green, QStringLiteral(), QStringLiteral());
94 INSERT(Settings, bg_blue, "", ""); 103 INSERT(Settings, bg_blue, QStringLiteral(), QStringLiteral());
95 104
96 // Renderer (Advanced Graphics) 105 // Renderer (Advanced Graphics)
97 INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", ""); 106 INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
98 INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)", 107 QStringLiteral());
99 "Runs work in the background while waiting for graphics commands to keep the GPU from " 108 INSERT(
100 "lowering its clock speed."); 109 Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),
101 INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", ""); 110 tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
102 INSERT(Settings, gpu_accuracy, "Accuracy Level:", ""); 111 "lowering its clock speed."));
103 INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)", 112 INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral());
104 "Enables asynchronous shader compilation, which may reduce shader stutter. This feature " 113 INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral());
105 "is experimental."); 114 INSERT(
106 INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)", 115 Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"),
107 "Enables Fast GPU Time. This option will force most games to run at their highest " 116 tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature "
108 "native resolution."); 117 "is experimental."));
109 INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache", 118 INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"),
110 "Enables GPU vendor-specific pipeline cache. This option can improve shader loading " 119 tr("Enables Fast GPU Time. This option will force most games to run at their highest "
111 "time significantly in cases where the Vulkan driver does not store pipeline cache " 120 "native resolution."));
112 "files internally."); 121 INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"),
113 INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)", 122 tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading "
114 "Enable compute pipelines, required by some games.\nThis setting only exists for Intel " 123 "time significantly in cases where the Vulkan driver does not store pipeline cache "
124 "files internally."));
125 INSERT(
126 Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"),
127 tr("Enable compute pipelines, required by some games.\nThis setting only exists for Intel "
115 "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled " 128 "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled "
116 "on all other drivers."); 129 "on all other drivers."));
117 INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing", 130 INSERT(
118 "Uses reactive flushing instead of predictive flushing, allowing more accurate memory " 131 Settings, use_reactive_flushing, tr("Enable Reactive Flushing"),
119 "syncing."); 132 tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
120 INSERT(Settings, use_video_framerate, "Sync to framerate of video playback", 133 "syncing."));
121 "Run the game at normal speed during video playback, even when the framerate is " 134 INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"),
122 "unlocked."); 135 tr("Run the game at normal speed during video playback, even when the framerate is "
123 INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops", 136 "unlocked."));
124 "Improves rendering of transparency effects in specific games."); 137 INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"),
138 tr("Improves rendering of transparency effects in specific games."));
125 139
126 // Renderer (Debug) 140 // Renderer (Debug)
127 141
128 // System 142 // System
129 INSERT(Settings, rng_seed, "RNG Seed", ""); 143 INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral());
130 INSERT(Settings, rng_seed_enabled, "", ""); 144 INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral());
131 INSERT(Settings, device_name, "Device Name", ""); 145 INSERT(Settings, device_name, tr("Device Name"), QStringLiteral());
132 INSERT(Settings, custom_rtc, "Custom RTC", ""); 146 INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral());
133 INSERT(Settings, custom_rtc_enabled, "", ""); 147 INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());
134 INSERT(Settings, language_index, 148 INSERT(Settings, language_index, tr("Language:"),
135 "Language:", "Note: this can be overridden when region setting is auto-select"); 149 tr("Note: this can be overridden when region setting is auto-select"));
136 INSERT(Settings, region_index, "Region:", ""); 150 INSERT(Settings, region_index, tr("Region:"), QStringLiteral());
137 INSERT(Settings, time_zone_index, "Time Zone:", ""); 151 INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral());
138 INSERT(Settings, sound_index, "Sound Output Mode:", ""); 152 INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral());
139 INSERT(Settings, use_docked_mode, "Console Mode:", ""); 153 INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral());
140 INSERT(Settings, current_user, "", ""); 154 INSERT(Settings, current_user, QStringLiteral(), QStringLiteral());
141 155
142 // Controls 156 // Controls
143 157
@@ -154,11 +168,17 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
154 // Ui 168 // Ui
155 169
156 // Ui General 170 // Ui General
157 INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); 171 INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral());
158 INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); 172 INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"),
159 INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); 173 QStringLiteral());
160 INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); 174 INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"),
161 INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); 175 QStringLiteral());
176 INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral());
177 INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
178 QStringLiteral());
179
180 // Linux
181 INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral());
162 182
163 // Ui Debugging 183 // Ui Debugging
164 184
@@ -178,140 +198,146 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
178 return parent->tr(text, context); 198 return parent->tr(text, context);
179 }; 199 };
180 200
181#define PAIR(ENUM, VALUE, TRANSLATION) \ 201#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
182 { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION) }
183#define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \
184 { static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) }
185 202
186 // Intentionally skipping VSyncMode to let the UI fill that one out 203 // Intentionally skipping VSyncMode to let the UI fill that one out
187 204
188 translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), 205 translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
189 { 206 {
190 PAIR(AstcDecodeMode, Cpu, "CPU"), 207 PAIR(AstcDecodeMode, Cpu, tr("CPU")),
191 PAIR(AstcDecodeMode, Gpu, "GPU"), 208 PAIR(AstcDecodeMode, Gpu, tr("GPU")),
192 PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"), 209 PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")),
193 }});
194 translations->insert({Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
195 {
196 PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"),
197 PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"),
198 PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"),
199 }}); 210 }});
211 translations->insert(
212 {Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
213 {
214 PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")),
215 PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
216 PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
217 }});
200 translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), 218 translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
201 { 219 {
202#ifdef HAS_OPENGL 220#ifdef HAS_OPENGL
203 PAIR(RendererBackend, OpenGL, "OpenGL"), 221 PAIR(RendererBackend, OpenGL, tr("OpenGL")),
204#endif 222#endif
205 PAIR(RendererBackend, Vulkan, "Vulkan"), 223 PAIR(RendererBackend, Vulkan, tr("Vulkan")),
206 PAIR(RendererBackend, Null, "Null"), 224 PAIR(RendererBackend, Null, tr("Null")),
207 }});
208 translations->insert({Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
209 {
210 PAIR(ShaderBackend, Glsl, "GLSL"),
211 PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"),
212 PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"),
213 }}); 225 }});
226 translations->insert(
227 {Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
228 {
229 PAIR(ShaderBackend, Glsl, tr("GLSL")),
230 PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")),
231 PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, Mesa Only)")),
232 }});
214 translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), 233 translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
215 { 234 {
216 PAIR(GpuAccuracy, Normal, "Normal"), 235 PAIR(GpuAccuracy, Normal, tr("Normal")),
217 PAIR(GpuAccuracy, High, "High"), 236 PAIR(GpuAccuracy, High, tr("High")),
218 PAIR(GpuAccuracy, Extreme, "Extreme"), 237 PAIR(GpuAccuracy, Extreme, tr("Extreme")),
219 }}); 238 }});
220 translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(), 239 translations->insert(
240 {Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
241 {
242 PAIR(CpuAccuracy, Auto, tr("Auto")),
243 PAIR(CpuAccuracy, Accurate, tr("Accurate")),
244 PAIR(CpuAccuracy, Unsafe, tr("Unsafe")),
245 PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")),
246 }});
247 translations->insert({Settings::EnumMetadata<Settings::CpuBackend>::Index(),
221 { 248 {
222 PAIR(CpuAccuracy, Auto, "Auto"), 249 PAIR(CpuBackend, Dynarmic, tr("Dynarmic")),
223 PAIR(CpuAccuracy, Accurate, "Accurate"), 250 PAIR(CpuBackend, Nce, tr("NCE")),
224 PAIR(CpuAccuracy, Unsafe, "Unsafe"),
225 PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"),
226 }}); 251 }});
227 translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(), 252 translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(),
228 { 253 {
229 PAIR(FullscreenMode, Borderless, "Borderless Windowed"), 254 PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")),
230 PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"), 255 PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")),
231 }}); 256 }});
232 translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(), 257 translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(),
233 { 258 {
234 PAIR(NvdecEmulation, Off, "No Video Output"), 259 PAIR(NvdecEmulation, Off, tr("No Video Output")),
235 PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"), 260 PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")),
236 PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"), 261 PAIR(NvdecEmulation, Gpu, tr("GPU Video Decoding (Default)")),
237 }});
238 translations->insert({Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
239 {
240 PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"),
241 PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"),
242 PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"),
243 PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"),
244 PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"),
245 PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"),
246 PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"),
247 PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"),
248 PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"),
249 PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"),
250 PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"),
251 }}); 262 }});
263 translations->insert(
264 {Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
265 {
266 PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")),
267 PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")),
268 PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")),
269 PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")),
270 PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")),
271 PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")),
272 PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")),
273 PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")),
274 PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")),
275 PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")),
276 PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")),
277 }});
252 translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(), 278 translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(),
253 { 279 {
254 PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"), 280 PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")),
255 PAIR(ScalingFilter, Bilinear, "Bilinear"), 281 PAIR(ScalingFilter, Bilinear, tr("Bilinear")),
256 PAIR(ScalingFilter, Bicubic, "Bicubic"), 282 PAIR(ScalingFilter, Bicubic, tr("Bicubic")),
257 PAIR(ScalingFilter, Gaussian, "Gaussian"), 283 PAIR(ScalingFilter, Gaussian, tr("Gaussian")),
258 PAIR(ScalingFilter, ScaleForce, "ScaleForce"), 284 PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")),
259 PAIR(ScalingFilter, Fsr, "AMD FidelityFX™️ Super Resolution"), 285 PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")),
260 }}); 286 }});
261 translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(), 287 translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
262 { 288 {
263 PAIR(AntiAliasing, None, "None"), 289 PAIR(AntiAliasing, None, tr("None")),
264 PAIR(AntiAliasing, Fxaa, "FXAA"), 290 PAIR(AntiAliasing, Fxaa, tr("FXAA")),
265 PAIR(AntiAliasing, Smaa, "SMAA"), 291 PAIR(AntiAliasing, Smaa, tr("SMAA")),
266 }}); 292 }});
267 translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), 293 translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
268 { 294 {
269 PAIR(AspectRatio, R16_9, "Default (16:9)"), 295 PAIR(AspectRatio, R16_9, tr("Default (16:9)")),
270 PAIR(AspectRatio, R4_3, "Force 4:3"), 296 PAIR(AspectRatio, R4_3, tr("Force 4:3")),
271 PAIR(AspectRatio, R21_9, "Force 21:9"), 297 PAIR(AspectRatio, R21_9, tr("Force 21:9")),
272 PAIR(AspectRatio, R16_10, "Force 16:10"), 298 PAIR(AspectRatio, R16_10, tr("Force 16:10")),
273 PAIR(AspectRatio, Stretch, "Stretch to Window"), 299 PAIR(AspectRatio, Stretch, tr("Stretch to Window")),
274 }}); 300 }});
275 translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(), 301 translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(),
276 { 302 {
277 PAIR(AnisotropyMode, Automatic, "Automatic"), 303 PAIR(AnisotropyMode, Automatic, tr("Automatic")),
278 PAIR(AnisotropyMode, Default, "Default"), 304 PAIR(AnisotropyMode, Default, tr("Default")),
279 PAIR(AnisotropyMode, X2, "2x"), 305 PAIR(AnisotropyMode, X2, tr("2x")),
280 PAIR(AnisotropyMode, X4, "4x"), 306 PAIR(AnisotropyMode, X4, tr("4x")),
281 PAIR(AnisotropyMode, X8, "8x"), 307 PAIR(AnisotropyMode, X8, tr("8x")),
282 PAIR(AnisotropyMode, X16, "16x"), 308 PAIR(AnisotropyMode, X16, tr("16x")),
283 }}); 309 }});
284 translations->insert( 310 translations->insert(
285 {Settings::EnumMetadata<Settings::Language>::Index(), 311 {Settings::EnumMetadata<Settings::Language>::Index(),
286 { 312 {
287 PAIR(Language, Japanese, "Japanese (日本語)"), 313 PAIR(Language, Japanese, tr("Japanese (日本語)")),
288 PAIR(Language, EnglishAmerican, "American English"), 314 PAIR(Language, EnglishAmerican, tr("American English")),
289 PAIR(Language, French, "French (français)"), 315 PAIR(Language, French, tr("French (français)")),
290 PAIR(Language, German, "German (Deutsch)"), 316 PAIR(Language, German, tr("German (Deutsch)")),
291 PAIR(Language, Italian, "Italian (italiano)"), 317 PAIR(Language, Italian, tr("Italian (italiano)")),
292 PAIR(Language, Spanish, "Spanish (español)"), 318 PAIR(Language, Spanish, tr("Spanish (español)")),
293 PAIR(Language, Chinese, "Chinese"), 319 PAIR(Language, Chinese, tr("Chinese")),
294 PAIR(Language, Korean, "Korean (한국어)"), 320 PAIR(Language, Korean, tr("Korean (한국어)")),
295 PAIR(Language, Dutch, "Dutch (Nederlands)"), 321 PAIR(Language, Dutch, tr("Dutch (Nederlands)")),
296 PAIR(Language, Portuguese, "Portuguese (português)"), 322 PAIR(Language, Portuguese, tr("Portuguese (português)")),
297 PAIR(Language, Russian, "Russian (Русский)"), 323 PAIR(Language, Russian, tr("Russian (Русский)")),
298 PAIR(Language, Taiwanese, "Taiwanese"), 324 PAIR(Language, Taiwanese, tr("Taiwanese")),
299 PAIR(Language, EnglishBritish, "British English"), 325 PAIR(Language, EnglishBritish, tr("British English")),
300 PAIR(Language, FrenchCanadian, "Canadian French"), 326 PAIR(Language, FrenchCanadian, tr("Canadian French")),
301 PAIR(Language, SpanishLatin, "Latin American Spanish"), 327 PAIR(Language, SpanishLatin, tr("Latin American Spanish")),
302 PAIR(Language, ChineseSimplified, "Simplified Chinese"), 328 PAIR(Language, ChineseSimplified, tr("Simplified Chinese")),
303 PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"), 329 PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")),
304 PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"), 330 PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")),
305 }}); 331 }});
306 translations->insert({Settings::EnumMetadata<Settings::Region>::Index(), 332 translations->insert({Settings::EnumMetadata<Settings::Region>::Index(),
307 { 333 {
308 PAIR(Region, Japan, "Japan"), 334 PAIR(Region, Japan, tr("Japan")),
309 PAIR(Region, Usa, "USA"), 335 PAIR(Region, Usa, tr("USA")),
310 PAIR(Region, Europe, "Europe"), 336 PAIR(Region, Europe, tr("Europe")),
311 PAIR(Region, Australia, "Australia"), 337 PAIR(Region, Australia, tr("Australia")),
312 PAIR(Region, China, "China"), 338 PAIR(Region, China, tr("China")),
313 PAIR(Region, Korea, "Korea"), 339 PAIR(Region, Korea, tr("Korea")),
314 PAIR(Region, Taiwan, "Taiwan"), 340 PAIR(Region, Taiwan, tr("Taiwan")),
315 }}); 341 }});
316 translations->insert( 342 translations->insert(
317 {Settings::EnumMetadata<Settings::TimeZone>::Index(), 343 {Settings::EnumMetadata<Settings::TimeZone>::Index(),
@@ -323,72 +349,74 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
323 {static_cast<u32>(Settings::TimeZone::Default), 349 {static_cast<u32>(Settings::TimeZone::Default),
324 tr("Default (%1)", "Default time zone") 350 tr("Default (%1)", "Default time zone")
325 .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, 351 .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
326 PAIR(TimeZone, Cet, "CET"), 352 PAIR(TimeZone, Cet, tr("CET")),
327 PAIR(TimeZone, Cst6Cdt, "CST6CDT"), 353 PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")),
328 PAIR(TimeZone, Cuba, "Cuba"), 354 PAIR(TimeZone, Cuba, tr("Cuba")),
329 PAIR(TimeZone, Eet, "EET"), 355 PAIR(TimeZone, Eet, tr("EET")),
330 PAIR(TimeZone, Egypt, "Egypt"), 356 PAIR(TimeZone, Egypt, tr("Egypt")),
331 PAIR(TimeZone, Eire, "Eire"), 357 PAIR(TimeZone, Eire, tr("Eire")),
332 PAIR(TimeZone, Est, "EST"), 358 PAIR(TimeZone, Est, tr("EST")),
333 PAIR(TimeZone, Est5Edt, "EST5EDT"), 359 PAIR(TimeZone, Est5Edt, tr("EST5EDT")),
334 PAIR(TimeZone, Gb, "GB"), 360 PAIR(TimeZone, Gb, tr("GB")),
335 PAIR(TimeZone, GbEire, "GB-Eire"), 361 PAIR(TimeZone, GbEire, tr("GB-Eire")),
336 PAIR(TimeZone, Gmt, "GMT"), 362 PAIR(TimeZone, Gmt, tr("GMT")),
337 PAIR(TimeZone, GmtPlusZero, "GMT+0"), 363 PAIR(TimeZone, GmtPlusZero, tr("GMT+0")),
338 PAIR(TimeZone, GmtMinusZero, "GMT-0"), 364 PAIR(TimeZone, GmtMinusZero, tr("GMT-0")),
339 PAIR(TimeZone, GmtZero, "GMT0"), 365 PAIR(TimeZone, GmtZero, tr("GMT0")),
340 PAIR(TimeZone, Greenwich, "Greenwich"), 366 PAIR(TimeZone, Greenwich, tr("Greenwich")),
341 PAIR(TimeZone, Hongkong, "Hongkong"), 367 PAIR(TimeZone, Hongkong, tr("Hongkong")),
342 PAIR(TimeZone, Hst, "HST"), 368 PAIR(TimeZone, Hst, tr("HST")),
343 PAIR(TimeZone, Iceland, "Iceland"), 369 PAIR(TimeZone, Iceland, tr("Iceland")),
344 PAIR(TimeZone, Iran, "Iran"), 370 PAIR(TimeZone, Iran, tr("Iran")),
345 PAIR(TimeZone, Israel, "Israel"), 371 PAIR(TimeZone, Israel, tr("Israel")),
346 PAIR(TimeZone, Jamaica, "Jamaica"), 372 PAIR(TimeZone, Jamaica, tr("Jamaica")),
347 PAIR(TimeZone, Japan, "Japan"), 373 PAIR(TimeZone, Japan, tr("Japan")),
348 PAIR(TimeZone, Kwajalein, "Kwajalein"), 374 PAIR(TimeZone, Kwajalein, tr("Kwajalein")),
349 PAIR(TimeZone, Libya, "Libya"), 375 PAIR(TimeZone, Libya, tr("Libya")),
350 PAIR(TimeZone, Met, "MET"), 376 PAIR(TimeZone, Met, tr("MET")),
351 PAIR(TimeZone, Mst, "MST"), 377 PAIR(TimeZone, Mst, tr("MST")),
352 PAIR(TimeZone, Mst7Mdt, "MST7MDT"), 378 PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")),
353 PAIR(TimeZone, Navajo, "Navajo"), 379 PAIR(TimeZone, Navajo, tr("Navajo")),
354 PAIR(TimeZone, Nz, "NZ"), 380 PAIR(TimeZone, Nz, tr("NZ")),
355 PAIR(TimeZone, NzChat, "NZ-CHAT"), 381 PAIR(TimeZone, NzChat, tr("NZ-CHAT")),
356 PAIR(TimeZone, Poland, "Poland"), 382 PAIR(TimeZone, Poland, tr("Poland")),
357 PAIR(TimeZone, Portugal, "Portugal"), 383 PAIR(TimeZone, Portugal, tr("Portugal")),
358 PAIR(TimeZone, Prc, "PRC"), 384 PAIR(TimeZone, Prc, tr("PRC")),
359 PAIR(TimeZone, Pst8Pdt, "PST8PDT"), 385 PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")),
360 PAIR(TimeZone, Roc, "ROC"), 386 PAIR(TimeZone, Roc, tr("ROC")),
361 PAIR(TimeZone, Rok, "ROK"), 387 PAIR(TimeZone, Rok, tr("ROK")),
362 PAIR(TimeZone, Singapore, "Singapore"), 388 PAIR(TimeZone, Singapore, tr("Singapore")),
363 PAIR(TimeZone, Turkey, "Turkey"), 389 PAIR(TimeZone, Turkey, tr("Turkey")),
364 PAIR(TimeZone, Uct, "UCT"), 390 PAIR(TimeZone, Uct, tr("UCT")),
365 PAIR(TimeZone, Universal, "Universal"), 391 PAIR(TimeZone, Universal, tr("Universal")),
366 PAIR(TimeZone, Utc, "UTC"), 392 PAIR(TimeZone, Utc, tr("UTC")),
367 PAIR(TimeZone, WSu, "W-SU"), 393 PAIR(TimeZone, WSu, tr("W-SU")),
368 PAIR(TimeZone, Wet, "WET"), 394 PAIR(TimeZone, Wet, tr("WET")),
369 PAIR(TimeZone, Zulu, "Zulu"), 395 PAIR(TimeZone, Zulu, tr("Zulu")),
370 }}); 396 }});
371 translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), 397 translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
372 { 398 {
373 PAIR(AudioMode, Mono, "Mono"), 399 PAIR(AudioMode, Mono, tr("Mono")),
374 PAIR(AudioMode, Stereo, "Stereo"), 400 PAIR(AudioMode, Stereo, tr("Stereo")),
375 PAIR(AudioMode, Surround, "Surround"), 401 PAIR(AudioMode, Surround, tr("Surround")),
376 }}); 402 }});
377 translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(), 403 translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(),
378 { 404 {
379 PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"), 405 PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")),
380 PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), 406 PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")),
381 PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), 407 PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")),
408 }});
409 translations->insert({Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
410 {
411 PAIR(ConsoleMode, Docked, tr("Docked")),
412 PAIR(ConsoleMode, Handheld, tr("Handheld")),
382 }}); 413 }});
383 translations->insert(
384 {Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
385 {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
386 translations->insert( 414 translations->insert(
387 {Settings::EnumMetadata<Settings::ConfirmStop>::Index(), 415 {Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
388 { 416 {
389 PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"), 417 PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")),
390 PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"), 418 PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")),
391 PAIR(ConfirmStop, Ask_Never, "Never ask"), 419 PAIR(ConfirmStop, Ask_Never, tr("Never ask")),
392 }}); 420 }});
393 421
394#undef PAIR 422#undef PAIR
diff --git a/src/yuzu/configuration/shared_translation.h b/src/yuzu/configuration/shared_translation.h
index 99a0e808c..d5fc3b8de 100644
--- a/src/yuzu/configuration/shared_translation.h
+++ b/src/yuzu/configuration/shared_translation.h
@@ -10,6 +10,7 @@
10#include <vector> 10#include <vector>
11#include <QString> 11#include <QString>
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/settings.h"
13 14
14class QWidget; 15class QWidget;
15 16
@@ -22,4 +23,46 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent);
22 23
23std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent); 24std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent);
24 25
26static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = {
27 {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
28 {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
29 {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
30};
31
32static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = {
33 {Settings::ScalingFilter::NearestNeighbor,
34 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
35 {Settings::ScalingFilter::Bilinear,
36 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
37 {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
38 {Settings::ScalingFilter::Gaussian,
39 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
40 {Settings::ScalingFilter::ScaleForce,
41 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
42 {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
43};
44
45static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
46 {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
47 {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
48};
49
50static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = {
51 {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
52 {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
53 {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
54};
55
56static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = {
57 {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
58 {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
59 {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
60};
61
62static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = {
63 {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
64 {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
65 {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
66};
67
25} // namespace ConfigurationShared 68} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
index ea8d7add4..941683a43 100644
--- a/src/yuzu/configuration/shared_widget.cpp
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -194,7 +194,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
194 return group; 194 return group;
195 } 195 }
196 196
197 const auto get_selected = [=]() -> int { 197 const auto get_selected = [this]() -> int {
198 for (const auto& [id, button] : radio_buttons) { 198 for (const auto& [id, button] : radio_buttons) {
199 if (button->isChecked()) { 199 if (button->isChecked()) {
200 return id; 200 return id;
@@ -203,7 +203,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
203 return -1; 203 return -1;
204 }; 204 };
205 205
206 const auto set_index = [=](u32 value) { 206 const auto set_index = [this](u32 value) {
207 for (const auto& [id, button] : radio_buttons) { 207 for (const auto& [id, button] : radio_buttons) {
208 button->setChecked(id == value); 208 button->setChecked(id == value);
209 } 209 }
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 7049c57b6..6d227ef8d 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -36,10 +36,8 @@ constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{
36 36
37bool IsDarkTheme() { 37bool IsDarkTheme() {
38 const auto& theme = UISettings::values.theme; 38 const auto& theme = UISettings::values.theme;
39 return theme == QStringLiteral("qdarkstyle") || 39 return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
40 theme == QStringLiteral("qdarkstyle_midnight_blue") || 40 theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
41 theme == QStringLiteral("colorful_dark") ||
42 theme == QStringLiteral("colorful_midnight_blue");
43} 41}
44 42
45} // namespace 43} // namespace
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index f294dc23d..59b317135 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -278,7 +278,7 @@ void GameList::OnUpdateThemedIcons() {
278 case GameListItemType::CustomDir: { 278 case GameListItemType::CustomDir: {
279 const UISettings::GameDir& game_dir = 279 const UISettings::GameDir& game_dir =
280 UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()]; 280 UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()];
281 const QString icon_name = QFileInfo::exists(game_dir.path) 281 const QString icon_name = QFileInfo::exists(QString::fromStdString(game_dir.path))
282 ? QStringLiteral("folder") 282 ? QStringLiteral("folder")
283 : QStringLiteral("bad_folder"); 283 : QStringLiteral("bad_folder");
284 child->setData( 284 child->setData(
@@ -727,7 +727,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
727 }); 727 });
728 728
729 connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { 729 connect(open_directory_location, &QAction::triggered, [this, game_dir_index] {
730 emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path); 730 emit OpenDirectory(
731 QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path));
731 }); 732 });
732} 733}
733 734
@@ -869,7 +870,7 @@ const QStringList GameList::supported_file_extensions = {
869 QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; 870 QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
870 871
871void GameList::RefreshGameDirectory() { 872void GameList::RefreshGameDirectory() {
872 if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { 873 if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
873 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); 874 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
874 PopulateAsync(UISettings::values.game_dirs); 875 PopulateAsync(UISettings::values.game_dirs);
875 } 876 }
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 86a0c41d9..c330b574f 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -286,13 +286,13 @@ public:
286 setData(QObject::tr("System Titles"), Qt::DisplayRole); 286 setData(QObject::tr("System Titles"), Qt::DisplayRole);
287 break; 287 break;
288 case GameListItemType::CustomDir: { 288 case GameListItemType::CustomDir: {
289 const QString icon_name = QFileInfo::exists(game_dir->path) 289 const QString path = QString::fromStdString(game_dir->path);
290 ? QStringLiteral("folder") 290 const QString icon_name =
291 : QStringLiteral("bad_folder"); 291 QFileInfo::exists(path) ? QStringLiteral("folder") : QStringLiteral("bad_folder");
292 setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( 292 setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled(
293 icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), 293 icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
294 Qt::DecorationRole); 294 Qt::DecorationRole);
295 setData(game_dir->path, Qt::DisplayRole); 295 setData(path, Qt::DisplayRole);
296 break; 296 break;
297 } 297 }
298 default: 298 default:
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 69be21027..dc006832e 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -456,29 +456,29 @@ void GameListWorker::run() {
456 break; 456 break;
457 } 457 }
458 458
459 if (game_dir.path == QStringLiteral("SDMC")) { 459 if (game_dir.path == std::string("SDMC")) {
460 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); 460 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
461 DirEntryReady(game_list_dir); 461 DirEntryReady(game_list_dir);
462 AddTitlesToGameList(game_list_dir); 462 AddTitlesToGameList(game_list_dir);
463 } else if (game_dir.path == QStringLiteral("UserNAND")) { 463 } else if (game_dir.path == std::string("UserNAND")) {
464 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); 464 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
465 DirEntryReady(game_list_dir); 465 DirEntryReady(game_list_dir);
466 AddTitlesToGameList(game_list_dir); 466 AddTitlesToGameList(game_list_dir);
467 } else if (game_dir.path == QStringLiteral("SysNAND")) { 467 } else if (game_dir.path == std::string("SysNAND")) {
468 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); 468 auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
469 DirEntryReady(game_list_dir); 469 DirEntryReady(game_list_dir);
470 AddTitlesToGameList(game_list_dir); 470 AddTitlesToGameList(game_list_dir);
471 } else { 471 } else {
472 watch_list.append(game_dir.path); 472 watch_list.append(QString::fromStdString(game_dir.path));
473 auto* const game_list_dir = new GameListDir(game_dir); 473 auto* const game_list_dir = new GameListDir(game_dir);
474 DirEntryReady(game_list_dir); 474 DirEntryReady(game_list_dir);
475 ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 475 ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan,
476 game_dir.deep_scan, game_list_dir); 476 game_list_dir);
477 ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), 477 ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan,
478 game_dir.deep_scan, game_list_dir); 478 game_list_dir);
479 } 479 }
480 } 480 }
481 481
482 RecordEvent([=](GameList* game_list) { game_list->DonePopulating(watch_list); }); 482 RecordEvent([this](GameList* game_list) { game_list->DonePopulating(watch_list); });
483 processing_completed.Set(); 483 processing_completed.Set();
484} 484}
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 6530186c1..eebfbf155 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -19,7 +19,7 @@ void HotkeyRegistry::SaveHotkeys() {
19 for (const auto& hotkey : group.second) { 19 for (const auto& hotkey : group.second) {
20 UISettings::values.shortcuts.push_back( 20 UISettings::values.shortcuts.push_back(
21 {hotkey.first, group.first, 21 {hotkey.first, group.first,
22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString(), 22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString().toStdString(),
23 hotkey.second.controller_keyseq, 23 hotkey.second.controller_keyseq,
24 hotkey.second.context, hotkey.second.repeat})}); 24 hotkey.second.context, hotkey.second.repeat})});
25 } 25 }
@@ -31,12 +31,12 @@ void HotkeyRegistry::LoadHotkeys() {
31 // beginGroup() 31 // beginGroup()
32 for (auto shortcut : UISettings::values.shortcuts) { 32 for (auto shortcut : UISettings::values.shortcuts) {
33 Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; 33 Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
34 if (!shortcut.shortcut.keyseq.isEmpty()) { 34 if (!shortcut.shortcut.keyseq.empty()) {
35 hk.keyseq = 35 hk.keyseq = QKeySequence::fromString(QString::fromStdString(shortcut.shortcut.keyseq),
36 QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText); 36 QKeySequence::NativeText);
37 hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context); 37 hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.context);
38 } 38 }
39 if (!shortcut.shortcut.controller_keyseq.isEmpty()) { 39 if (!shortcut.shortcut.controller_keyseq.empty()) {
40 hk.controller_keyseq = shortcut.shortcut.controller_keyseq; 40 hk.controller_keyseq = shortcut.shortcut.controller_keyseq;
41 } 41 }
42 if (hk.shortcut) { 42 if (hk.shortcut) {
@@ -51,7 +51,8 @@ void HotkeyRegistry::LoadHotkeys() {
51 } 51 }
52} 52}
53 53
54QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { 54QShortcut* HotkeyRegistry::GetHotkey(const std::string& group, const std::string& action,
55 QWidget* widget) {
55 Hotkey& hk = hotkey_groups[group][action]; 56 Hotkey& hk = hotkey_groups[group][action];
56 57
57 if (!hk.shortcut) { 58 if (!hk.shortcut) {
@@ -62,7 +63,8 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
62 return hk.shortcut; 63 return hk.shortcut;
63} 64}
64 65
65ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, const QString& action, 66ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const std::string& group,
67 const std::string& action,
66 Core::HID::EmulatedController* controller) { 68 Core::HID::EmulatedController* controller) {
67 Hotkey& hk = hotkey_groups[group][action]; 69 Hotkey& hk = hotkey_groups[group][action];
68 70
@@ -74,12 +76,12 @@ ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, co
74 return hk.controller_shortcut; 76 return hk.controller_shortcut;
75} 77}
76 78
77QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { 79QKeySequence HotkeyRegistry::GetKeySequence(const std::string& group, const std::string& action) {
78 return hotkey_groups[group][action].keyseq; 80 return hotkey_groups[group][action].keyseq;
79} 81}
80 82
81Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group, 83Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const std::string& group,
82 const QString& action) { 84 const std::string& action) {
83 return hotkey_groups[group][action].context; 85 return hotkey_groups[group][action].context;
84} 86}
85 87
@@ -101,10 +103,10 @@ void ControllerShortcut::SetKey(const ControllerButtonSequence& buttons) {
101 button_sequence = buttons; 103 button_sequence = buttons;
102} 104}
103 105
104void ControllerShortcut::SetKey(const QString& buttons_shortcut) { 106void ControllerShortcut::SetKey(const std::string& buttons_shortcut) {
105 ControllerButtonSequence sequence{}; 107 ControllerButtonSequence sequence{};
106 name = buttons_shortcut.toStdString(); 108 name = buttons_shortcut;
107 std::istringstream command_line(buttons_shortcut.toStdString()); 109 std::istringstream command_line(buttons_shortcut);
108 std::string line; 110 std::string line;
109 while (std::getline(command_line, line, '+')) { 111 while (std::getline(command_line, line, '+')) {
110 if (line.empty()) { 112 if (line.empty()) {
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index 56eee8d82..e11332d2e 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -33,7 +33,7 @@ public:
33 ~ControllerShortcut(); 33 ~ControllerShortcut();
34 34
35 void SetKey(const ControllerButtonSequence& buttons); 35 void SetKey(const ControllerButtonSequence& buttons);
36 void SetKey(const QString& buttons_shortcut); 36 void SetKey(const std::string& buttons_shortcut);
37 37
38 ControllerButtonSequence ButtonSequence() const; 38 ControllerButtonSequence ButtonSequence() const;
39 39
@@ -88,8 +88,8 @@ public:
88 * will be the same. Thus, you shouldn't rely on the caller really being the 88 * will be the same. Thus, you shouldn't rely on the caller really being the
89 * QShortcut's parent. 89 * QShortcut's parent.
90 */ 90 */
91 QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); 91 QShortcut* GetHotkey(const std::string& group, const std::string& action, QWidget* widget);
92 ControllerShortcut* GetControllerHotkey(const QString& group, const QString& action, 92 ControllerShortcut* GetControllerHotkey(const std::string& group, const std::string& action,
93 Core::HID::EmulatedController* controller); 93 Core::HID::EmulatedController* controller);
94 94
95 /** 95 /**
@@ -98,7 +98,7 @@ public:
98 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). 98 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
99 * @param action Name of the action (e.g. "Start Emulation", "Load Image"). 99 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
100 */ 100 */
101 QKeySequence GetKeySequence(const QString& group, const QString& action); 101 QKeySequence GetKeySequence(const std::string& group, const std::string& action);
102 102
103 /** 103 /**
104 * Returns a Qt::ShortcutContext object who can be connected to other 104 * Returns a Qt::ShortcutContext object who can be connected to other
@@ -108,20 +108,20 @@ public:
108 * "Debugger"). 108 * "Debugger").
109 * @param action Name of the action (e.g. "Start Emulation", "Load Image"). 109 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
110 */ 110 */
111 Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action); 111 Qt::ShortcutContext GetShortcutContext(const std::string& group, const std::string& action);
112 112
113private: 113private:
114 struct Hotkey { 114 struct Hotkey {
115 QKeySequence keyseq; 115 QKeySequence keyseq;
116 QString controller_keyseq; 116 std::string controller_keyseq;
117 QShortcut* shortcut = nullptr; 117 QShortcut* shortcut = nullptr;
118 ControllerShortcut* controller_shortcut = nullptr; 118 ControllerShortcut* controller_shortcut = nullptr;
119 Qt::ShortcutContext context = Qt::WindowShortcut; 119 Qt::ShortcutContext context = Qt::WindowShortcut;
120 bool repeat; 120 bool repeat;
121 }; 121 };
122 122
123 using HotkeyMap = std::map<QString, Hotkey>; 123 using HotkeyMap = std::map<std::string, Hotkey>;
124 using HotkeyGroupMap = std::map<QString, HotkeyMap>; 124 using HotkeyGroupMap = std::map<std::string, HotkeyMap>;
125 125
126 HotkeyGroupMap hotkey_groups; 126 HotkeyGroupMap hotkey_groups;
127}; 127};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f6b548fd3..b056c3717 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -17,6 +17,7 @@
17#ifdef __unix__ 17#ifdef __unix__
18#include <csignal> 18#include <csignal>
19#include <sys/socket.h> 19#include <sys/socket.h>
20#include "common/linux/gamemode.h"
20#endif 21#endif
21 22
22#include <boost/container/flat_set.hpp> 23#include <boost/container/flat_set.hpp>
@@ -47,6 +48,7 @@
47#include "core/hle/service/am/applet_ae.h" 48#include "core/hle/service/am/applet_ae.h"
48#include "core/hle/service/am/applet_oe.h" 49#include "core/hle/service/am/applet_oe.h"
49#include "core/hle/service/am/applets/applets.h" 50#include "core/hle/service/am/applets/applets.h"
51#include "core/hle/service/set/set_sys.h"
50#include "yuzu/multiplayer/state.h" 52#include "yuzu/multiplayer/state.h"
51#include "yuzu/util/controller_navigation.h" 53#include "yuzu/util/controller_navigation.h"
52 54
@@ -128,6 +130,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
128#include "core/loader/loader.h" 130#include "core/loader/loader.h"
129#include "core/perf_stats.h" 131#include "core/perf_stats.h"
130#include "core/telemetry_session.h" 132#include "core/telemetry_session.h"
133#include "frontend_common/config.h"
131#include "input_common/drivers/tas_input.h" 134#include "input_common/drivers/tas_input.h"
132#include "input_common/drivers/virtual_amiibo.h" 135#include "input_common/drivers/virtual_amiibo.h"
133#include "input_common/main.h" 136#include "input_common/main.h"
@@ -140,9 +143,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
140#include "yuzu/bootmanager.h" 143#include "yuzu/bootmanager.h"
141#include "yuzu/compatdb.h" 144#include "yuzu/compatdb.h"
142#include "yuzu/compatibility_list.h" 145#include "yuzu/compatibility_list.h"
143#include "yuzu/configuration/config.h"
144#include "yuzu/configuration/configure_dialog.h" 146#include "yuzu/configuration/configure_dialog.h"
145#include "yuzu/configuration/configure_input_per_game.h" 147#include "yuzu/configuration/configure_input_per_game.h"
148#include "yuzu/configuration/qt_config.h"
146#include "yuzu/debugger/console.h" 149#include "yuzu/debugger/console.h"
147#include "yuzu/debugger/controller.h" 150#include "yuzu/debugger/controller.h"
148#include "yuzu/debugger/profiler.h" 151#include "yuzu/debugger/profiler.h"
@@ -185,7 +188,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
185#endif 188#endif
186 189
187constexpr int default_mouse_hide_timeout = 2500; 190constexpr int default_mouse_hide_timeout = 2500;
188constexpr int default_mouse_center_timeout = 10;
189constexpr int default_input_update_timeout = 1; 191constexpr int default_input_update_timeout = 1;
190 192
191constexpr size_t CopyBufferSize = 1_MiB; 193constexpr size_t CopyBufferSize = 1_MiB;
@@ -311,13 +313,14 @@ bool GMainWindow::CheckDarkMode() {
311#endif // __unix__ 313#endif // __unix__
312} 314}
313 315
314GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) 316GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan)
315 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, 317 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
316 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, 318 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
317 vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, 319 vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
318 provider{std::make_unique<FileSys::ManualContentProvider>()} { 320 provider{std::make_unique<FileSys::ManualContentProvider>()} {
319#ifdef __unix__ 321#ifdef __unix__
320 SetupSigInterrupts(); 322 SetupSigInterrupts();
323 SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
321#endif 324#endif
322 system->Initialize(); 325 system->Initialize();
323 326
@@ -435,9 +438,6 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
435 connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); 438 connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
436 connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); 439 connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
437 440
438 mouse_center_timer.setInterval(default_mouse_center_timeout);
439 connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor);
440
441 update_input_timer.setInterval(default_input_update_timeout); 441 update_input_timer.setInterval(default_input_update_timeout);
442 connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers); 442 connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers);
443 update_input_timer.start(); 443 update_input_timer.start();
@@ -676,7 +676,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
676 // Don't forget to apply settings. 676 // Don't forget to apply settings.
677 system->HIDCore().DisableAllControllerConfiguration(); 677 system->HIDCore().DisableAllControllerConfiguration();
678 system->ApplySettings(); 678 system->ApplySettings();
679 config->Save(); 679 config->SaveAllValues();
680 680
681 UpdateStatusButtons(); 681 UpdateStatusButtons();
682 682
@@ -1047,7 +1047,12 @@ void GMainWindow::InitializeWidgets() {
1047 statusBar()->addPermanentWidget(label); 1047 statusBar()->addPermanentWidget(label);
1048 } 1048 }
1049 1049
1050 // TODO (flTobi): Add the widget when multiplayer is fully implemented 1050 firmware_label = new QLabel();
1051 firmware_label->setObjectName(QStringLiteral("FirmwareLabel"));
1052 firmware_label->setVisible(false);
1053 firmware_label->setFocusPolicy(Qt::NoFocus);
1054 statusBar()->addPermanentWidget(firmware_label);
1055
1051 statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); 1056 statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
1052 statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); 1057 statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
1053 1058
@@ -1129,7 +1134,7 @@ void GMainWindow::InitializeWidgets() {
1129 connect(aa_status_button, &QPushButton::customContextMenuRequested, 1134 connect(aa_status_button, &QPushButton::customContextMenuRequested,
1130 [this](const QPoint& menu_location) { 1135 [this](const QPoint& menu_location) {
1131 QMenu context_menu; 1136 QMenu context_menu;
1132 for (auto const& aa_text_pair : Config::anti_aliasing_texts_map) { 1137 for (auto const& aa_text_pair : ConfigurationShared::anti_aliasing_texts_map) {
1133 context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] { 1138 context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] {
1134 Settings::values.anti_aliasing.SetValue(aa_text_pair.first); 1139 Settings::values.anti_aliasing.SetValue(aa_text_pair.first);
1135 UpdateAAText(); 1140 UpdateAAText();
@@ -1153,7 +1158,7 @@ void GMainWindow::InitializeWidgets() {
1153 connect(filter_status_button, &QPushButton::customContextMenuRequested, 1158 connect(filter_status_button, &QPushButton::customContextMenuRequested,
1154 [this](const QPoint& menu_location) { 1159 [this](const QPoint& menu_location) {
1155 QMenu context_menu; 1160 QMenu context_menu;
1156 for (auto const& filter_text_pair : Config::scaling_filter_texts_map) { 1161 for (auto const& filter_text_pair : ConfigurationShared::scaling_filter_texts_map) {
1157 context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] { 1162 context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] {
1158 Settings::values.scaling_filter.SetValue(filter_text_pair.first); 1163 Settings::values.scaling_filter.SetValue(filter_text_pair.first);
1159 UpdateFilterText(); 1164 UpdateFilterText();
@@ -1176,7 +1181,7 @@ void GMainWindow::InitializeWidgets() {
1176 [this](const QPoint& menu_location) { 1181 [this](const QPoint& menu_location) {
1177 QMenu context_menu; 1182 QMenu context_menu;
1178 1183
1179 for (auto const& pair : Config::use_docked_mode_texts_map) { 1184 for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) {
1180 context_menu.addAction(pair.second, [this, &pair] { 1185 context_menu.addAction(pair.second, [this, &pair] {
1181 if (pair.first != Settings::values.use_docked_mode.GetValue()) { 1186 if (pair.first != Settings::values.use_docked_mode.GetValue()) {
1182 OnToggleDockedMode(); 1187 OnToggleDockedMode();
@@ -1200,7 +1205,7 @@ void GMainWindow::InitializeWidgets() {
1200 [this](const QPoint& menu_location) { 1205 [this](const QPoint& menu_location) {
1201 QMenu context_menu; 1206 QMenu context_menu;
1202 1207
1203 for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { 1208 for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) {
1204 if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { 1209 if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
1205 continue; 1210 continue;
1206 } 1211 }
@@ -1229,7 +1234,8 @@ void GMainWindow::InitializeWidgets() {
1229 [this](const QPoint& menu_location) { 1234 [this](const QPoint& menu_location) {
1230 QMenu context_menu; 1235 QMenu context_menu;
1231 1236
1232 for (auto const& renderer_backend_pair : Config::renderer_backend_texts_map) { 1237 for (auto const& renderer_backend_pair :
1238 ConfigurationShared::renderer_backend_texts_map) {
1233 if (renderer_backend_pair.first == Settings::RendererBackend::Null) { 1239 if (renderer_backend_pair.first == Settings::RendererBackend::Null) {
1234 continue; 1240 continue;
1235 } 1241 }
@@ -1294,16 +1300,17 @@ void GMainWindow::InitializeRecentFileMenuActions() {
1294 1300
1295void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name, 1301void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name,
1296 const bool tas_allowed) { 1302 const bool tas_allowed) {
1297 static const QString main_window = QStringLiteral("Main Window"); 1303 static const auto main_window = std::string("Main Window");
1298 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); 1304 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name.toStdString()));
1299 action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); 1305 action->setShortcutContext(
1306 hotkey_registry.GetShortcutContext(main_window, action_name.toStdString()));
1300 action->setAutoRepeat(false); 1307 action->setAutoRepeat(false);
1301 1308
1302 this->addAction(action); 1309 this->addAction(action);
1303 1310
1304 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 1311 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
1305 const auto* controller_hotkey = 1312 const auto* controller_hotkey =
1306 hotkey_registry.GetControllerHotkey(main_window, action_name, controller); 1313 hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller);
1307 connect( 1314 connect(
1308 controller_hotkey, &ControllerShortcut::Activated, this, 1315 controller_hotkey, &ControllerShortcut::Activated, this,
1309 [action, tas_allowed, this] { 1316 [action, tas_allowed, this] {
@@ -1335,10 +1342,11 @@ void GMainWindow::InitializeHotkeys() {
1335 1342
1336 static const QString main_window = QStringLiteral("Main Window"); 1343 static const QString main_window = QStringLiteral("Main Window");
1337 const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { 1344 const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
1338 const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); 1345 const auto* hotkey =
1346 hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this);
1339 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 1347 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
1340 const auto* controller_hotkey = 1348 const auto* controller_hotkey = hotkey_registry.GetControllerHotkey(
1341 hotkey_registry.GetControllerHotkey(main_window, action_name, controller); 1349 main_window.toStdString(), action_name.toStdString(), controller);
1342 connect(hotkey, &QShortcut::activated, this, function); 1350 connect(hotkey, &QShortcut::activated, this, function);
1343 connect(controller_hotkey, &ControllerShortcut::Activated, this, function, 1351 connect(controller_hotkey, &ControllerShortcut::Activated, this, function,
1344 Qt::QueuedConnection); 1352 Qt::QueuedConnection);
@@ -1366,14 +1374,6 @@ void GMainWindow::InitializeHotkeys() {
1366 } 1374 }
1367 }); 1375 });
1368 connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] { 1376 connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
1369 if (Settings::values.mouse_enabled) {
1370 Settings::values.mouse_panning = false;
1371 QMessageBox::warning(
1372 this, tr("Emulated mouse is enabled"),
1373 tr("Real mouse input and mouse panning are incompatible. Please disable the "
1374 "emulated mouse in input advanced settings to allow mouse panning."));
1375 return;
1376 }
1377 Settings::values.mouse_panning = !Settings::values.mouse_panning; 1377 Settings::values.mouse_panning = !Settings::values.mouse_panning;
1378 if (Settings::values.mouse_panning) { 1378 if (Settings::values.mouse_panning) {
1379 render_window->installEventFilter(render_window); 1379 render_window->installEventFilter(render_window);
@@ -1575,6 +1575,7 @@ void GMainWindow::ConnectMenuEvents() {
1575 connect_menu(ui->action_Load_Cabinet_Formatter, 1575 connect_menu(ui->action_Load_Cabinet_Formatter,
1576 [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); 1576 [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); });
1577 connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); 1577 connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit);
1578 connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu);
1578 connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); 1579 connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot);
1579 1580
1580 // TAS 1581 // TAS
@@ -1602,14 +1603,13 @@ void GMainWindow::UpdateMenuState() {
1602 ui->action_Pause, 1603 ui->action_Pause,
1603 }; 1604 };
1604 1605
1605 const std::array applet_actions{ 1606 const std::array applet_actions{ui->action_Load_Album,
1606 ui->action_Load_Album, 1607 ui->action_Load_Cabinet_Nickname_Owner,
1607 ui->action_Load_Cabinet_Nickname_Owner, 1608 ui->action_Load_Cabinet_Eraser,
1608 ui->action_Load_Cabinet_Eraser, 1609 ui->action_Load_Cabinet_Restorer,
1609 ui->action_Load_Cabinet_Restorer, 1610 ui->action_Load_Cabinet_Formatter,
1610 ui->action_Load_Cabinet_Formatter, 1611 ui->action_Load_Mii_Edit,
1611 ui->action_Load_Mii_Edit, 1612 ui->action_Open_Controller_Menu};
1612 };
1613 1613
1614 for (QAction* action : running_actions) { 1614 for (QAction* action : running_actions) {
1615 action->setEnabled(emulation_running); 1615 action->setEnabled(emulation_running);
@@ -1918,7 +1918,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1918 // Save configurations 1918 // Save configurations
1919 UpdateUISettings(); 1919 UpdateUISettings();
1920 game_list->SaveInterfaceLayout(); 1920 game_list->SaveInterfaceLayout();
1921 config->Save(); 1921 config->SaveAllValues();
1922 1922
1923 u64 title_id{0}; 1923 u64 title_id{0};
1924 1924
@@ -1936,7 +1936,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1936 const auto config_file_name = title_id == 0 1936 const auto config_file_name = title_id == 0
1937 ? Common::FS::PathToUTF8String(file_path.filename()) 1937 ? Common::FS::PathToUTF8String(file_path.filename())
1938 : fmt::format("{:016X}", title_id); 1938 : fmt::format("{:016X}", title_id);
1939 Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); 1939 QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
1940 system->HIDCore().ReloadInputDevices(); 1940 system->HIDCore().ReloadInputDevices();
1941 system->ApplySettings(); 1941 system->ApplySettings();
1942 } 1942 }
@@ -2122,6 +2122,10 @@ void GMainWindow::OnEmulationStopped() {
2122 2122
2123 discord_rpc->Update(); 2123 discord_rpc->Update();
2124 2124
2125#ifdef __unix__
2126 Common::Linux::StopGamemode();
2127#endif
2128
2125 // The emulation is stopped, so closing the window or not does not matter anymore 2129 // The emulation is stopped, so closing the window or not does not matter anymore
2126 disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 2130 disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
2127 2131
@@ -2161,6 +2165,10 @@ void GMainWindow::OnEmulationStopped() {
2161 emu_frametime_label->setVisible(false); 2165 emu_frametime_label->setVisible(false);
2162 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); 2166 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
2163 2167
2168 if (!firmware_label->text().isEmpty()) {
2169 firmware_label->setVisible(true);
2170 }
2171
2164 current_game_path.clear(); 2172 current_game_path.clear();
2165 2173
2166 // When closing the game, destroy the GLWindow to clear the context after the game is closed 2174 // When closing the game, destroy the GLWindow to clear the context after the game is closed
@@ -2705,11 +2713,6 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
2705 } 2713 }
2706 2714
2707 const auto base_romfs = base_nca->GetRomFS(); 2715 const auto base_romfs = base_nca->GetRomFS();
2708 if (!base_romfs) {
2709 failed();
2710 return;
2711 }
2712
2713 const auto dump_dir = 2716 const auto dump_dir =
2714 target == DumpRomFSTarget::Normal 2717 target == DumpRomFSTarget::Normal
2715 ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) 2718 ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)
@@ -3135,7 +3138,7 @@ void GMainWindow::OnGameListAddDirectory() {
3135 return; 3138 return;
3136 } 3139 }
3137 3140
3138 UISettings::GameDir game_dir{dir_path, false, true}; 3141 UISettings::GameDir game_dir{dir_path.toStdString(), false, true};
3139 if (!UISettings::values.game_dirs.contains(game_dir)) { 3142 if (!UISettings::values.game_dirs.contains(game_dir)) {
3140 UISettings::values.game_dirs.append(game_dir); 3143 UISettings::values.game_dirs.append(game_dir);
3141 game_list->PopulateAsync(UISettings::values.game_dirs); 3144 game_list->PopulateAsync(UISettings::values.game_dirs);
@@ -3181,14 +3184,14 @@ void GMainWindow::OnMenuLoadFile() {
3181 "%1 is an identifier for the Switch executable file extensions.") 3184 "%1 is an identifier for the Switch executable file extensions.")
3182 .arg(extensions); 3185 .arg(extensions);
3183 const QString filename = QFileDialog::getOpenFileName( 3186 const QString filename = QFileDialog::getOpenFileName(
3184 this, tr("Load File"), UISettings::values.roms_path, file_filter); 3187 this, tr("Load File"), QString::fromStdString(UISettings::values.roms_path), file_filter);
3185 is_load_file_select_active = false; 3188 is_load_file_select_active = false;
3186 3189
3187 if (filename.isEmpty()) { 3190 if (filename.isEmpty()) {
3188 return; 3191 return;
3189 } 3192 }
3190 3193
3191 UISettings::values.roms_path = QFileInfo(filename).path(); 3194 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
3192 BootGame(filename); 3195 BootGame(filename);
3193} 3196}
3194 3197
@@ -3221,7 +3224,8 @@ void GMainWindow::OnMenuInstallToNAND() {
3221 "Image (*.xci)"); 3224 "Image (*.xci)");
3222 3225
3223 QStringList filenames = QFileDialog::getOpenFileNames( 3226 QStringList filenames = QFileDialog::getOpenFileNames(
3224 this, tr("Install Files"), UISettings::values.roms_path, file_filter); 3227 this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path),
3228 file_filter);
3225 3229
3226 if (filenames.isEmpty()) { 3230 if (filenames.isEmpty()) {
3227 return; 3231 return;
@@ -3239,7 +3243,7 @@ void GMainWindow::OnMenuInstallToNAND() {
3239 } 3243 }
3240 3244
3241 // Save folder location of the first selected file 3245 // Save folder location of the first selected file
3242 UISettings::values.roms_path = QFileInfo(filenames[0]).path(); 3246 UISettings::values.roms_path = QFileInfo(filenames[0]).path().toStdString();
3243 3247
3244 int remaining = filenames.size(); 3248 int remaining = filenames.size();
3245 3249
@@ -3499,6 +3503,10 @@ void GMainWindow::OnStartGame() {
3499 play_time_manager->Start(); 3503 play_time_manager->Start();
3500 3504
3501 discord_rpc->Update(); 3505 discord_rpc->Update();
3506
3507#ifdef __unix__
3508 Common::Linux::StartGamemode();
3509#endif
3502} 3510}
3503 3511
3504void GMainWindow::OnRestartGame() { 3512void GMainWindow::OnRestartGame() {
@@ -3519,6 +3527,10 @@ void GMainWindow::OnPauseGame() {
3519 play_time_manager->Stop(); 3527 play_time_manager->Stop();
3520 UpdateMenuState(); 3528 UpdateMenuState();
3521 AllowOSSleep(); 3529 AllowOSSleep();
3530
3531#ifdef __unix__
3532 Common::Linux::StopGamemode();
3533#endif
3522} 3534}
3523 3535
3524void GMainWindow::OnPauseContinueGame() { 3536void GMainWindow::OnPauseContinueGame() {
@@ -3584,7 +3596,7 @@ void GMainWindow::OnExit() {
3584 3596
3585void GMainWindow::OnSaveConfig() { 3597void GMainWindow::OnSaveConfig() {
3586 system->ApplySettings(); 3598 system->ApplySettings();
3587 config->Save(); 3599 config->SaveAllValues();
3588} 3600}
3589 3601
3590void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { 3602void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
@@ -3800,6 +3812,9 @@ void GMainWindow::OnConfigure() {
3800 const auto old_theme = UISettings::values.theme; 3812 const auto old_theme = UISettings::values.theme;
3801 const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); 3813 const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
3802 const auto old_language_index = Settings::values.language_index.GetValue(); 3814 const auto old_language_index = Settings::values.language_index.GetValue();
3815#ifdef __unix__
3816 const bool old_gamemode = Settings::values.enable_gamemode.GetValue();
3817#endif
3803 3818
3804 Settings::SetConfiguringGlobal(true); 3819 Settings::SetConfiguringGlobal(true);
3805 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), 3820 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(),
@@ -3840,7 +3855,7 @@ void GMainWindow::OnConfigure() {
3840 3855
3841 Settings::values.disabled_addons.clear(); 3856 Settings::values.disabled_addons.clear();
3842 3857
3843 config = std::make_unique<Config>(); 3858 config = std::make_unique<QtConfig>();
3844 UISettings::values.reset_to_defaults = false; 3859 UISettings::values.reset_to_defaults = false;
3845 3860
3846 UISettings::values.game_dirs = std::move(old_game_dirs); 3861 UISettings::values.game_dirs = std::move(old_game_dirs);
@@ -3861,6 +3876,11 @@ void GMainWindow::OnConfigure() {
3861 if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { 3876 if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {
3862 SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); 3877 SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
3863 } 3878 }
3879#ifdef __unix__
3880 if (Settings::values.enable_gamemode.GetValue() != old_gamemode) {
3881 SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
3882 }
3883#endif
3864 3884
3865 if (!multiplayer_state->IsHostingPublicRoom()) { 3885 if (!multiplayer_state->IsHostingPublicRoom()) {
3866 multiplayer_state->UpdateCredentials(); 3886 multiplayer_state->UpdateCredentials();
@@ -3875,7 +3895,7 @@ void GMainWindow::OnConfigure() {
3875 3895
3876 UISettings::values.configuration_applied = false; 3896 UISettings::values.configuration_applied = false;
3877 3897
3878 config->Save(); 3898 config->SaveAllValues();
3879 3899
3880 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { 3900 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
3881 render_window->installEventFilter(render_window); 3901 render_window->installEventFilter(render_window);
@@ -4091,7 +4111,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
4091 UISettings::values.configuration_applied = false; 4111 UISettings::values.configuration_applied = false;
4092 4112
4093 if (!is_powered_on) { 4113 if (!is_powered_on) {
4094 config->Save(); 4114 config->SaveAllValues();
4095 } 4115 }
4096} 4116}
4097 4117
@@ -4324,7 +4344,7 @@ void GMainWindow::OnAlbum() {
4324 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); 4344 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer);
4325 4345
4326 const auto filename = QString::fromStdString(album_nca->GetFullPath()); 4346 const auto filename = QString::fromStdString(album_nca->GetFullPath());
4327 UISettings::values.roms_path = QFileInfo(filename).path(); 4347 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4328 BootGame(filename, AlbumId); 4348 BootGame(filename, AlbumId);
4329} 4349}
4330 4350
@@ -4348,7 +4368,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
4348 system->GetAppletManager().SetCabinetMode(mode); 4368 system->GetAppletManager().SetCabinetMode(mode);
4349 4369
4350 const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); 4370 const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
4351 UISettings::values.roms_path = QFileInfo(filename).path(); 4371 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4352 BootGame(filename, CabinetId); 4372 BootGame(filename, CabinetId);
4353} 4373}
4354 4374
@@ -4371,10 +4391,35 @@ void GMainWindow::OnMiiEdit() {
4371 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); 4391 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit);
4372 4392
4373 const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); 4393 const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
4374 UISettings::values.roms_path = QFileInfo(filename).path(); 4394 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4375 BootGame(filename, MiiEditId); 4395 BootGame(filename, MiiEditId);
4376} 4396}
4377 4397
4398void GMainWindow::OnOpenControllerMenu() {
4399 constexpr u64 ControllerAppletId =
4400 static_cast<u64>(Service::AM::Applets::AppletProgramId::Controller);
4401 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4402 if (!bis_system) {
4403 QMessageBox::warning(this, tr("No firmware available"),
4404 tr("Please install the firmware to use the Controller Menu."));
4405 return;
4406 }
4407
4408 auto controller_applet_nca =
4409 bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program);
4410 if (!controller_applet_nca) {
4411 QMessageBox::warning(this, tr("Controller Applet"),
4412 tr("Controller Menu is not available. Please reinstall firmware."));
4413 return;
4414 }
4415
4416 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller);
4417
4418 const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath()));
4419 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4420 BootGame(filename, ControllerAppletId);
4421}
4422
4378void GMainWindow::OnCaptureScreenshot() { 4423void GMainWindow::OnCaptureScreenshot() {
4379 if (emu_thread == nullptr || !emu_thread->IsRunning()) { 4424 if (emu_thread == nullptr || !emu_thread->IsRunning()) {
4380 return; 4425 return;
@@ -4561,11 +4606,13 @@ void GMainWindow::UpdateStatusBar() {
4561 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); 4606 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());
4562 game_fps_label->setVisible(true); 4607 game_fps_label->setVisible(true);
4563 emu_frametime_label->setVisible(true); 4608 emu_frametime_label->setVisible(true);
4609 firmware_label->setVisible(false);
4564} 4610}
4565 4611
4566void GMainWindow::UpdateGPUAccuracyButton() { 4612void GMainWindow::UpdateGPUAccuracyButton() {
4567 const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); 4613 const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue();
4568 const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; 4614 const auto gpu_accuracy_text =
4615 ConfigurationShared::gpu_accuracy_texts_map.find(gpu_accuracy)->second;
4569 gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); 4616 gpu_accuracy_button->setText(gpu_accuracy_text.toUpper());
4570 gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); 4617 gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal);
4571} 4618}
@@ -4574,31 +4621,32 @@ void GMainWindow::UpdateDockedButton() {
4574 const auto console_mode = Settings::values.use_docked_mode.GetValue(); 4621 const auto console_mode = Settings::values.use_docked_mode.GetValue();
4575 dock_status_button->setChecked(Settings::IsDockedMode()); 4622 dock_status_button->setChecked(Settings::IsDockedMode());
4576 dock_status_button->setText( 4623 dock_status_button->setText(
4577 Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); 4624 ConfigurationShared::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
4578} 4625}
4579 4626
4580void GMainWindow::UpdateAPIText() { 4627void GMainWindow::UpdateAPIText() {
4581 const auto api = Settings::values.renderer_backend.GetValue(); 4628 const auto api = Settings::values.renderer_backend.GetValue();
4582 const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; 4629 const auto renderer_status_text =
4630 ConfigurationShared::renderer_backend_texts_map.find(api)->second;
4583 renderer_status_button->setText( 4631 renderer_status_button->setText(
4584 api == Settings::RendererBackend::OpenGL 4632 api == Settings::RendererBackend::OpenGL
4585 ? tr("%1 %2").arg( 4633 ? tr("%1 %2").arg(renderer_status_text.toUpper(),
4586 renderer_status_text.toUpper(), 4634 ConfigurationShared::shader_backend_texts_map
4587 Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue()) 4635 .find(Settings::values.shader_backend.GetValue())
4588 ->second) 4636 ->second)
4589 : renderer_status_text.toUpper()); 4637 : renderer_status_text.toUpper());
4590} 4638}
4591 4639
4592void GMainWindow::UpdateFilterText() { 4640void GMainWindow::UpdateFilterText() {
4593 const auto filter = Settings::values.scaling_filter.GetValue(); 4641 const auto filter = Settings::values.scaling_filter.GetValue();
4594 const auto filter_text = Config::scaling_filter_texts_map.find(filter)->second; 4642 const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second;
4595 filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") 4643 filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR")
4596 : filter_text.toUpper()); 4644 : filter_text.toUpper());
4597} 4645}
4598 4646
4599void GMainWindow::UpdateAAText() { 4647void GMainWindow::UpdateAAText() {
4600 const auto aa_mode = Settings::values.anti_aliasing.GetValue(); 4648 const auto aa_mode = Settings::values.anti_aliasing.GetValue();
4601 const auto aa_text = Config::anti_aliasing_texts_map.find(aa_mode)->second; 4649 const auto aa_text = ConfigurationShared::anti_aliasing_texts_map.find(aa_mode)->second;
4602 aa_status_button->setText(aa_mode == Settings::AntiAliasing::None 4650 aa_status_button->setText(aa_mode == Settings::AntiAliasing::None
4603 ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA")) 4651 ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA"))
4604 : aa_text.toUpper()); 4652 : aa_text.toUpper());
@@ -4668,26 +4716,10 @@ void GMainWindow::ShowMouseCursor() {
4668 } 4716 }
4669} 4717}
4670 4718
4671void GMainWindow::CenterMouseCursor() {
4672 if (emu_thread == nullptr || !Settings::values.mouse_panning) {
4673 mouse_center_timer.stop();
4674 return;
4675 }
4676 if (!this->isActiveWindow()) {
4677 mouse_center_timer.stop();
4678 return;
4679 }
4680 const int center_x = render_window->width() / 2;
4681 const int center_y = render_window->height() / 2;
4682
4683 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
4684}
4685
4686void GMainWindow::OnMouseActivity() { 4719void GMainWindow::OnMouseActivity() {
4687 if (!Settings::values.mouse_panning) { 4720 if (!Settings::values.mouse_panning) {
4688 ShowMouseCursor(); 4721 ShowMouseCursor();
4689 } 4722 }
4690 mouse_center_timer.stop();
4691} 4723}
4692 4724
4693void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { 4725void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
@@ -4778,6 +4810,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
4778 "games.")); 4810 "games."));
4779 } 4811 }
4780 4812
4813 SetFirmwareVersion();
4814
4781 if (behavior == ReinitializeKeyBehavior::Warning) { 4815 if (behavior == ReinitializeKeyBehavior::Warning) {
4782 game_list->PopulateAsync(UISettings::values.game_dirs); 4816 game_list->PopulateAsync(UISettings::values.game_dirs);
4783 } 4817 }
@@ -4805,7 +4839,7 @@ bool GMainWindow::CheckSystemArchiveDecryption() {
4805} 4839}
4806 4840
4807bool GMainWindow::CheckFirmwarePresence() { 4841bool GMainWindow::CheckFirmwarePresence() {
4808 constexpr u64 MiiEditId = 0x0100000000001009ull; 4842 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
4809 4843
4810 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4844 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4811 if (!bis_system) { 4845 if (!bis_system) {
@@ -4820,6 +4854,28 @@ bool GMainWindow::CheckFirmwarePresence() {
4820 return true; 4854 return true;
4821} 4855}
4822 4856
4857void GMainWindow::SetFirmwareVersion() {
4858 Service::Set::FirmwareVersionFormat firmware_data{};
4859 const auto result = Service::Set::GetFirmwareVersionImpl(
4860 firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2);
4861
4862 if (result.IsError() || !CheckFirmwarePresence()) {
4863 LOG_INFO(Frontend, "Installed firmware: No firmware available");
4864 firmware_label->setVisible(false);
4865 return;
4866 }
4867
4868 firmware_label->setVisible(true);
4869
4870 const std::string display_version(firmware_data.display_version.data());
4871 const std::string display_title(firmware_data.display_title.data());
4872
4873 LOG_INFO(Frontend, "Installed firmware: {}", display_title);
4874
4875 firmware_label->setText(QString::fromStdString(display_version));
4876 firmware_label->setToolTip(QString::fromStdString(display_title));
4877}
4878
4823bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, 4879bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
4824 u64* selected_title_id, u8* selected_content_record_type) { 4880 u64* selected_title_id, u8* selected_content_record_type) {
4825 using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; 4881 using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
@@ -4901,6 +4957,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
4901 4957
4902 UpdateUISettings(); 4958 UpdateUISettings();
4903 game_list->SaveInterfaceLayout(); 4959 game_list->SaveInterfaceLayout();
4960 UISettings::SaveWindowState();
4904 hotkey_registry.SaveHotkeys(); 4961 hotkey_registry.SaveHotkeys();
4905 4962
4906 // Unload controllers early 4963 // Unload controllers early
@@ -4963,22 +5020,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
4963 AcceptDropEvent(event); 5020 AcceptDropEvent(event);
4964} 5021}
4965 5022
4966void GMainWindow::leaveEvent(QEvent* event) {
4967 if (Settings::values.mouse_panning) {
4968 const QRect& rect = geometry();
4969 QPoint position = QCursor::pos();
4970
4971 qint32 x = qBound(rect.left(), position.x(), rect.right());
4972 qint32 y = qBound(rect.top(), position.y(), rect.bottom());
4973 // Only start the timer if the mouse has left the window bound.
4974 // The leave event is also triggered when the window looses focus.
4975 if (x != position.x() || y != position.y()) {
4976 mouse_center_timer.start();
4977 }
4978 event->accept();
4979 }
4980}
4981
4982bool GMainWindow::ConfirmChangeGame() { 5023bool GMainWindow::ConfirmChangeGame() {
4983 if (emu_thread == nullptr) 5024 if (emu_thread == nullptr)
4984 return true; 5025 return true;
@@ -5055,9 +5096,9 @@ static void AdjustLinkColor() {
5055} 5096}
5056 5097
5057void GMainWindow::UpdateUITheme() { 5098void GMainWindow::UpdateUITheme() {
5058 const QString default_theme = 5099 const QString default_theme = QString::fromUtf8(
5059 QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second); 5100 UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second);
5060 QString current_theme = UISettings::values.theme; 5101 QString current_theme = QString::fromStdString(UISettings::values.theme);
5061 5102
5062 if (current_theme.isEmpty()) { 5103 if (current_theme.isEmpty()) {
5063 current_theme = default_theme; 5104 current_theme = default_theme;
@@ -5085,7 +5126,7 @@ void GMainWindow::UpdateUITheme() {
5085 QFile f(theme_uri); 5126 QFile f(theme_uri);
5086 if (!f.open(QFile::ReadOnly | QFile::Text)) { 5127 if (!f.open(QFile::ReadOnly | QFile::Text)) {
5087 LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", 5128 LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
5088 UISettings::values.theme.toStdString()); 5129 UISettings::values.theme);
5089 current_theme = default_theme; 5130 current_theme = default_theme;
5090 } 5131 }
5091 } 5132 }
@@ -5098,7 +5139,7 @@ void GMainWindow::UpdateUITheme() {
5098 setStyleSheet(ts.readAll()); 5139 setStyleSheet(ts.readAll());
5099 } else { 5140 } else {
5100 LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", 5141 LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
5101 UISettings::values.theme.toStdString()); 5142 UISettings::values.theme);
5102 qApp->setStyleSheet({}); 5143 qApp->setStyleSheet({});
5103 setStyleSheet({}); 5144 setStyleSheet({});
5104 } 5145 }
@@ -5107,27 +5148,28 @@ void GMainWindow::UpdateUITheme() {
5107void GMainWindow::LoadTranslation() { 5148void GMainWindow::LoadTranslation() {
5108 bool loaded; 5149 bool loaded;
5109 5150
5110 if (UISettings::values.language.isEmpty()) { 5151 if (UISettings::values.language.empty()) {
5111 // If the selected language is empty, use system locale 5152 // If the selected language is empty, use system locale
5112 loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); 5153 loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
5113 } else { 5154 } else {
5114 // Otherwise load from the specified file 5155 // Otherwise load from the specified file
5115 loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/")); 5156 loaded = translator.load(QString::fromStdString(UISettings::values.language),
5157 QStringLiteral(":/languages/"));
5116 } 5158 }
5117 5159
5118 if (loaded) { 5160 if (loaded) {
5119 qApp->installTranslator(&translator); 5161 qApp->installTranslator(&translator);
5120 } else { 5162 } else {
5121 UISettings::values.language = QStringLiteral("en"); 5163 UISettings::values.language = std::string("en");
5122 } 5164 }
5123} 5165}
5124 5166
5125void GMainWindow::OnLanguageChanged(const QString& locale) { 5167void GMainWindow::OnLanguageChanged(const QString& locale) {
5126 if (UISettings::values.language != QStringLiteral("en")) { 5168 if (UISettings::values.language != std::string("en")) {
5127 qApp->removeTranslator(&translator); 5169 qApp->removeTranslator(&translator);
5128 } 5170 }
5129 5171
5130 UISettings::values.language = locale; 5172 UISettings::values.language = locale.toStdString();
5131 LoadTranslation(); 5173 LoadTranslation();
5132 ui->retranslateUi(this); 5174 ui->retranslateUi(this);
5133 multiplayer_state->retranslateUi(); 5175 multiplayer_state->retranslateUi();
@@ -5147,13 +5189,21 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
5147 discord_rpc->Update(); 5189 discord_rpc->Update();
5148} 5190}
5149 5191
5192#ifdef __unix__
5193void GMainWindow::SetGamemodeEnabled(bool state) {
5194 if (emulation_running) {
5195 Common::Linux::SetGamemodeState(state);
5196 }
5197}
5198#endif
5199
5150void GMainWindow::changeEvent(QEvent* event) { 5200void GMainWindow::changeEvent(QEvent* event) {
5151#ifdef __unix__ 5201#ifdef __unix__
5152 // PaletteChange event appears to only reach so far into the GUI, explicitly asking to 5202 // PaletteChange event appears to only reach so far into the GUI, explicitly asking to
5153 // UpdateUITheme is a decent work around 5203 // UpdateUITheme is a decent work around
5154 if (event->type() == QEvent::PaletteChange) { 5204 if (event->type() == QEvent::PaletteChange) {
5155 const QPalette test_palette(qApp->palette()); 5205 const QPalette test_palette(qApp->palette());
5156 const QString current_theme = UISettings::values.theme; 5206 const QString current_theme = QString::fromStdString(UISettings::values.theme);
5157 // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too 5207 // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
5158 static QColor last_window_color; 5208 static QColor last_window_color;
5159 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); 5209 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
@@ -5247,7 +5297,8 @@ static void SetHighDPIAttributes() {
5247} 5297}
5248 5298
5249int main(int argc, char* argv[]) { 5299int main(int argc, char* argv[]) {
5250 std::unique_ptr<Config> config = std::make_unique<Config>(); 5300 std::unique_ptr<QtConfig> config = std::make_unique<QtConfig>();
5301 UISettings::RestoreWindowState(config);
5251 bool has_broken_vulkan = false; 5302 bool has_broken_vulkan = false;
5252 bool is_child = false; 5303 bool is_child = false;
5253 if (CheckEnvVars(&is_child)) { 5304 if (CheckEnvVars(&is_child)) {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index f67c4cfda..530e445f9 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -15,6 +15,7 @@
15 15
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 "input_common/drivers/tas_input.h" 19#include "input_common/drivers/tas_input.h"
19#include "yuzu/compatibility_list.h" 20#include "yuzu/compatibility_list.h"
20#include "yuzu/hotkeys.h" 21#include "yuzu/hotkeys.h"
@@ -26,7 +27,7 @@
26#include <QtDBus/QtDBus> 27#include <QtDBus/QtDBus>
27#endif 28#endif
28 29
29class Config; 30class QtConfig;
30class ClickableLabel; 31class ClickableLabel;
31class EmuThread; 32class EmuThread;
32class GameList; 33class GameList;
@@ -185,7 +186,7 @@ class GMainWindow : public QMainWindow {
185public: 186public:
186 void filterBarSetChecked(bool state); 187 void filterBarSetChecked(bool state);
187 void UpdateUITheme(); 188 void UpdateUITheme();
188 explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan); 189 explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan);
189 ~GMainWindow() override; 190 ~GMainWindow() override;
190 191
191 bool DropAction(QDropEvent* event); 192 bool DropAction(QDropEvent* event);
@@ -339,6 +340,7 @@ private:
339 void SetupSigInterrupts(); 340 void SetupSigInterrupts();
340 static void HandleSigInterrupt(int); 341 static void HandleSigInterrupt(int);
341 void OnSigInterruptNotifierActivated(); 342 void OnSigInterruptNotifierActivated();
343 void SetGamemodeEnabled(bool state);
342#endif 344#endif
343 345
344private slots: 346private slots:
@@ -410,6 +412,7 @@ private slots:
410 void OnAlbum(); 412 void OnAlbum();
411 void OnCabinet(Service::NFP::CabinetMode mode); 413 void OnCabinet(Service::NFP::CabinetMode mode);
412 void OnMiiEdit(); 414 void OnMiiEdit();
415 void OnOpenControllerMenu();
413 void OnCaptureScreenshot(); 416 void OnCaptureScreenshot();
414 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 417 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
415 void OnLanguageChanged(const QString& locale); 418 void OnLanguageChanged(const QString& locale);
@@ -449,13 +452,13 @@ private:
449 void UpdateInputDrivers(); 452 void UpdateInputDrivers();
450 void HideMouseCursor(); 453 void HideMouseCursor();
451 void ShowMouseCursor(); 454 void ShowMouseCursor();
452 void CenterMouseCursor();
453 void OpenURL(const QUrl& url); 455 void OpenURL(const QUrl& url);
454 void LoadTranslation(); 456 void LoadTranslation();
455 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); 457 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
456 bool CheckDarkMode(); 458 bool CheckDarkMode();
457 bool CheckSystemArchiveDecryption(); 459 bool CheckSystemArchiveDecryption();
458 bool CheckFirmwarePresence(); 460 bool CheckFirmwarePresence();
461 void SetFirmwareVersion();
459 void ConfigureFilesystemProvider(const std::string& filepath); 462 void ConfigureFilesystemProvider(const std::string& filepath);
460 /** 463 /**
461 * Open (or not) the right confirm dialog based on current setting and game exit lock 464 * Open (or not) the right confirm dialog based on current setting and game exit lock
@@ -510,6 +513,7 @@ private:
510 QLabel* game_fps_label = nullptr; 513 QLabel* game_fps_label = nullptr;
511 QLabel* emu_frametime_label = nullptr; 514 QLabel* emu_frametime_label = nullptr;
512 QLabel* tas_label = nullptr; 515 QLabel* tas_label = nullptr;
516 QLabel* firmware_label = nullptr;
513 QPushButton* gpu_accuracy_button = nullptr; 517 QPushButton* gpu_accuracy_button = nullptr;
514 QPushButton* renderer_status_button = nullptr; 518 QPushButton* renderer_status_button = nullptr;
515 QPushButton* dock_status_button = nullptr; 519 QPushButton* dock_status_button = nullptr;
@@ -520,7 +524,7 @@ private:
520 QSlider* volume_slider = nullptr; 524 QSlider* volume_slider = nullptr;
521 QTimer status_bar_update_timer; 525 QTimer status_bar_update_timer;
522 526
523 std::unique_ptr<Config> config; 527 std::unique_ptr<QtConfig> config;
524 528
525 // Whether emulation is currently running in yuzu. 529 // Whether emulation is currently running in yuzu.
526 bool emulation_running = false; 530 bool emulation_running = false;
@@ -531,7 +535,6 @@ private:
531 bool auto_paused = false; 535 bool auto_paused = false;
532 bool auto_muted = false; 536 bool auto_muted = false;
533 QTimer mouse_hide_timer; 537 QTimer mouse_hide_timer;
534 QTimer mouse_center_timer;
535 QTimer update_input_timer; 538 QTimer update_input_timer;
536 539
537 QString startup_icon_theme; 540 QString startup_icon_theme;
@@ -588,5 +591,4 @@ protected:
588 void dropEvent(QDropEvent* event) override; 591 void dropEvent(QDropEvent* event) override;
589 void dragEnterEvent(QDragEnterEvent* event) override; 592 void dragEnterEvent(QDragEnterEvent* event) override;
590 void dragMoveEvent(QDragMoveEvent* event) override; 593 void dragMoveEvent(QDragMoveEvent* event) override;
591 void leaveEvent(QEvent* event) override;
592}; 594};
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 88684ffb5..e53f9951e 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -25,7 +25,7 @@
25 </property> 25 </property>
26 <widget class="QWidget" name="centralwidget"> 26 <widget class="QWidget" name="centralwidget">
27 <layout class="QHBoxLayout" name="horizontalLayout"> 27 <layout class="QHBoxLayout" name="horizontalLayout">
28 <property name="margin"> 28 <property name="margin" stdset="0">
29 <number>0</number> 29 <number>0</number>
30 </property> 30 </property>
31 </layout> 31 </layout>
@@ -36,7 +36,7 @@
36 <x>0</x> 36 <x>0</x>
37 <y>0</y> 37 <y>0</y>
38 <width>1280</width> 38 <width>1280</width>
39 <height>26</height> 39 <height>21</height>
40 </rect> 40 </rect>
41 </property> 41 </property>
42 <widget class="QMenu" name="menu_File"> 42 <widget class="QMenu" name="menu_File">
@@ -162,6 +162,7 @@
162 <addaction name="menu_cabinet_applet"/> 162 <addaction name="menu_cabinet_applet"/>
163 <addaction name="action_Load_Album"/> 163 <addaction name="action_Load_Album"/>
164 <addaction name="action_Load_Mii_Edit"/> 164 <addaction name="action_Load_Mii_Edit"/>
165 <addaction name="action_Open_Controller_Menu"/>
165 <addaction name="separator"/> 166 <addaction name="separator"/>
166 <addaction name="action_Capture_Screenshot"/> 167 <addaction name="action_Capture_Screenshot"/>
167 <addaction name="menuTAS"/> 168 <addaction name="menuTAS"/>
@@ -382,9 +383,9 @@
382 </property> 383 </property>
383 </action> 384 </action>
384 <action name="action_Load_Album"> 385 <action name="action_Load_Album">
385 <property name="text"> 386 <property name="text">
386 <string>Open &amp;Album</string> 387 <string>Open &amp;Album</string>
387 </property> 388 </property>
388 </action> 389 </action>
389 <action name="action_Load_Cabinet_Nickname_Owner"> 390 <action name="action_Load_Cabinet_Nickname_Owner">
390 <property name="text"> 391 <property name="text">
@@ -407,9 +408,9 @@
407 </property> 408 </property>
408 </action> 409 </action>
409 <action name="action_Load_Mii_Edit"> 410 <action name="action_Load_Mii_Edit">
410 <property name="text"> 411 <property name="text">
411 <string>Open &amp;Mii Editor</string> 412 <string>Open &amp;Mii Editor</string>
412 </property> 413 </property>
413 </action> 414 </action>
414 <action name="action_Configure_Tas"> 415 <action name="action_Configure_Tas">
415 <property name="text"> 416 <property name="text">
@@ -454,6 +455,11 @@
454 <string>R&amp;ecord</string> 455 <string>R&amp;ecord</string>
455 </property> 456 </property>
456 </action> 457 </action>
458 <action name="action_Open_Controller_Menu">
459 <property name="text">
460 <string>Open &amp;Controller Menu</string>
461 </property>
462 </action>
457 </widget> 463 </widget>
458 <resources> 464 <resources>
459 <include location="yuzu.qrc"/> 465 <include location="yuzu.qrc"/>
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 1c833767b..7bb7e95af 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -1,6 +1,9 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <QSettings>
5#include "common/fs/fs.h"
6#include "common/fs/path_util.h"
4#include "yuzu/uisettings.h" 7#include "yuzu/uisettings.h"
5 8
6#ifndef CANNOT_EXPLICITLY_INSTANTIATE 9#ifndef CANNOT_EXPLICITLY_INSTANTIATE
@@ -15,6 +18,8 @@ template class Setting<unsigned long long>;
15} // namespace Settings 18} // namespace Settings
16#endif 19#endif
17 20
21namespace FS = Common::FS;
22
18namespace UISettings { 23namespace UISettings {
19 24
20const Themes themes{{ 25const Themes themes{{
@@ -28,10 +33,8 @@ const Themes themes{{
28 33
29bool IsDarkTheme() { 34bool IsDarkTheme() {
30 const auto& theme = UISettings::values.theme; 35 const auto& theme = UISettings::values.theme;
31 return theme == QStringLiteral("qdarkstyle") || 36 return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
32 theme == QStringLiteral("qdarkstyle_midnight_blue") || 37 theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
33 theme == QStringLiteral("colorful_dark") ||
34 theme == QStringLiteral("colorful_midnight_blue");
35} 38}
36 39
37Values values = {}; 40Values values = {};
@@ -52,4 +55,58 @@ u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) {
52 return height * 16 / 9; 55 return height * 16 / 9;
53} 56}
54 57
58void SaveWindowState() {
59 const auto window_state_config_loc =
60 FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini");
61
62 void(FS::CreateParentDir(window_state_config_loc));
63 QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat);
64
65 config.setValue(QStringLiteral("geometry"), values.geometry);
66 config.setValue(QStringLiteral("state"), values.state);
67 config.setValue(QStringLiteral("geometryRenderWindow"), values.renderwindow_geometry);
68 config.setValue(QStringLiteral("gameListHeaderState"), values.gamelist_header_state);
69 config.setValue(QStringLiteral("microProfileDialogGeometry"), values.microprofile_geometry);
70
71 config.sync();
72}
73
74void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig) {
75 const auto window_state_config_loc =
76 FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini");
77
78 // Migrate window state from old location
79 if (!FS::Exists(window_state_config_loc) && qtConfig->Exists("UI", "UILayout\\geometry")) {
80 const auto config_loc =
81 FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "qt-config.ini");
82 QSettings config(QString::fromStdString(config_loc), QSettings::IniFormat);
83
84 config.beginGroup(QStringLiteral("UI"));
85 config.beginGroup(QStringLiteral("UILayout"));
86 values.geometry = config.value(QStringLiteral("geometry")).toByteArray();
87 values.state = config.value(QStringLiteral("state")).toByteArray();
88 values.renderwindow_geometry =
89 config.value(QStringLiteral("geometryRenderWindow")).toByteArray();
90 values.gamelist_header_state =
91 config.value(QStringLiteral("gameListHeaderState")).toByteArray();
92 values.microprofile_geometry =
93 config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray();
94 config.endGroup();
95 config.endGroup();
96 return;
97 }
98
99 void(FS::CreateParentDir(window_state_config_loc));
100 const QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat);
101
102 values.geometry = config.value(QStringLiteral("geometry")).toByteArray();
103 values.state = config.value(QStringLiteral("state")).toByteArray();
104 values.renderwindow_geometry =
105 config.value(QStringLiteral("geometryRenderWindow")).toByteArray();
106 values.gamelist_header_state =
107 config.value(QStringLiteral("gameListHeaderState")).toByteArray();
108 values.microprofile_geometry =
109 config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray();
110}
111
55} // namespace UISettings 112} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 3485a6347..549a39e1b 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -14,6 +14,7 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/settings.h" 15#include "common/settings.h"
16#include "common/settings_enums.h" 16#include "common/settings_enums.h"
17#include "configuration/qt_config.h"
17 18
18using Settings::Category; 19using Settings::Category;
19using Settings::ConfirmStop; 20using Settings::ConfirmStop;
@@ -37,15 +38,15 @@ namespace UISettings {
37bool IsDarkTheme(); 38bool IsDarkTheme();
38 39
39struct ContextualShortcut { 40struct ContextualShortcut {
40 QString keyseq; 41 std::string keyseq;
41 QString controller_keyseq; 42 std::string controller_keyseq;
42 int context; 43 int context;
43 bool repeat; 44 bool repeat;
44}; 45};
45 46
46struct Shortcut { 47struct Shortcut {
47 QString name; 48 std::string name;
48 QString group; 49 std::string group;
49 ContextualShortcut shortcut; 50 ContextualShortcut shortcut;
50}; 51};
51 52
@@ -58,11 +59,19 @@ enum class Theme {
58 MidnightBlueColorful, 59 MidnightBlueColorful,
59}; 60};
60 61
62static constexpr Theme default_theme{
63#ifdef _WIN32
64 Theme::DarkColorful
65#else
66 Theme::DefaultColorful
67#endif
68};
69
61using Themes = std::array<std::pair<const char*, const char*>, 6>; 70using Themes = std::array<std::pair<const char*, const char*>, 6>;
62extern const Themes themes; 71extern const Themes themes;
63 72
64struct GameDir { 73struct GameDir {
65 QString path; 74 std::string path;
66 bool deep_scan = false; 75 bool deep_scan = false;
67 bool expanded = false; 76 bool expanded = false;
68 bool operator==(const GameDir& rhs) const { 77 bool operator==(const GameDir& rhs) const {
@@ -144,15 +153,15 @@ struct Values {
144 Category::Screenshots}; 153 Category::Screenshots};
145 Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots}; 154 Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
146 155
147 QString roms_path; 156 std::string roms_path;
148 QString symbols_path; 157 std::string symbols_path;
149 QString game_dir_deprecated; 158 std::string game_dir_deprecated;
150 bool game_dir_deprecated_deepscan; 159 bool game_dir_deprecated_deepscan;
151 QVector<UISettings::GameDir> game_dirs; 160 QVector<GameDir> game_dirs;
152 QStringList recent_files; 161 QStringList recent_files;
153 QString language; 162 std::string language;
154 163
155 QString theme; 164 std::string theme;
156 165
157 // Shortcut name <Shortcut, context> 166 // Shortcut name <Shortcut, context>
158 std::vector<Shortcut> shortcuts; 167 std::vector<Shortcut> shortcuts;
@@ -206,6 +215,54 @@ extern Values values;
206 215
207u32 CalculateWidth(u32 height, Settings::AspectRatio ratio); 216u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);
208 217
218void SaveWindowState();
219void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);
220
221// This shouldn't have anything except static initializers (no functions). So
222// QKeySequence(...).toString() is NOT ALLOWED HERE.
223// This must be in alphabetical order according to action name as it must have the same order as
224// UISetting::values.shortcuts, which is alphabetically ordered.
225// clang-format off
226const std::array<Shortcut, 23> default_hotkeys{{
227 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
228 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
229 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
230 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"), std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
231 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"), std::string("Home+L"), Qt::ApplicationShortcut, false}},
232 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"), std::string("Home+X"), Qt::ApplicationShortcut, false}},
233 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"), std::string("Home+R"), Qt::ApplicationShortcut, false}},
234 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}},
235 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}},
236 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}},
237 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}},
238 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}},
239 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
240 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
241 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
242 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},
243 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}},
244 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}},
245 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}},
246 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}},
247 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}},
248 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}},
249 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}},
250}};
251// clang-format on
252
209} // namespace UISettings 253} // namespace UISettings
210 254
211Q_DECLARE_METATYPE(UISettings::GameDir*); 255Q_DECLARE_METATYPE(UISettings::GameDir*);
256
257// These metatype declarations cannot be in common/settings.h because core is devoid of QT
258Q_DECLARE_METATYPE(Settings::CpuAccuracy);
259Q_DECLARE_METATYPE(Settings::GpuAccuracy);
260Q_DECLARE_METATYPE(Settings::FullscreenMode);
261Q_DECLARE_METATYPE(Settings::NvdecEmulation);
262Q_DECLARE_METATYPE(Settings::ResolutionSetup);
263Q_DECLARE_METATYPE(Settings::ScalingFilter);
264Q_DECLARE_METATYPE(Settings::AntiAliasing);
265Q_DECLARE_METATYPE(Settings::RendererBackend);
266Q_DECLARE_METATYPE(Settings::ShaderBackend);
267Q_DECLARE_METATYPE(Settings::AstcRecompression);
268Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp
index 7b2a47496..e22cf84bf 100644
--- a/src/yuzu/util/util.cpp
+++ b/src/yuzu/util/util.cpp
@@ -4,7 +4,10 @@
4#include <array> 4#include <array>
5#include <cmath> 5#include <cmath>
6#include <QPainter> 6#include <QPainter>
7
8#include "common/logging/log.h"
7#include "yuzu/util/util.h" 9#include "yuzu/util/util.h"
10
8#ifdef _WIN32 11#ifdef _WIN32
9#include <windows.h> 12#include <windows.h>
10#include "common/fs/file.h" 13#include "common/fs/file.h"
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index 46eddf423..fbeba8813 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -13,9 +13,6 @@ function(create_resource file output filename)
13endfunction() 13endfunction()
14 14
15add_executable(yuzu-cmd 15add_executable(yuzu-cmd
16 config.cpp
17 config.h
18 default_ini.h
19 emu_window/emu_window_sdl2.cpp 16 emu_window/emu_window_sdl2.cpp
20 emu_window/emu_window_sdl2.h 17 emu_window/emu_window_sdl2.h
21 emu_window/emu_window_sdl2_gl.cpp 18 emu_window/emu_window_sdl2_gl.cpp
@@ -25,14 +22,16 @@ add_executable(yuzu-cmd
25 emu_window/emu_window_sdl2_vk.cpp 22 emu_window/emu_window_sdl2_vk.cpp
26 emu_window/emu_window_sdl2_vk.h 23 emu_window/emu_window_sdl2_vk.h
27 precompiled_headers.h 24 precompiled_headers.h
25 sdl_config.cpp
26 sdl_config.h
28 yuzu.cpp 27 yuzu.cpp
29 yuzu.rc 28 yuzu.rc
30) 29)
31 30
32create_target_directory_groups(yuzu-cmd) 31create_target_directory_groups(yuzu-cmd)
33 32
34target_link_libraries(yuzu-cmd PRIVATE common core input_common) 33target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common)
35target_link_libraries(yuzu-cmd PRIVATE inih::INIReader glad) 34target_link_libraries(yuzu-cmd PRIVATE glad)
36if (MSVC) 35if (MSVC)
37 target_link_libraries(yuzu-cmd PRIVATE getopt) 36 target_link_libraries(yuzu-cmd PRIVATE getopt)
38endif() 37endif()
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
deleted file mode 100644
index 0d25ff400..000000000
--- a/src/yuzu_cmd/config.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <memory>
5#include <optional>
6#include <sstream>
7#include <INIReader.h>
8#include <SDL.h>
9#include "common/fs/file.h"
10#include "common/fs/fs.h"
11#include "common/fs/path_util.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "core/hle/service/acc/profile_manager.h"
15#include "input_common/main.h"
16#include "yuzu_cmd/config.h"
17#include "yuzu_cmd/default_ini.h"
18
19namespace FS = Common::FS;
20
21const std::filesystem::path default_config_path =
22 FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
23
24Config::Config(std::optional<std::filesystem::path> config_path)
25 : sdl2_config_loc{config_path.value_or(default_config_path)},
26 sdl2_config{std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc))} {
27 Reload();
28}
29
30Config::~Config() = default;
31
32bool Config::LoadINI(const std::string& default_contents, bool retry) {
33 const auto config_loc_str = FS::PathToUTF8String(sdl2_config_loc);
34 if (sdl2_config->ParseError() < 0) {
35 if (retry) {
36 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
37 config_loc_str);
38
39 void(FS::CreateParentDir(sdl2_config_loc));
40 void(FS::WriteStringToFile(sdl2_config_loc, FS::FileType::TextFile, default_contents));
41
42 sdl2_config = std::make_unique<INIReader>(config_loc_str);
43
44 return LoadINI(default_contents, false);
45 }
46 LOG_ERROR(Config, "Failed.");
47 return false;
48 }
49 LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
50 return true;
51}
52
53static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = {
54 SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
55 SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
56 SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
57};
58
59static const std::array<int, Settings::NativeMotion::NumMotions> default_motions = {
60 SDL_SCANCODE_7,
61 SDL_SCANCODE_8,
62};
63
64static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{
65 {
66 SDL_SCANCODE_UP,
67 SDL_SCANCODE_DOWN,
68 SDL_SCANCODE_LEFT,
69 SDL_SCANCODE_RIGHT,
70 SDL_SCANCODE_D,
71 },
72 {
73 SDL_SCANCODE_I,
74 SDL_SCANCODE_K,
75 SDL_SCANCODE_J,
76 SDL_SCANCODE_L,
77 SDL_SCANCODE_D,
78 },
79}};
80
81template <>
82void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
83 std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault());
84 if (setting_value.empty()) {
85 setting_value = setting.GetDefault();
86 }
87 setting = std::move(setting_value);
88}
89
90template <>
91void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
92 setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
93}
94
95template <typename Type, bool ranged>
96void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
97 setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(),
98 static_cast<long>(setting.GetDefault())));
99}
100
101void Config::ReadCategory(Settings::Category category) {
102 for (const auto setting : Settings::values.linkage.by_category[category]) {
103 const char* category_name = [&]() {
104 if (category == Settings::Category::Controls) {
105 // For compatibility with older configs
106 return "ControlsGeneral";
107 } else {
108 return Settings::TranslateCategory(category);
109 }
110 }();
111 std::string setting_value =
112 sdl2_config->Get(category_name, setting->GetLabel(), setting->DefaultToString());
113 setting->LoadString(setting_value);
114 }
115}
116
117void Config::ReadValues() {
118 // Controls
119 ReadCategory(Settings::Category::Controls);
120
121 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
122 auto& player = Settings::values.players.GetValue()[p];
123
124 const auto group = fmt::format("ControlsP{}", p);
125 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
126 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
127 player.buttons[i] =
128 sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
129 if (player.buttons[i].empty()) {
130 player.buttons[i] = default_param;
131 }
132 }
133
134 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
135 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
136 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
137 default_analogs[i][3], default_analogs[i][4], 0.5f);
138 player.analogs[i] =
139 sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
140 if (player.analogs[i].empty()) {
141 player.analogs[i] = default_param;
142 }
143 }
144
145 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
146 const std::string default_param =
147 InputCommon::GenerateKeyboardParam(default_motions[i]);
148 auto& player_motions = player.motions[i];
149
150 player_motions =
151 sdl2_config->Get(group, Settings::NativeMotion::mapping[i], default_param);
152 if (player_motions.empty()) {
153 player_motions = default_param;
154 }
155 }
156
157 player.connected = sdl2_config->GetBoolean(group, "connected", false);
158 }
159
160 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
161 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
162 Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
163 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i],
164 default_param);
165 if (Settings::values.debug_pad_buttons[i].empty())
166 Settings::values.debug_pad_buttons[i] = default_param;
167 }
168
169 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
170 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
171 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
172 default_analogs[i][3], default_analogs[i][4], 0.5f);
173 Settings::values.debug_pad_analogs[i] = sdl2_config->Get(
174 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i],
175 default_param);
176 if (Settings::values.debug_pad_analogs[i].empty())
177 Settings::values.debug_pad_analogs[i] = default_param;
178 }
179
180 Settings::values.touchscreen.enabled =
181 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
182 Settings::values.touchscreen.rotation_angle =
183 sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0);
184 Settings::values.touchscreen.diameter_x =
185 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
186 Settings::values.touchscreen.diameter_y =
187 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
188
189 int num_touch_from_button_maps =
190 sdl2_config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
191 if (num_touch_from_button_maps > 0) {
192 for (int i = 0; i < num_touch_from_button_maps; ++i) {
193 Settings::TouchFromButtonMap map;
194 map.name = sdl2_config->Get("ControlsGeneral",
195 std::string("touch_from_button_maps_") + std::to_string(i) +
196 std::string("_name"),
197 "default");
198 const int num_touch_maps = sdl2_config->GetInteger(
199 "ControlsGeneral",
200 std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
201 0);
202 map.buttons.reserve(num_touch_maps);
203
204 for (int j = 0; j < num_touch_maps; ++j) {
205 std::string touch_mapping =
206 sdl2_config->Get("ControlsGeneral",
207 std::string("touch_from_button_maps_") + std::to_string(i) +
208 std::string("_bind_") + std::to_string(j),
209 "");
210 map.buttons.emplace_back(std::move(touch_mapping));
211 }
212
213 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
214 }
215 } else {
216 Settings::values.touch_from_button_maps.emplace_back(
217 Settings::TouchFromButtonMap{"default", {}});
218 num_touch_from_button_maps = 1;
219 }
220 Settings::values.touch_from_button_map_index = std::clamp(
221 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
222
223 ReadCategory(Settings::Category::Audio);
224 ReadCategory(Settings::Category::Core);
225 ReadCategory(Settings::Category::Cpu);
226 ReadCategory(Settings::Category::CpuDebug);
227 ReadCategory(Settings::Category::CpuUnsafe);
228 ReadCategory(Settings::Category::Renderer);
229 ReadCategory(Settings::Category::RendererAdvanced);
230 ReadCategory(Settings::Category::RendererDebug);
231 ReadCategory(Settings::Category::System);
232 ReadCategory(Settings::Category::SystemAudio);
233 ReadCategory(Settings::Category::DataStorage);
234 ReadCategory(Settings::Category::Debugging);
235 ReadCategory(Settings::Category::DebuggingGraphics);
236 ReadCategory(Settings::Category::Miscellaneous);
237 ReadCategory(Settings::Category::Network);
238 ReadCategory(Settings::Category::WebService);
239
240 // Data Storage
241 FS::SetYuzuPath(FS::YuzuPath::NANDDir,
242 sdl2_config->Get("Data Storage", "nand_directory",
243 FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
244 FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
245 sdl2_config->Get("Data Storage", "sdmc_directory",
246 FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
247 FS::SetYuzuPath(FS::YuzuPath::LoadDir,
248 sdl2_config->Get("Data Storage", "load_directory",
249 FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
250 FS::SetYuzuPath(FS::YuzuPath::DumpDir,
251 sdl2_config->Get("Data Storage", "dump_directory",
252 FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
253
254 // Debugging
255 Settings::values.record_frame_times =
256 sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
257
258 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
259 std::stringstream ss(title_list);
260 std::string line;
261 while (std::getline(ss, line, '|')) {
262 const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
263 const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, "");
264
265 std::stringstream inner_ss(disabled_list);
266 std::string inner_line;
267 std::vector<std::string> out;
268 while (std::getline(inner_ss, inner_line, '|')) {
269 out.push_back(inner_line);
270 }
271
272 Settings::values.disabled_addons.insert_or_assign(title_id, out);
273 }
274}
275
276void Config::Reload() {
277 LoadINI(DefaultINI::sdl2_config_file);
278 ReadValues();
279}
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h
deleted file mode 100644
index 512591a39..000000000
--- a/src/yuzu_cmd/config.h
+++ /dev/null
@@ -1,38 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <filesystem>
7#include <memory>
8#include <optional>
9#include <string>
10
11#include "common/settings.h"
12
13class INIReader;
14
15class Config {
16 std::filesystem::path sdl2_config_loc;
17 std::unique_ptr<INIReader> sdl2_config;
18
19 bool LoadINI(const std::string& default_contents = "", bool retry = true);
20 void ReadValues();
21
22public:
23 explicit Config(std::optional<std::filesystem::path> config_path);
24 ~Config();
25
26 void Reload();
27
28private:
29 /**
30 * Applies a value read from the sdl2_config to a Setting.
31 *
32 * @param group The name of the INI group
33 * @param setting The yuzu setting to modify
34 */
35 template <typename Type, bool ranged>
36 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
37 void ReadCategory(Settings::Category category);
38};
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
deleted file mode 100644
index 119e22183..000000000
--- a/src/yuzu_cmd/default_ini.h
+++ /dev/null
@@ -1,553 +0,0 @@
1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace DefaultINI {
7
8const char* sdl2_config_file =
9 R"(
10[ControlsP0]
11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
13# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
15
16# Indicates if this player should be connected at boot
17# 0 (default): Disabled, 1: Enabled
18connected=
19
20# for button input, the following devices are available:
21# - "keyboard" (default) for keyboard input. Required parameters:
22# - "code": the code of the key to bind
23# - "sdl" for joystick input using SDL. Required parameters:
24# - "guid": SDL identification GUID of the joystick
25# - "port": the index of the joystick to bind
26# - "button"(optional): the index of the button to bind
27# - "hat"(optional): the index of the hat to bind as direction buttons
28# - "axis"(optional): the index of the axis to bind
29# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
30# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
31# triggered if the axis value crosses
32# - "direction"(only used for axis): "+" means the button is triggered when the axis value
33# is greater than the threshold; "-" means the button is triggered when the axis value
34# is smaller than the threshold
35button_a=
36button_b=
37button_x=
38button_y=
39button_lstick=
40button_rstick=
41button_l=
42button_r=
43button_zl=
44button_zr=
45button_plus=
46button_minus=
47button_dleft=
48button_dup=
49button_dright=
50button_ddown=
51button_lstick_left=
52button_lstick_up=
53button_lstick_right=
54button_lstick_down=
55button_sl=
56button_sr=
57button_home=
58button_screenshot=
59
60# for analog input, the following devices are available:
61# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
62# - "up", "down", "left", "right": sub-devices for each direction.
63# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
64# - "modifier": sub-devices as a modifier.
65# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
66# Must be in range of 0.0-1.0. Defaults to 0.5
67# - "sdl" for joystick input using SDL. Required parameters:
68# - "guid": SDL identification GUID of the joystick
69# - "port": the index of the joystick to bind
70# - "axis_x": the index of the axis to bind as x-axis (default to 0)
71# - "axis_y": the index of the axis to bind as y-axis (default to 1)
72lstick=
73rstick=
74
75# for motion input, the following devices are available:
76# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
77# - "code": the code of the key to bind
78# - "sdl" for motion input using SDL. Required parameters:
79# - "guid": SDL identification GUID of the joystick
80# - "port": the index of the joystick to bind
81# - "motion": the index of the motion sensor to bind
82# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
83# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
84# - "port": the port of the cemu hook server
85# - "pad": the index of the joystick
86# - "motion": the index of the motion sensor of the joystick to bind
87motionleft=
88motionright=
89
90[ControlsGeneral]
91# To use the debug_pad, prepend `debug_pad_` before each button setting above.
92# i.e. debug_pad_button_a=
93
94# Enable debug pad inputs to the guest
95# 0 (default): Disabled, 1: Enabled
96debug_pad_enabled =
97
98# Enable sdl raw input. Allows to configure up to 8 xinput controllers.
99# 0 (default): Disabled, 1: Enabled
100enable_raw_input =
101
102# Enable yuzu joycon driver instead of SDL drive.
103# 0: Disabled, 1 (default): Enabled
104enable_joycon_driver =
105
106# Emulates an analog input from buttons. Allowing to dial any angle.
107# 0 (default): Disabled, 1: Enabled
108emulate_analog_keyboard =
109
110# Whether to enable or disable vibration
111# 0: Disabled, 1 (default): Enabled
112vibration_enabled=
113
114# Whether to enable or disable accurate vibrations
115# 0 (default): Disabled, 1: Enabled
116enable_accurate_vibrations=
117
118# Enables controller motion inputs
119# 0: Disabled, 1 (default): Enabled
120motion_enabled =
121
122# Defines the udp device's touch screen coordinate system for cemuhookudp devices
123# - "min_x", "min_y", "max_x", "max_y"
124touch_device=
125
126# for mapping buttons to touch inputs.
127#touch_from_button_map=1
128#touch_from_button_maps_0_name=default
129#touch_from_button_maps_0_count=2
130#touch_from_button_maps_0_bind_0=foo
131#touch_from_button_maps_0_bind_1=bar
132# etc.
133
134# List of Cemuhook UDP servers, delimited by ','.
135# Default: 127.0.0.1:26760
136# Example: 127.0.0.1:26760,123.4.5.67:26761
137udp_input_servers =
138
139# Enable controlling an axis via a mouse input.
140# 0 (default): Off, 1: On
141mouse_panning =
142
143# Set mouse panning horizontal sensitivity.
144# Default: 50.0
145mouse_panning_x_sensitivity =
146
147# Set mouse panning vertical sensitivity.
148# Default: 50.0
149mouse_panning_y_sensitivity =
150
151# Set mouse panning deadzone horizontal counterweight.
152# Default: 0.0
153mouse_panning_deadzone_x_counterweight =
154
155# Set mouse panning deadzone vertical counterweight.
156# Default: 0.0
157mouse_panning_deadzone_y_counterweight =
158
159# Set mouse panning stick decay strength.
160# Default: 22.0
161mouse_panning_decay_strength =
162
163# Set mouse panning stick minimum decay.
164# Default: 5.0
165mouse_panning_minimum_decay =
166
167# Emulate an analog control stick from keyboard inputs.
168# 0 (default): Disabled, 1: Enabled
169emulate_analog_keyboard =
170
171# Enable mouse inputs to the guest
172# 0 (default): Disabled, 1: Enabled
173mouse_enabled =
174
175# Enable keyboard inputs to the guest
176# 0 (default): Disabled, 1: Enabled
177keyboard_enabled =
178
179)"
180 R"(
181[Core]
182# Whether to use multi-core for CPU emulation
183# 0: Disabled, 1 (default): Enabled
184use_multi_core =
185
186# Enable unsafe extended guest system memory layout (8GB DRAM)
187# 0 (default): Disabled, 1: Enabled
188use_unsafe_extended_memory_layout =
189
190[Cpu]
191# Adjusts various optimizations.
192# Auto-select mode enables choice unsafe optimizations.
193# Accurate enables only safe optimizations.
194# Unsafe allows any unsafe optimizations.
195# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
196cpu_accuracy =
197
198# Allow disabling safe optimizations.
199# 0 (default): Disabled, 1: Enabled
200cpu_debug_mode =
201
202# Enable inline page tables optimization (faster guest memory access)
203# 0: Disabled, 1 (default): Enabled
204cpuopt_page_tables =
205
206# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
207# 0: Disabled, 1 (default): Enabled
208cpuopt_block_linking =
209
210# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
211# 0: Disabled, 1 (default): Enabled
212cpuopt_return_stack_buffer =
213
214# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
215# 0: Disabled, 1 (default): Enabled
216cpuopt_fast_dispatcher =
217
218# Enable context elimination CPU Optimization (reduce host memory use for guest context)
219# 0: Disabled, 1 (default): Enabled
220cpuopt_context_elimination =
221
222# Enable constant propagation CPU optimization (basic IR optimization)
223# 0: Disabled, 1 (default): Enabled
224cpuopt_const_prop =
225
226# Enable miscellaneous CPU optimizations (basic IR optimization)
227# 0: Disabled, 1 (default): Enabled
228cpuopt_misc_ir =
229
230# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
231# 0: Disabled, 1 (default): Enabled
232cpuopt_reduce_misalign_checks =
233
234# Enable Host MMU Emulation (faster guest memory access)
235# 0: Disabled, 1 (default): Enabled
236cpuopt_fastmem =
237
238# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
239# 0: Disabled, 1 (default): Enabled
240cpuopt_fastmem_exclusives =
241
242# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
243# 0: Disabled, 1 (default): Enabled
244cpuopt_recompile_exclusives =
245
246# Enable optimization to ignore invalid memory accesses (faster guest memory access)
247# 0: Disabled, 1 (default): Enabled
248cpuopt_ignore_memory_aborts =
249
250# Enable unfuse FMA (improve performance on CPUs without FMA)
251# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
252# 0: Disabled, 1 (default): Enabled
253cpuopt_unsafe_unfuse_fma =
254
255# Enable faster FRSQRTE and FRECPE
256# Only enabled if cpu_accuracy is set to Unsafe.
257# 0: Disabled, 1 (default): Enabled
258cpuopt_unsafe_reduce_fp_error =
259
260# Enable faster ASIMD instructions (32 bits only)
261# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
262# 0: Disabled, 1 (default): Enabled
263cpuopt_unsafe_ignore_standard_fpcr =
264
265# Enable inaccurate NaN handling
266# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
267# 0: Disabled, 1 (default): Enabled
268cpuopt_unsafe_inaccurate_nan =
269
270# Disable address space checks (64 bits only)
271# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
272# 0: Disabled, 1 (default): Enabled
273cpuopt_unsafe_fastmem_check =
274
275# Enable faster exclusive instructions
276# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
277# 0: Disabled, 1 (default): Enabled
278cpuopt_unsafe_ignore_global_monitor =
279
280)"
281 R"(
282[Renderer]
283# Which backend API to use.
284# 0: OpenGL, 1 (default): Vulkan
285backend =
286
287# Whether to enable asynchronous presentation (Vulkan only)
288# 0 (default): Off, 1: On
289async_presentation =
290
291# Enable graphics API debugging mode.
292# 0 (default): Disabled, 1: Enabled
293debug =
294
295# Enable shader feedback.
296# 0 (default): Disabled, 1: Enabled
297renderer_shader_feedback =
298
299# Enable Nsight Aftermath crash dumps
300# 0 (default): Disabled, 1: Enabled
301nsight_aftermath =
302
303# Disable shader loop safety checks, executing the shader without loop logic changes
304# 0 (default): Disabled, 1: Enabled
305disable_shader_loop_safety_checks =
306
307# Which Vulkan physical device to use (defaults to 0)
308vulkan_device =
309
310# 0: 0.5x (360p/540p) [EXPERIMENTAL]
311# 1: 0.75x (540p/810p) [EXPERIMENTAL]
312# 2 (default): 1x (720p/1080p)
313# 3: 1.5x (1080p/1620p) [EXPERIMENTAL]
314# 4: 2x (1440p/2160p)
315# 5: 3x (2160p/3240p)
316# 6: 4x (2880p/4320p)
317# 7: 5x (3600p/5400p)
318# 8: 6x (4320p/6480p)
319# 9: 7x (5040p/7560p)
320# 10: 8x (5760/8640p)
321resolution_setup =
322
323# Pixel filter to use when up- or down-sampling rendered frames.
324# 0: Nearest Neighbor
325# 1 (default): Bilinear
326# 2: Bicubic
327# 3: Gaussian
328# 4: ScaleForce
329# 5: AMD FidelityFX™️ Super Resolution
330scaling_filter =
331
332# Anti-Aliasing (AA)
333# 0 (default): None, 1: FXAA, 2: SMAA
334anti_aliasing =
335
336# Whether to use fullscreen or borderless window mode
337# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
338fullscreen_mode =
339
340# Aspect ratio
341# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
342aspect_ratio =
343
344# Anisotropic filtering
345# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
346max_anisotropy =
347
348# Whether to enable VSync or not.
349# OpenGL: Values other than 0 enable VSync
350# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
351# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
352# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
353# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
354# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
355# 0: Immediate (Off), 1: Mailbox, 2 (Default): FIFO (On), 3: FIFO Relaxed
356use_vsync =
357
358# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
359# not available and GLASM is selected, GLSL will be used.
360# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
361shader_backend =
362
363# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
364# 0: Off, 1 (default): On
365use_reactive_flushing =
366
367# Whether to allow asynchronous shader building.
368# 0 (default): Off, 1: On
369use_asynchronous_shaders =
370
371# NVDEC emulation.
372# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
373nvdec_emulation =
374
375# Accelerate ASTC texture decoding.
376# 0: Off, 1 (default): On
377accelerate_astc =
378
379# Decode ASTC textures asynchronously.
380# 0 (default): Off, 1: On
381async_astc =
382
383# Recompress ASTC textures to a different format.
384# 0 (default): Uncompressed, 1: BC1 (Low quality), 2: BC3: (Medium quality)
385async_astc =
386
387# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
388# 0: Off, 1: On (default)
389use_speed_limit =
390
391# Limits the speed of the game to run no faster than this value as a percentage of target speed
392# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
393speed_limit =
394
395# Whether to use disk based shader cache
396# 0: Off, 1 (default): On
397use_disk_shader_cache =
398
399# Which gpu accuracy level to use
400# 0: Normal, 1 (default): High, 2: Extreme (Very slow)
401gpu_accuracy =
402
403# Whether to use asynchronous GPU emulation
404# 0 : Off (slow), 1 (default): On (fast)
405use_asynchronous_gpu_emulation =
406
407# Inform the guest that GPU operations completed more quickly than they did.
408# 0: Off, 1 (default): On
409use_fast_gpu_time =
410
411# Whether to use garbage collection or not for GPU caches.
412# 0 (default): Off, 1: On
413use_caches_gc =
414
415# The clear color for the renderer. What shows up on the sides of the bottom screen.
416# Must be in range of 0-255. Defaults to 0 for all.
417bg_red =
418bg_blue =
419bg_green =
420
421)"
422 R"(
423[Audio]
424# Which audio output engine to use.
425# auto (default): Auto-select
426# cubeb: Cubeb audio engine (if available)
427# sdl2: SDL2 audio engine (if available)
428# null: No audio output
429output_engine =
430
431# Which audio device to use.
432# auto (default): Auto-select
433output_device =
434
435# Output volume.
436# 100 (default): 100%, 0; mute
437volume =
438
439[Data Storage]
440# Whether to create a virtual SD card.
441# 1 (default): Yes, 0: No
442use_virtual_sd =
443
444# Whether or not to enable gamecard emulation
445# 1: Yes, 0 (default): No
446gamecard_inserted =
447
448# Whether or not the gamecard should be emulated as the current game
449# If 'gamecard_inserted' is 0 this setting is irrelevant
450# 1: Yes, 0 (default): No
451gamecard_current_game =
452
453# Path to an XCI file to use as the gamecard
454# If 'gamecard_inserted' is 0 this setting is irrelevant
455# If 'gamecard_current_game' is 1 this setting is irrelevant
456gamecard_path =
457
458[System]
459# Whether the system is docked
460# 1 (default): Yes, 0: No
461use_docked_mode =
462
463# Sets the seed for the RNG generator built into the switch
464# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
465rng_seed_enabled =
466rng_seed =
467
468# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
469# This will auto-increment, with the time set being the time the game is started
470# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
471custom_rtc_enabled =
472custom_rtc =
473
474# Sets the systems language index
475# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
476# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
477# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
478language_index =
479
480# The system region that yuzu will use during emulation
481# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
482region_index =
483
484# The system time zone that yuzu will use during emulation
485# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
486time_zone_index =
487
488# Sets the sound output mode.
489# 0: Mono, 1 (default): Stereo, 2: Surround
490sound_index =
491
492[Miscellaneous]
493# A filter which removes logs below a certain logging level.
494# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
495log_filter = *:Trace
496
497# Use developer keys
498# 0 (default): Disabled, 1: Enabled
499use_dev_keys =
500
501[Debugging]
502# Record frame time data, can be found in the log directory. Boolean value
503record_frame_times =
504# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
505dump_exefs=false
506# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
507dump_nso=false
508# Determines whether or not yuzu will save the filesystem access log.
509enable_fs_access_log=false
510# Enables verbose reporting services
511reporting_services =
512# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
513# false: Retail/Normal Mode (default), true: Kiosk Mode
514quest_flag =
515# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
516# false: Disabled (default), true: Enabled
517use_debug_asserts =
518# Determines whether unimplemented HLE service calls should be automatically stubbed.
519# false: Disabled (default), true: Enabled
520use_auto_stub =
521# Enables/Disables the macro JIT compiler
522disable_macro_jit=false
523# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
524# false: Disabled (default), true: Enabled
525use_gdbstub=false
526# The port to use for the GDB server, if it is enabled.
527gdbstub_port=6543
528
529[WebService]
530# Whether or not to enable telemetry
531# 0: No, 1 (default): Yes
532enable_telemetry =
533# URL for Web API
534web_api_url = https://api.yuzu-emu.org
535# Username and token for yuzu Web Service
536# See https://profile.yuzu-emu.org/ for more info
537yuzu_username =
538yuzu_token =
539
540[Network]
541# Name of the network interface device to use with yuzu LAN play.
542# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
543# e.g. On Windows: 'Ethernet', 'Wi-Fi'
544network_interface =
545
546[AddOns]
547# Used to disable add-ons
548# List of title IDs of games that will have add-ons disabled (separated by '|'):
549title_ids =
550# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
551# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
552)";
553} // namespace DefaultINI
diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp
new file mode 100644
index 000000000..39fd8050c
--- /dev/null
+++ b/src/yuzu_cmd/sdl_config.cpp
@@ -0,0 +1,257 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// SDL will break our main function in yuzu-cmd if we don't define this before adding SDL.h
5#define SDL_MAIN_HANDLED
6#include <SDL.h>
7
8#include "input_common/main.h"
9#include "sdl_config.h"
10
11const std::array<int, Settings::NativeButton::NumButtons> SdlConfig::default_buttons = {
12 SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
13 SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
14 SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
15};
16
17const std::array<int, Settings::NativeMotion::NumMotions> SdlConfig::default_motions = {
18 SDL_SCANCODE_7,
19 SDL_SCANCODE_8,
20};
21
22const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> SdlConfig::default_analogs{
23 {
24 {
25 SDL_SCANCODE_UP,
26 SDL_SCANCODE_DOWN,
27 SDL_SCANCODE_LEFT,
28 SDL_SCANCODE_RIGHT,
29 },
30 {
31 SDL_SCANCODE_I,
32 SDL_SCANCODE_K,
33 SDL_SCANCODE_J,
34 SDL_SCANCODE_L,
35 },
36 }};
37
38const std::array<int, 2> SdlConfig::default_stick_mod = {
39 SDL_SCANCODE_D,
40 0,
41};
42
43const std::array<int, 2> SdlConfig::default_ringcon_analogs{{
44 0,
45 0,
46}};
47
48SdlConfig::SdlConfig(const std::optional<std::string> config_path) {
49 Initialize(config_path);
50 ReadSdlValues();
51 SaveSdlValues();
52}
53
54SdlConfig::~SdlConfig() {
55 if (global) {
56 SdlConfig::SaveAllValues();
57 }
58}
59
60void SdlConfig::ReloadAllValues() {
61 Reload();
62 ReadSdlValues();
63 SaveSdlValues();
64}
65
66void SdlConfig::SaveAllValues() {
67 Save();
68 SaveSdlValues();
69}
70
71void SdlConfig::ReadSdlValues() {
72 ReadSdlControlValues();
73}
74
75void SdlConfig::ReadSdlControlValues() {
76 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
77
78 Settings::values.players.SetGlobal(!IsCustomConfig());
79 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
80 ReadSdlPlayerValues(p);
81 }
82 if (IsCustomConfig()) {
83 EndGroup();
84 return;
85 }
86 ReadDebugControlValues();
87 ReadHidbusValues();
88
89 EndGroup();
90}
91
92void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) {
93 std::string player_prefix;
94 if (type != ConfigType::InputProfile) {
95 player_prefix.append("player_").append(ToString(player_index)).append("_");
96 }
97
98 auto& player = Settings::values.players.GetValue()[player_index];
99 if (IsCustomConfig()) {
100 const auto profile_name =
101 ReadStringSetting(std::string(player_prefix).append("profile_name"));
102 if (profile_name.empty()) {
103 // Use the global input config
104 player = Settings::values.players.GetValue(true)[player_index];
105 return;
106 }
107 }
108
109 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
110 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
111 auto& player_buttons = player.buttons[i];
112
113 player_buttons = ReadStringSetting(
114 std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
115 if (player_buttons.empty()) {
116 player_buttons = default_param;
117 }
118 }
119
120 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
121 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
122 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
123 default_analogs[i][3], default_stick_mod[i], 0.5f);
124 auto& player_analogs = player.analogs[i];
125
126 player_analogs = ReadStringSetting(
127 std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
128 if (player_analogs.empty()) {
129 player_analogs = default_param;
130 }
131 }
132
133 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
134 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
135 auto& player_motions = player.motions[i];
136
137 player_motions = ReadStringSetting(
138 std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
139 if (player_motions.empty()) {
140 player_motions = default_param;
141 }
142 }
143}
144
145void SdlConfig::ReadDebugControlValues() {
146 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
147 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
148 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
149 debug_pad_buttons = ReadStringSetting(
150 std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param);
151 if (debug_pad_buttons.empty()) {
152 debug_pad_buttons = default_param;
153 }
154 }
155 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
156 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
157 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
158 default_analogs[i][3], default_stick_mod[i], 0.5f);
159 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
160 debug_pad_analogs = ReadStringSetting(
161 std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param);
162 if (debug_pad_analogs.empty()) {
163 debug_pad_analogs = default_param;
164 }
165 }
166}
167
168void SdlConfig::ReadHidbusValues() {
169 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
170 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
171 auto& ringcon_analogs = Settings::values.ringcon_analogs;
172
173 ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param);
174 if (ringcon_analogs.empty()) {
175 ringcon_analogs = default_param;
176 }
177}
178
179void SdlConfig::SaveSdlValues() {
180 SaveSdlControlValues();
181
182 WriteToIni();
183}
184
185void SdlConfig::SaveSdlControlValues() {
186 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
187
188 Settings::values.players.SetGlobal(!IsCustomConfig());
189 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
190 SaveSdlPlayerValues(p);
191 }
192 if (IsCustomConfig()) {
193 EndGroup();
194 return;
195 }
196 SaveDebugControlValues();
197 SaveHidbusValues();
198
199 EndGroup();
200}
201
202void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) {
203 std::string player_prefix;
204 if (type != ConfigType::InputProfile) {
205 player_prefix = std::string("player_").append(ToString(player_index)).append("_");
206 }
207
208 const auto& player = Settings::values.players.GetValue()[player_index];
209 if (IsCustomConfig() && player.profile_name.empty()) {
210 // No custom profile selected
211 return;
212 }
213
214 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
215 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
216 WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
217 player.buttons[i], std::make_optional(default_param));
218 }
219 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
220 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
221 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
222 default_analogs[i][3], default_stick_mod[i], 0.5f);
223 WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
224 player.analogs[i], std::make_optional(default_param));
225 }
226 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
227 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
228 WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
229 player.motions[i], std::make_optional(default_param));
230 }
231}
232
233void SdlConfig::SaveDebugControlValues() {
234 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
235 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
236 WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
237 Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
238 }
239 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
240 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
241 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
242 default_analogs[i][3], default_stick_mod[i], 0.5f);
243 WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
244 Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
245 }
246}
247
248void SdlConfig::SaveHidbusValues() {
249 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
250 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
251 WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
252 std::make_optional(default_param));
253}
254
255std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) {
256 return Settings::values.linkage.by_category[category];
257}
diff --git a/src/yuzu_cmd/sdl_config.h b/src/yuzu_cmd/sdl_config.h
new file mode 100644
index 000000000..1fd1c692d
--- /dev/null
+++ b/src/yuzu_cmd/sdl_config.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "frontend_common/config.h"
7
8class SdlConfig final : public Config {
9public:
10 explicit SdlConfig(std::optional<std::string> config_path);
11 ~SdlConfig() override;
12
13 void ReloadAllValues() override;
14 void SaveAllValues() override;
15
16protected:
17 void ReadSdlValues();
18 void ReadSdlPlayerValues(std::size_t player_index);
19 void ReadSdlControlValues();
20 void ReadHidbusValues() override;
21 void ReadDebugControlValues() override;
22 void ReadPathValues() override {}
23 void ReadShortcutValues() override {}
24 void ReadUIValues() override {}
25 void ReadUIGamelistValues() override {}
26 void ReadUILayoutValues() override {}
27 void ReadMultiplayerValues() override {}
28
29 void SaveSdlValues();
30 void SaveSdlPlayerValues(std::size_t player_index);
31 void SaveSdlControlValues();
32 void SaveHidbusValues() override;
33 void SaveDebugControlValues() override;
34 void SavePathValues() override {}
35 void SaveShortcutValues() override {}
36 void SaveUIValues() override {}
37 void SaveUIGamelistValues() override {}
38 void SaveUILayoutValues() override {}
39 void SaveMultiplayerValues() override {}
40
41 std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
42
43public:
44 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
45 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
46 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
47 static const std::array<int, 2> default_stick_mod;
48 static const std::array<int, 2> default_ringcon_analogs;
49};
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 087cfaa26..a81635fa4 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -29,10 +29,11 @@
29#include "core/hle/service/filesystem/filesystem.h" 29#include "core/hle/service/filesystem/filesystem.h"
30#include "core/loader/loader.h" 30#include "core/loader/loader.h"
31#include "core/telemetry_session.h" 31#include "core/telemetry_session.h"
32#include "frontend_common/config.h"
32#include "input_common/main.h" 33#include "input_common/main.h"
33#include "network/network.h" 34#include "network/network.h"
35#include "sdl_config.h"
34#include "video_core/renderer_base.h" 36#include "video_core/renderer_base.h"
35#include "yuzu_cmd/config.h"
36#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 37#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
37#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" 38#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
38#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" 39#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
@@ -62,6 +63,10 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
62} 63}
63#endif 64#endif
64 65
66#ifdef __unix__
67#include "common/linux/gamemode.h"
68#endif
69
65static void PrintHelp(const char* argv0) { 70static void PrintHelp(const char* argv0) {
66 std::cout << "Usage: " << argv0 71 std::cout << "Usage: " << argv0
67 << " [options] <filename>\n" 72 << " [options] <filename>\n"
@@ -300,7 +305,7 @@ int main(int argc, char** argv) {
300 } 305 }
301 } 306 }
302 307
303 Config config{config_path}; 308 SdlConfig config{config_path};
304 309
305 // apply the log_filter setting 310 // apply the log_filter setting
306 // the logger was initialized before and doesn't pick up the filter on its own 311 // the logger was initialized before and doesn't pick up the filter on its own
@@ -424,6 +429,10 @@ int main(int argc, char** argv) {
424 exit(0); 429 exit(0);
425 }); 430 });
426 431
432#ifdef __unix__
433 Common::Linux::StartGamemode();
434#endif
435
427 void(system.Run()); 436 void(system.Run());
428 if (system.DebuggerEnabled()) { 437 if (system.DebuggerEnabled()) {
429 system.InitializeDebugger(); 438 system.InitializeDebugger();
@@ -435,6 +444,10 @@ int main(int argc, char** argv) {
435 void(system.Pause()); 444 void(system.Pause());
436 system.ShutdownMainProcess(); 445 system.ShutdownMainProcess();
437 446
447#ifdef __unix__
448 Common::Linux::StopGamemode();
449#endif
450
438 detached_tasks.WaitForAllTasks(); 451 detached_tasks.WaitForAllTasks();
439 return 0; 452 return 0;
440} 453}